diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index cdefd1d7f1..4a8f479df1 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -150,8 +150,10 @@ jobs:
- name: Install Composer Packages
run: composer install --prefer-dist --no-interaction --no-progress
- - name: Copy database.php
- run: cp ./tests/config/database.php ./vendor/pieceofcake2/app/config/
+ - name: Copy config
+ run: |
+ cp ./tests/config/database.php ./vendor/pieceofcake2/app/config/
+ cp ./tests/config/Schema/i18n.php ./vendor/pieceofcake2/app/config/Schema/
- name: Make temporary directories writable
run: |
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4329146f5c..04a857a3a8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,71 @@
## Unreleased
+### PHPStan Level 5 Compliance & Comprehensive Type Declarations ([PR #32](https://github.com/pieceofcake2/cakephp/pull/32))
+
+Achieved 100% PHPStan Level 5 compliance with comprehensive type declarations across the entire codebase. This represents a major milestone in type safety and static analysis coverage.
+
+#### PHPStan Level 5 Achievement
+- **Static Analysis**: PHPStan Level 5 now passes with zero errors (599 → 0)
+ - Added type declarations to 200+ methods across all core classes
+ - Fixed type coercion issues in transaction handling and return values
+ - Resolved contravariance issues in method signatures
+ - Fixed undefined variable and property errors throughout codebase
+
+#### Comprehensive Type Declarations
+
+**Model Layer:**
+- Model, I18nModel: Complete method signatures with return types
+- Behaviors: TreeBehavior (265 errors), TranslateBehavior, ContainableBehavior, AclBehavior
+- BehaviorCollection, ModelBehavior: Full type coverage
+- AclNode, Permission: Type declarations added
+
+**DataSource Layer:**
+- DboSource: Comprehensive type declarations for all methods
+- Database Drivers: Mysql, Postgres, Sqlite, Sqlserver with full type coverage
+- DataSource, CakeSession, ConnectionManager: Complete type declarations
+- Removed `extract()` usage in `buildColumn()` for better type safety
+
+**Controller Layer:**
+- Controller, Component: Base class type declarations
+- All Auth classes: Authenticators and Authorizers with full type coverage
+- Components: Acl (split PhpAcl into PhpAco/PhpAro classes), Security, RequestHandler, Flash
+- ComponentCollection, Scaffold: Type declarations added
+
+**Console Layer:**
+- Shell, ShellDispatcher: Complete method signatures
+- All Commands: AclShell, ApiShell, SchemaShell, I18nShell, TestShell, ConsoleShell
+- Tasks: ExtractTask with comprehensive type coverage
+- ConsoleOutput, ConsoleInput: Full type declarations
+
+**Core & Utilities:**
+- Configure: Type declarations and bug fixes
+- App, CakeObject: Type declarations
+- CakeEventManager, CakeEvent: Full type coverage
+- L10n, I18n, Multibyte: Complete type declarations
+- CakeText, Folder: Type declarations added
+- LegacyClassLoader: Changed `autoload()` return type to `void` (spl_autoload_register requirement)
+
+**Test Infrastructure:**
+- CakeTestFixture: Updated to match new type signatures
+- Replaced `SqlserverTestResultIterator` with proper PDOStatement mocks
+- Updated all test mocks to return correct types
+
+#### Code Quality Improvements
+- **Multibyte Optimization**: Simplified ASCII character conversion
+ - Removed unnecessary loops in `strtolower()` and `strtoupper()`
+ - Changed from 6-line loop to simple `ord(strtolower(chr($char)))`
+- **EmailConfig Interface**: Added type-safe email configuration
+ - New `EmailConfigInterface` for consistent email configuration
+ - Updated UPGRADE.md with migration guide
+- **Test Mock Improvements**: Enhanced type safety in test mocks
+ - All database transaction mocks now return proper boolean values
+ - Prevents null→false coercion issues with typed return values
+
+#### Breaking Changes
+- `EmailConfig` classes must now implement `EmailConfigInterface`
+- See UPGRADE.md for detailed migration instructions
+
### PHPStan Static Analysis & Comprehensive Type Declarations ([PR #31](https://github.com/pieceofcake2/cakephp/pull/31))
Achieved 100% PHPStan level 0 compliance and added comprehensive property type declarations to all CakePHP 2.x core classes using Rector.
diff --git a/UPGRADE.md b/UPGRADE.md
index 408f9c33f5..76753f4772 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -305,6 +305,51 @@ class User extends AppModel
**Your existing non-namespaced application will continue to work without any changes** thanks to the LegacyClassLoader. This migration is completely optional and can be done at your own pace.
+### EmailConfig Must Implement EmailConfigInterface ([PR #32](https://github.com/pieceofcake2/cakephp/pull/32))
+
+All email configuration classes must now implement `EmailConfigInterface`. This change improves type safety and ensures consistent email configuration across applications.
+
+#### What Changed
+
+- `EmailConfig` class must implement `Cake\Network\Email\EmailConfigInterface`
+- The interface requires all email configuration classes to define email settings as public properties
+
+#### Impact on Your Application
+
+If you have a custom `EmailConfig` class in `app/Config/email.php` or `config/email.php`, you need to update it to implement the interface:
+
+**Before:**
+```php
+ 'Mail',
+ 'from' => 'you@localhost',
+ ];
+}
+```
+
+**After:**
+```php
+ 'Mail',
+ 'from' => 'you@localhost',
+ ];
+}
+```
+
+#### Migration Steps
+
+1. Add the `use` statement for `EmailConfigInterface` at the top of your `EmailConfig` class file
+2. Add `implements EmailConfigInterface` to your `EmailConfig` class declaration
+3. Ensure all email configurations are defined as public properties (arrays)
+
### Directory Structure Modernization ([PR #21](https://github.com/pieceofcake2/cakephp/pull/21))
- **Directory layout has been restructured to modern standards**
diff --git a/phpcs.xml b/phpcs.xml
index 822269ce69..21791445c5 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -90,7 +90,7 @@
src/Controller/Component/Acl/PhpAcl.phpsrc/Error/exceptions.php
- src/TestSuite/ControllerTestCase.ph
+ src/TestSuite/ControllerTestCase.phptests/TestCase/*
@@ -113,23 +113,6 @@
tests/test_app/Plugin/*/Console/Command/*
-
-
- src/basics.php
- src/functions.php
- src/Cache/Engine/FileEngine.php
- src/Console/Shell.php
- src/Network/CakeResponse.php
- src/Utility/Folder.php
- src/View/Helper.php
- src/View/View.php
- tests/TestCase/Cache/CacheTest.php
- tests/TestCase/Cache/Engine/MemcacheEngineTest.php
- tests/TestCase/Cache/Engine/MemcachedEngineTest.php
- tests/TestCase/Cache/Engine/RedisEngineTest.php
- tests/TestCase/Error/ErrorHandlerTest.php
-
-
*/database.php
diff --git a/phpstan.neon b/phpstan.neon
index 9abe7df740..fd34b37162 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -2,12 +2,13 @@ includes:
- vendor/pieceofcake2/phpstan-cakephp2/extension.neon
parameters:
- level: 0
+ level: 5
phpVersion: 80000
paths:
- src
excludePaths:
- src/Console/Templates/*
+ - src/TestSuite/templates/*
- src/View/Scaffolds/*
bootstrapFiles:
- tests/bootstrap.php
@@ -15,10 +16,13 @@ parameters:
ignoreErrors:
# Ignore deprecated functions that are part of CakePHP 2.x
- '#Function strftime\(\) is deprecated#'
+ # Ignore mcrypt constants (extension removed in PHP 7.2+)
- '#Function mcrypt_.*\(\) is deprecated#'
- '#Function mcrypt_.* not found#'
+ - '#Constant MCRYPT_.* not found#'
# Config classes (generated at runtime)
- '#Class EmailConfig not found#'
+ - '# has unknown class DATABASE_CONFIG as its type.#'
parallel:
processTimeout: 300.0
maximumNumberOfProcesses: 4
diff --git a/src/Cache/Cache.php b/src/Cache/Cache.php
index 7ec7b956fd..aae6b24297 100644
--- a/src/Cache/Cache.php
+++ b/src/Cache/Cache.php
@@ -52,28 +52,28 @@ class Cache
*
* @var array
*/
- protected static $_config = [];
+ protected static array $_config = [];
/**
* Group to Config mapping
*
* @var array
*/
- protected static $_groups = [];
+ protected static array $_groups = [];
/**
* Whether to reset the settings with the next call to Cache::set();
*
- * @var array
+ * @var bool
*/
- protected static $_reset = false;
+ protected static bool $_reset = false;
/**
* Engine instances keyed by configuration name.
*
* @var array
*/
- protected static $_engines = [];
+ protected static array $_engines = [];
/**
* Set the cache configuration to use. config() can
@@ -116,13 +116,13 @@ class Cache
* - `user` Used by Xcache. Username for XCache
* - `password` Used by Xcache/Redis. Password for XCache/Redis
*
- * @param string $name Name of the configuration
+ * @param array|string|null $name Name of the configuration
* @param array $settings Optional associative array of settings passed to the engine
- * @return array|false array(engine, settings) on success, false on failure
+ * @return array{engine: mixed, settings: mixed}|false array(engine, settings) on success, false on failure
* @throws CacheException
* @see app/Config/core.php for configuration settings
*/
- public static function config($name = null, $settings = [])
+ public static function config(array|string|null $name = null, array $settings = []): array|false
{
if (is_array($name)) {
$settings = $name;
@@ -168,7 +168,7 @@ public static function config($name = null, $settings = [])
* @return bool
* @throws CacheException
*/
- protected static function _buildEngine($name)
+ protected static function _buildEngine(string $name): bool
{
$config = static::$_config[$name];
@@ -202,7 +202,7 @@ protected static function _buildEngine($name)
*
* @return array Array of configured Cache config names.
*/
- public static function configured()
+ public static function configured(): array
{
return array_keys(static::$_config);
}
@@ -215,7 +215,7 @@ public static function configured()
* @param string $name A currently configured cache config you wish to remove.
* @return bool success of the removal, returns false when the config does not exist.
*/
- public static function drop($name)
+ public static function drop(string $name): bool
{
if (!isset(static::$_config[$name])) {
return false;
@@ -243,12 +243,12 @@ public static function drop($name)
*
* `Cache::set(null, 'my_config');`
*
- * @param array|string $settings Optional string for simple name-value pair or array
- * @param string $value Optional for a simple name-value pair
+ * @param array|string|null $settings Optional string for simple name-value pair or array
+ * @param string|null $value Optional for a simple name-value pair
* @param string $config The configuration name you are changing. Defaults to 'default'
* @return array|false Array of settings.
*/
- public static function set($settings = [], $value = null, $config = 'default')
+ public static function set(array|string|null $settings = [], ?string $value = null, string $config = 'default'): array|false
{
if (is_array($settings) && $value !== null) {
$config = $value;
@@ -285,10 +285,10 @@ public static function set($settings = [], $value = null, $config = 'default')
* Permanently remove all expired and deleted data
*
* @param string $config [optional] The config name you wish to have garbage collected. Defaults to 'default'
- * @param int $expires [optional] An expires timestamp. Defaults to NULL
+ * @param int|null $expires [optional] An expires timestamp. Defaults to NULL
* @return bool
*/
- public static function gc($config = 'default', $expires = null)
+ public static function gc(string $config = 'default', ?int $expires = null): bool
{
return static::$_engines[$config]->gc($expires);
}
@@ -306,12 +306,12 @@ public static function gc($config = 'default', $expires = null)
*
* `Cache::write('cached_data', $data, 'long_term');`
*
- * @param string $key Identifier for the data
+ * @param string|null $key Identifier for the data
* @param mixed $value Data to be cached - anything except a resource
* @param string $config Optional string configuration name to write to. Defaults to 'default'
* @return bool True if the data was successfully cached, false on failure
*/
- public static function write($key, $value, $config = 'default')
+ public static function write(?string $key, mixed $value, string $config = 'default'): bool
{
$settings = static::settings($config);
@@ -362,7 +362,7 @@ public static function write($key, $value, $config = 'default')
* @param string $config optional name of the configuration to use. Defaults to 'default'
* @return mixed The cached data, or false if the data doesn't exist, has expired, or if there was an error fetching it
*/
- public static function read($key, $config = 'default')
+ public static function read(string $key, string $config = 'default'): mixed
{
$settings = static::settings($config);
@@ -386,10 +386,10 @@ public static function read($key, $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 bool 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')
+ public static function increment(string $key, int $offset = 1, string $config = 'default'): bool
{
$settings = static::settings($config);
@@ -401,7 +401,7 @@ public static function increment($key, $offset = 1, $config = 'default')
}
$key = static::$_engines[$config]->key($key);
- if (!$key || !is_int($offset) || $offset < 0) {
+ if (!$key || $offset < 0) {
return false;
}
$success = static::$_engines[$config]->increment($settings['prefix'] . $key, $offset);
@@ -416,10 +416,10 @@ 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 bool 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')
+ public static function decrement(string $key, int $offset = 1, string $config = 'default'): bool
{
$settings = static::settings($config);
@@ -431,7 +431,7 @@ public static function decrement($key, $offset = 1, $config = 'default')
}
$key = static::$_engines[$config]->key($key);
- if (!$key || !is_int($offset) || $offset < 0) {
+ if (!$key || $offset < 0) {
return false;
}
$success = static::$_engines[$config]->decrement($settings['prefix'] . $key, $offset);
@@ -457,7 +457,7 @@ public static function decrement($key, $offset = 1, $config = 'default')
* @param string $config name of the configuration to use. Defaults to 'default'
* @return bool True if the value was successfully deleted, false if it didn't exist or couldn't be removed
*/
- public static function delete($key, $config = 'default')
+ public static function delete(string $key, string $config = 'default'): bool
{
$settings = static::settings($config);
@@ -485,7 +485,7 @@ public static function delete($key, $config = 'default')
* @param string $config name of the configuration to use. Defaults to 'default'
* @return bool True if the cache was successfully cleared, false otherwise
*/
- public static function clear($check = false, $config = 'default')
+ public static function clear(bool $check = false, string $config = 'default'): bool
{
if (!static::isInitialized($config)) {
return false;
@@ -503,7 +503,7 @@ public static function clear($check = false, $config = 'default')
* @param string $config name of the configuration to use. Defaults to 'default'
* @return bool True if the cache group was successfully cleared, false otherwise
*/
- public static function clearGroup($group, $config = 'default')
+ public static function clearGroup(string $group, string $config = 'default'): bool
{
if (!static::isInitialized($config)) {
return false;
@@ -520,7 +520,7 @@ public static function clearGroup($group, $config = 'default')
* @param string $config name of the configuration to use. Defaults to 'default'
* @return bool Whether or not the config name has been initialized.
*/
- public static function isInitialized($config = 'default')
+ public static function isInitialized(string $config = 'default'): bool
{
if (Configure::read('Cache.disable')) {
return false;
@@ -536,7 +536,7 @@ public static function isInitialized($config = 'default')
* @return array list of settings for this engine
* @see Cache::config()
*/
- public static function settings($name = 'default')
+ public static function settings(string $name = 'default'): array
{
if (!empty(static::$_engines[$name])) {
return static::$_engines[$name]->settings();
@@ -560,11 +560,11 @@ public static function settings($name = 'default')
*
* $config will equal to `array('posts' => array('daily', 'weekly'))`
*
- * @param string $group group name or null to retrieve all group mappings
+ * @param string|null $group group name or null to retrieve all group mappings
* @return array map of group and all configuration that has the same group
* @throws CacheException
*/
- public static function groupConfigs($group = null)
+ public static function groupConfigs(?string $group = null): array
{
if ($group === null) {
return static::$_groups;
@@ -600,12 +600,13 @@ public static function groupConfigs($group = null)
* Defaults to default.
* @return mixed The results of the callable or unserialized results.
*/
- public static function remember($key, $callable, $config = 'default')
+ public static function remember(string $key, callable $callable, string $config = 'default'): mixed
{
$existing = static::read($key, $config);
- if ($existing !== false) {
- return $existing;
+ if ($existing) {
+ return true;
}
+
$results = call_user_func($callable);
static::write($key, $results, $config);
@@ -631,7 +632,7 @@ public static function remember($key, $callable, $config = 'default')
* @return bool True if the data was successfully cached, false on failure.
* Or if the key existed already.
*/
- public static function add($key, $value, $config = 'default')
+ public static function add(string $key, mixed $value, string $config = 'default'): bool
{
$settings = self::settings($config);
@@ -659,7 +660,7 @@ public static function add($key, $value, $config = 'default')
* @param string $config Optional string configuration name to get an engine for. Defaults to 'default'.
* @return CacheEngine|null Null if the engine has not been initialized or the engine.
*/
- public static function engine($config = 'default')
+ public static function engine(string $config = 'default'): ?CacheEngine
{
if (self::isInitialized($config)) {
return self::$_engines[$config];
diff --git a/src/Cache/CacheEngine.php b/src/Cache/CacheEngine.php
index 8e015ae108..6c1ed92e5b 100644
--- a/src/Cache/CacheEngine.php
+++ b/src/Cache/CacheEngine.php
@@ -46,7 +46,7 @@ abstract class CacheEngine
* @param array $settings Associative array of parameters for the engine
* @return bool True if the engine has been successfully initialized, false if not
*/
- public function init($settings = [])
+ public function init(array $settings = []): bool
{
$settings += $this->settings + [
'prefix' => 'cake_',
@@ -71,11 +71,12 @@ public function init($settings = [])
*
* Permanently remove all expired and deleted data
*
- * @param int $expires [optional] An expires timestamp, invalidating all data before.
- * @return void
+ * @param int|null $expires [optional] An expires timestamp, invalidating all data before.
+ * @return bool
*/
- public function gc($expires = null)
+ public function gc(?int $expires = null): bool
{
+ return true;
}
/**
@@ -86,7 +87,7 @@ public function gc($expires = null)
* @param int $duration How long to cache for.
* @return bool True if the data was successfully cached, false on failure
*/
- abstract public function write($key, $value, $duration);
+ abstract public function write(string $key, mixed $value, int $duration): bool;
/**
* Write value for a key into cache if it doesn't already exist
@@ -96,7 +97,7 @@ abstract public function write($key, $value, $duration);
* @param int $duration How long to cache for.
* @return bool True if the data was successfully cached, false on failure
*/
- abstract public function add($key, $value, $duration);
+ abstract public function add(string $key, mixed $value, int $duration): bool;
/**
* Read a key from the cache
@@ -104,7 +105,7 @@ abstract public function add($key, $value, $duration);
* @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
*/
- abstract public function read($key);
+ abstract public function read(string $key): mixed;
/**
* Increment a number under the key and return incremented value
@@ -113,7 +114,7 @@ abstract public function read($key);
* @param int $offset How much to add
* @return int|false New incremented value, false otherwise
*/
- abstract public function increment(string $key, int $offset = 1);
+ abstract public function increment(string $key, int $offset = 1): int|false;
/**
* Decrement a number under the key and return decremented value
@@ -122,7 +123,7 @@ abstract public function increment(string $key, int $offset = 1);
* @param int $offset How much to subtract
* @return int|false New incremented value, false otherwise
*/
- abstract public function decrement(string $key, int $offset = 1);
+ abstract public function decrement(string $key, int $offset = 1): int|false;
/**
* Delete a key from the cache
@@ -130,7 +131,7 @@ abstract public function decrement(string $key, int $offset = 1);
* @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
*/
- abstract public function delete(string $key);
+ abstract public function delete(string $key): bool;
/**
* Delete all keys from the cache
@@ -138,7 +139,7 @@ abstract public function delete(string $key);
* @param bool $check if true will check expiration, otherwise delete all
* @return bool True if the cache was successfully cleared, false otherwise
*/
- abstract public function clear(bool $check);
+ abstract public function clear(bool $check): bool;
/**
* Clears all values belonging to a group. Is up to the implementing engine
@@ -157,7 +158,7 @@ abstract public function clearGroup(string $group): bool;
*
* @return array
*/
- public function groups()
+ public function groups(): array
{
return $this->settings['groups'];
}
@@ -167,7 +168,7 @@ public function groups()
*
* @return array settings
*/
- public function settings()
+ public function settings(): array
{
return $this->settings;
}
@@ -175,10 +176,10 @@ public function settings()
/**
* Generates a safe key for use with cache engine storage engines.
*
- * @param string $key the key passed over
- * @return mixed string $key or false
+ * @param string|null $key the key passed over
+ * @return string|false string $key or false
*/
- public function key($key)
+ public function key(?string $key): string|false
{
if (empty($key)) {
return false;
@@ -189,7 +190,7 @@ public function key($key)
$prefix = md5(implode('_', $this->groups()));
}
- $key = preg_replace('/[\s]+/', '_', strtolower(trim(str_replace([DS, '/', '.'], '_', strval($key)))));
+ $key = preg_replace('/\s+/', '_', strtolower(trim(str_replace([DS, '/', '.'], '_', $key))));
return $prefix . $key;
}
diff --git a/src/Cache/Engine/ApcEngine.php b/src/Cache/Engine/ApcEngine.php
index 7b58bffe93..5347d567eb 100644
--- a/src/Cache/Engine/ApcEngine.php
+++ b/src/Cache/Engine/ApcEngine.php
@@ -54,7 +54,7 @@ class ApcEngine extends CacheEngine
* @return bool True if the engine has been successfully initialized, false if not
* @see CacheEngine::__defaults
*/
- public function init($settings = [])
+ public function init(array $settings = []): bool
{
if (!isset($settings['prefix'])) {
$settings['prefix'] = Inflector::slug(APP_DIR) . '_';
@@ -78,7 +78,7 @@ public function init($settings = [])
* @param int $duration How long to cache the data, in seconds
* @return bool True if the data was successfully cached, false on failure
*/
- public function write($key, $value, $duration)
+ public function write(string $key, mixed $value, int $duration): bool
{
$expires = 0;
if ($duration) {
@@ -96,7 +96,7 @@ public function write($key, $value, $duration)
* @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)
+ public function read(string $key): mixed
{
$time = time();
$func = $this->_apcExtension . '_fetch';
@@ -115,7 +115,7 @@ public function read($key)
* @param int $offset How much to increment
* @return int|false New incremented value, false otherwise
*/
- public function increment(string $key, int $offset = 1)
+ public function increment(string $key, int $offset = 1): int|false
{
$func = $this->_apcExtension . '_inc';
@@ -129,7 +129,7 @@ public function increment(string $key, int $offset = 1)
* @param int $offset How much to subtract
* @return int|false New decremented value, false otherwise
*/
- public function decrement(string $key, int $offset = 1)
+ public function decrement(string $key, int $offset = 1): int|false
{
$func = $this->_apcExtension . '_dec';
@@ -142,7 +142,7 @@ public function decrement(string $key, int $offset = 1)
* @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(string $key)
+ public function delete(string $key): bool
{
$func = $this->_apcExtension . '_delete';
@@ -156,11 +156,12 @@ public function delete(string $key)
* from APC as they expired. This flag is really only used by FileEngine.
* @return bool True Returns true.
*/
- public function clear($check)
+ public function clear(bool $check): bool
{
if ($check) {
return true;
}
+
$func = $this->_apcExtension . '_delete';
if (class_exists(APCIterator::class, false)) {
$iterator = new APCIterator(
@@ -189,7 +190,7 @@ public function clear($check)
*
* @return array
*/
- public function groups()
+ public function groups(): array
{
if (empty($this->_compiledGroupNames)) {
foreach ($this->settings['groups'] as $group) {
@@ -245,7 +246,7 @@ public function clearGroup(string $group): bool
* @return bool True if the data was successfully cached, false on failure.
* @link http://php.net/manual/en/function.apc-add.php
*/
- public function add($key, $value, $duration)
+ public function add(string $key, mixed $value, int $duration): bool
{
$expires = 0;
if ($duration) {
diff --git a/src/Cache/Engine/FileEngine.php b/src/Cache/Engine/FileEngine.php
index 47d9600b94..3ac4d9c343 100644
--- a/src/Cache/Engine/FileEngine.php
+++ b/src/Cache/Engine/FileEngine.php
@@ -45,7 +45,7 @@ class FileEngine extends CacheEngine
/**
* Instance of SplFileObject class
*
- * @var SplFileObject
+ * @var SplFileObject|null
*/
protected ?SplFileObject $_File = null;
@@ -67,7 +67,7 @@ class FileEngine extends CacheEngine
*
* @var bool
*/
- protected $_init = true;
+ protected bool $_init = true;
/**
* Initialize the Cache Engine
@@ -78,7 +78,7 @@ class FileEngine extends CacheEngine
* @param array $settings array of setting for the engine
* @return bool True if the engine has been successfully initialized, false if not
*/
- public function init($settings = [])
+ public function init(array $settings = []): bool
{
$settings += [
'engine' => 'File',
@@ -91,7 +91,7 @@ public function init($settings = [])
];
parent::init($settings);
- if (DS === '\\') {
+ if (DIRECTORY_SEPARATOR === '\\') {
$this->settings['isWindows'] = true;
}
if (substr($this->settings['path'], -1) !== DS) {
@@ -107,10 +107,10 @@ public function init($settings = [])
/**
* Garbage collection. Permanently remove all expired and deleted data
*
- * @param int $expires [optional] An expires timestamp, invalidating all data before.
+ * @param int|null $expires [optional] An expires timestamp, invalidating all data before.
* @return bool True if garbage collection was successful, false on failure
*/
- public function gc($expires = null)
+ public function gc(?int $expires = null): bool
{
return $this->clear(true);
}
@@ -119,11 +119,11 @@ public function gc($expires = null)
* Write data for key into cache
*
* @param string $key Identifier for the data
- * @param mixed $data Data to be cached
+ * @param mixed $value Data to be cached
* @param int $duration How long to cache the data, in seconds
* @return bool True if the data was successfully cached, false on failure
*/
- public function write($key, $data, $duration)
+ public function write(string $key, mixed $value, int $duration): bool
{
if (!$this->_init) {
return false;
@@ -141,14 +141,14 @@ public function write($key, $data, $duration)
if (!empty($this->settings['serialize'])) {
if ($this->settings['isWindows']) {
- $data = str_replace('\\', '\\\\\\\\', serialize($data));
+ $value = str_replace('\\', '\\\\\\\\', serialize($value));
} else {
- $data = serialize($data);
+ $value = serialize($value);
}
}
$expires = time() + $duration;
- $contents = implode('', [$expires, $lineBreak, $data, $lineBreak]);
+ $contents = implode('', [$expires, $lineBreak, $value, $lineBreak]);
if ($this->settings['lock']) {
$this->_File->flock(LOCK_EX);
@@ -170,7 +170,7 @@ public function write($key, $data, $duration)
* @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)
+ public function read(string $key): mixed
{
if (!$this->_init || $this->_setKey($key) === false) {
return false;
@@ -184,7 +184,7 @@ public function read($key)
$time = time();
$cachetime = (int)$this->_File->current();
- if ($cachetime !== false && ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime)) {
+ if ($cachetime < $time || ($time + $this->settings['duration']) < $cachetime) {
if ($this->settings['lock']) {
$this->_File->flock(LOCK_UN);
}
@@ -221,7 +221,7 @@ public function read($key)
* @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(string $key)
+ public function delete(string $key): bool
{
if ($this->_setKey($key) === false || !$this->_init) {
return false;
@@ -229,9 +229,7 @@ public function delete(string $key)
$path = $this->_File->getRealPath();
$this->_File = null;
- //@codingStandardsIgnoreStart
- return @unlink($path);
- //@codingStandardsIgnoreEnd
+ return @unlink($path); // phpcs:ignore
}
/**
@@ -240,7 +238,7 @@ public function delete(string $key)
* @param bool $check Optional - only delete expired cache items
* @return bool True if the cache was successfully cleared, false otherwise
*/
- public function clear(bool $check)
+ public function clear(bool $check): bool
{
if (!$this->_init) {
return false;
@@ -281,8 +279,11 @@ public function clear(bool $check)
* @param int $threshold Any file not modified after this value will be deleted.
* @return void
*/
- protected function _clearDirectory($path, $now, $threshold)
- {
+ protected function _clearDirectory(
+ string $path,
+ int $now,
+ int $threshold,
+ ): void {
$prefixLength = strlen($this->settings['prefix']);
if (!is_dir($path)) {
@@ -321,9 +322,7 @@ protected function _clearDirectory($path, $now, $threshold)
$filePath = $file->getRealPath();
$file = null;
- //@codingStandardsIgnoreStart
- @unlink($filePath);
- //@codingStandardsIgnoreEnd
+ @unlink($filePath); // phpcs:ignore
}
}
}
@@ -336,7 +335,7 @@ protected function _clearDirectory($path, $now, $threshold)
* @return int|false
* @throws CacheException
*/
- public function decrement(string $key, int $offset = 1)
+ public function decrement(string $key, int $offset = 1): int|false
{
throw new CacheException(__d('cake_dev', 'Files cannot be atomically decremented.'));
}
@@ -349,7 +348,7 @@ public function decrement(string $key, int $offset = 1)
* @return int|false
* @throws CacheException
*/
- public function increment(string $key, int $offset = 1)
+ public function increment(string $key, int $offset = 1): int|false
{
throw new CacheException(__d('cake_dev', 'Files cannot be atomically incremented.'));
}
@@ -362,7 +361,7 @@ public function increment(string $key, int $offset = 1)
* @param bool $createKey Whether the key should be created if it doesn't exists, or not
* @return bool true if the cache key could be set, false otherwise
*/
- protected function _setKey($key, $createKey = false)
+ protected function _setKey(string $key, bool $createKey = false): bool
{
$groups = null;
if (!empty($this->_groupPrefix)) {
@@ -410,7 +409,7 @@ protected function _setKey($key, $createKey = false)
*
* @return bool
*/
- protected function _active()
+ protected function _active(): bool
{
$dir = new SplFileInfo($this->settings['path']);
if (Configure::read('debug')) {
@@ -432,18 +431,16 @@ protected function _active()
/**
* Generates a safe key for use with cache engine storage engines.
*
- * @param string $key the key passed over
- * @return mixed string $key or false
+ * @param string|null $key the key passed over
+ * @return string|false string $key or false
*/
- public function key($key)
+ public function key(?string $key): string|false
{
if (empty($key)) {
return false;
}
- $key = Inflector::underscore(str_replace([DS, '/', '.', '<', '>', '?', ':', '|', '*', '"'], '_', strval($key)));
-
- return $key;
+ return Inflector::underscore(str_replace([DS, '/', '.', '<', '>', '?', ':', '|', '*', '"'], '_', $key));
}
/**
@@ -466,9 +463,7 @@ public function clearGroup(string $group): bool
if ($object->isFile() && $containsGroup && $hasPrefix) {
$path = $object->getPathName();
$object = null;
- //@codingStandardsIgnoreStart
- @unlink($path);
- //@codingStandardsIgnoreEnd
+ @unlink($path); // phpcs:ignore
}
}
@@ -484,7 +479,7 @@ public function clearGroup(string $group): bool
* @param int $duration How long to cache the data, in seconds.
* @return bool True if the data was successfully cached, false on failure.
*/
- public function add($key, $value, $duration)
+ public function add(string $key, mixed $value, int $duration): bool
{
$cachedValue = $this->read($key);
if ($cachedValue === false) {
diff --git a/src/Cache/Engine/MemcacheEngine.php b/src/Cache/Engine/MemcacheEngine.php
index 1e7356d81b..e4b77437fc 100644
--- a/src/Cache/Engine/MemcacheEngine.php
+++ b/src/Cache/Engine/MemcacheEngine.php
@@ -68,7 +68,7 @@ class MemcacheEngine extends CacheEngine
* @param array $settings array of setting for the engine
* @return bool True if the engine has been successfully initialized, false if not
*/
- public function init($settings = [])
+ public function init(array $settings = []): bool
{
if (!class_exists(Memcache::class)) {
return false;
@@ -113,7 +113,7 @@ public function init($settings = [])
* @param string $server The server address string.
* @return array Array containing host, port
*/
- protected function _parseServerString($server)
+ protected function _parseServerString(string $server): array
{
if (str_starts_with($server, 'unix://')) {
return [$server, 0];
@@ -147,7 +147,7 @@ protected function _parseServerString($server)
* @return bool True if the data was successfully cached, false on failure
* @see http://php.net/manual/en/memcache.set.php
*/
- public function write($key, $value, $duration)
+ public function write(string $key, mixed $value, int $duration): bool
{
if ($duration > 30 * DAY) {
$duration = 0;
@@ -162,7 +162,7 @@ public function write($key, $value, $duration)
* @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)
+ public function read(string $key): mixed
{
return $this->_Memcache->get($key);
}
@@ -175,11 +175,11 @@ public function read($key)
* @return int|false New incremented value, false otherwise
* @throws CacheException when you try to increment with compress = true
*/
- public function increment(string $key, int $offset = 1)
+ public function increment(string $key, int $offset = 1): int|false
{
if ($this->settings['compress']) {
throw new CacheException(
- __d('cake_dev', 'Method %s not implemented for compressed cache in %s', 'increment()', self::class),
+ __d('cake_dev', 'Method %s not implemented for compressed cache in %s', 'increment()', static::class),
);
}
@@ -194,11 +194,11 @@ public function increment(string $key, int $offset = 1)
* @return int|false New decremented value, false otherwise
* @throws CacheException when you try to decrement with compress = true
*/
- public function decrement(string $key, int $offset = 1)
+ public function decrement(string $key, int $offset = 1): int|false
{
if ($this->settings['compress']) {
throw new CacheException(
- __d('cake_dev', 'Method %s not implemented for compressed cache in %s', 'decrement()', self::class),
+ __d('cake_dev', 'Method %s not implemented for compressed cache in %s', 'decrement()', static::class),
);
}
@@ -211,7 +211,7 @@ public function decrement(string $key, int $offset = 1)
* @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(string $key)
+ public function delete(string $key): bool
{
return $this->_Memcache->delete($key);
}
@@ -223,7 +223,7 @@ public function delete(string $key)
* on key TTL values.
* @return bool True if the cache was successfully cleared, false otherwise
*/
- public function clear(bool $check)
+ public function clear(bool $check): bool
{
if ($check) {
return true;
@@ -257,7 +257,7 @@ public function clear(bool $check)
* @param int $port Server port
* @return bool True if memcache server was connected
*/
- public function connect($host, $port = 11211)
+ public function connect(string $host, int $port = 11211): bool
{
if ($this->_Memcache->getServerStatus($host, $port) === 0) {
if ($this->_Memcache->connect($host, $port)) {
@@ -277,7 +277,7 @@ public function connect($host, $port = 11211)
*
* @return array
*/
- public function groups()
+ public function groups(): array
{
if (empty($this->_compiledGroupNames)) {
foreach ($this->settings['groups'] as $group) {
@@ -289,7 +289,7 @@ public function groups()
if (count($groups) !== count($this->settings['groups'])) {
foreach ($this->_compiledGroupNames as $group) {
if (!isset($groups[$group])) {
- $this->_Memcache->set($group, 1, false, 0);
+ $this->_Memcache->set($group, 1, 0, 0);
$groups[$group] = 1;
}
}
@@ -329,7 +329,7 @@ public function clearGroup(string $group): bool
* @return bool True if the data was successfully cached, false on failure.
* @link http://php.net/manual/en/memcache.add.php
*/
- public function add($key, $value, $duration)
+ public function add(string $key, mixed $value, int $duration): bool
{
if ($duration > 30 * DAY) {
$duration = 0;
diff --git a/src/Cache/Engine/MemcachedEngine.php b/src/Cache/Engine/MemcachedEngine.php
index 9d584836bb..8508ad9152 100644
--- a/src/Cache/Engine/MemcachedEngine.php
+++ b/src/Cache/Engine/MemcachedEngine.php
@@ -39,7 +39,7 @@ class MemcachedEngine extends CacheEngine
/**
* memcached wrapper.
*
- * @var Memcached
+ * @var Memcached|null
*/
protected ?Memcached $_Memcached = null;
@@ -73,7 +73,7 @@ class MemcachedEngine extends CacheEngine
*
* @var array
*/
- protected $_serializers = [
+ protected array $_serializers = [
'igbinary' => Memcached::SERIALIZER_IGBINARY,
'json' => Memcached::SERIALIZER_JSON,
'php' => Memcached::SERIALIZER_PHP,
@@ -89,7 +89,7 @@ class MemcachedEngine extends CacheEngine
* @return bool True if the engine has been successfully initialized, false if not
* @throws CacheException when you try use authentication without Memcached compiled with SASL support
*/
- public function init($settings = [])
+ public function init(array $settings = []): bool
{
if (!class_exists(Memcached::class)) {
return false;
@@ -166,7 +166,7 @@ public function init($settings = [])
* @throws CacheException when the Memcached extension is not built with the desired serializer engine
* @return void
*/
- protected function _setOptions()
+ protected function _setOptions(): void
{
$this->_Memcached->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
@@ -200,7 +200,7 @@ protected function _setOptions()
* @param string $server The server address string.
* @return array Array containing host, port
*/
- protected function _parseServerString($server)
+ protected function _parseServerString(string $server): array
{
$socketTransport = 'unix://';
if (str_starts_with($server, $socketTransport)) {
@@ -235,7 +235,7 @@ protected function _parseServerString($server)
* @return bool True if the data was successfully cached, false on failure
* @see http://php.net/manual/en/memcache.set.php
*/
- public function write($key, $value, $duration)
+ public function write(string $key, mixed $value, int $duration): bool
{
if ($duration > 30 * DAY) {
$duration = 0;
@@ -250,7 +250,7 @@ public function write($key, $value, $duration)
* @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)
+ public function read(string $key): mixed
{
return $this->_Memcached->get($key);
}
@@ -263,7 +263,7 @@ public function read($key)
* @return int|false New incremented value, false otherwise
* @throws CacheException when you try to increment with compress = true
*/
- public function increment(string $key, int $offset = 1)
+ public function increment(string $key, int $offset = 1): int|false
{
return $this->_Memcached->increment($key, $offset);
}
@@ -276,7 +276,7 @@ public function increment(string $key, int $offset = 1)
* @return int|false New decremented value, false otherwise
* @throws CacheException when you try to decrement with compress = true
*/
- public function decrement(string $key, int $offset = 1)
+ public function decrement(string $key, int $offset = 1): int|false
{
return $this->_Memcached->decrement($key, $offset);
}
@@ -287,7 +287,7 @@ public function decrement(string $key, int $offset = 1)
* @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(string $key)
+ public function delete(string $key): bool
{
return $this->_Memcached->delete($key);
}
@@ -300,7 +300,7 @@ public function delete(string $key)
* @return bool True if the cache was successfully cleared, false otherwise. Will
* also return false if you are using a binary protocol.
*/
- public function clear(bool $check)
+ public function clear(bool $check): bool
{
if ($check) {
return true;
@@ -327,7 +327,7 @@ public function clear(bool $check)
*
* @return array
*/
- public function groups()
+ public function groups(): array
{
if (empty($this->_compiledGroupNames)) {
foreach ($this->settings['groups'] as $group) {
@@ -379,7 +379,7 @@ public function clearGroup(string $group): bool
* @return bool True if the data was successfully cached, false on failure.
* @link http://php.net/manual/en/memcached.add.php
*/
- public function add($key, $value, $duration)
+ public function add(string $key, mixed $value, int $duration): bool
{
if ($duration > 30 * DAY) {
$duration = 0;
diff --git a/src/Cache/Engine/RedisEngine.php b/src/Cache/Engine/RedisEngine.php
index f19603259f..3f811e63e0 100644
--- a/src/Cache/Engine/RedisEngine.php
+++ b/src/Cache/Engine/RedisEngine.php
@@ -34,9 +34,9 @@ class RedisEngine extends CacheEngine
/**
* Redis wrapper.
*
- * @var Redis
+ * @var Redis|null
*/
- protected $_Redis = null;
+ protected ?Redis $_Redis = null;
/**
* Settings
@@ -61,7 +61,7 @@ class RedisEngine extends CacheEngine
* @param array $settings array of setting for the engine
* @return bool True if the engine has been successfully initialized, false if not
*/
- public function init($settings = [])
+ public function init(array $settings = []): bool
{
if (!class_exists(Redis::class)) {
return false;
@@ -76,7 +76,7 @@ public function init($settings = [])
'timeout' => 0,
'persistent' => true,
'unix_socket' => false,
- ], $settings),);
+ ], $settings));
return $this->_connect();
}
@@ -86,7 +86,7 @@ public function init($settings = [])
*
* @return bool True if Redis server was connected
*/
- protected function _connect()
+ protected function _connect(): bool
{
try {
$this->_Redis = new Redis();
@@ -119,7 +119,7 @@ protected function _connect()
* @param int $duration How long to cache the data, in seconds
* @return bool True if the data was successfully cached, false on failure
*/
- public function write($key, $value, $duration)
+ public function write(string $key, mixed $value, int $duration): bool
{
if (!is_int($value)) {
$value = serialize($value);
@@ -142,10 +142,10 @@ public function write($key, $value, $duration)
* @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)
+ public function read(string $key): mixed
{
$value = $this->_Redis->get($key);
- if (preg_match('/^[-]?\d+$/', $value)) {
+ if (preg_match('/^-?\d+$/', $value)) {
return (int)$value;
}
if ($value !== false && is_string($value)) {
@@ -163,7 +163,7 @@ public function read($key)
* @return int|false New incremented value, false otherwise
* @throws CacheException when you try to increment with compress = true
*/
- public function increment(string $key, int $offset = 1)
+ public function increment(string $key, int $offset = 1): int|false
{
return (int)$this->_Redis->incrBy($key, $offset);
}
@@ -176,7 +176,7 @@ public function increment(string $key, int $offset = 1)
* @return int|false New decremented value, false otherwise
* @throws CacheException when you try to decrement with compress = true
*/
- public function decrement(string $key, int $offset = 1)
+ public function decrement(string $key, int $offset = 1): int|false
{
return (int)$this->_Redis->decrBy($key, $offset);
}
@@ -187,7 +187,7 @@ public function decrement(string $key, int $offset = 1)
* @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(string $key)
+ public function delete(string $key): bool
{
return $this->_Redis->delete($key) > 0;
}
@@ -199,7 +199,7 @@ public function delete(string $key)
* true, no keys will be removed as cache will rely on redis TTL's.
* @return bool True if the cache was successfully cleared, false otherwise
*/
- public function clear(bool $check)
+ public function clear(bool $check): bool
{
if ($check) {
return true;
@@ -217,7 +217,7 @@ public function clear(bool $check)
*
* @return array
*/
- public function groups()
+ public function groups(): array
{
$result = [];
foreach ($this->settings['groups'] as $group) {
@@ -264,7 +264,7 @@ public function __destruct()
* @return bool True if the data was successfully cached, false on failure.
* @link https://github.com/phpredis/phpredis#setnx
*/
- public function add($key, $value, $duration)
+ public function add(string $key, mixed $value, int $duration): bool
{
if (!is_int($value)) {
$value = serialize($value);
diff --git a/src/Configure/ConfigReaderInterface.php b/src/Configure/ConfigReaderInterface.php
index 6d4c084aaf..fef3dd09c7 100644
--- a/src/Configure/ConfigReaderInterface.php
+++ b/src/Configure/ConfigReaderInterface.php
@@ -31,14 +31,14 @@ interface ConfigReaderInterface
* @param string $key Key to read.
* @return array An array of data to merge into the runtime configuration
*/
- public function read($key);
+ public function read(string $key): array;
/**
* Dumps the configure data into source.
*
* @param string $key The identifier to write to.
* @param array $data The data to dump.
- * @return bool True on success or false on failure.
+ * @return int|false True on success or false on failure.
*/
- public function dump($key, $data);
+ public function dump(string $key, array $data): int|false;
}
diff --git a/src/Configure/IniReader.php b/src/Configure/IniReader.php
index 02cdbc0104..3057f0b8e3 100644
--- a/src/Configure/IniReader.php
+++ b/src/Configure/IniReader.php
@@ -61,27 +61,29 @@ class IniReader implements ConfigReaderInterface
/**
* The path to read ini files from.
*
- * @var array
+ * @var string|null
*/
- protected $_path;
+ protected ?string $_path = null;
/**
* The section to read, if null all sections will be read.
*
- * @var string
+ * @var string|null
*/
- protected $_section;
+ protected ?string $_section = null;
/**
* Build and construct a new ini file parser. The parser can be used to read
* ini files that are on the filesystem.
*
- * @param string $path Path to load ini config files from. Defaults to CONFIG
- * @param string $section Only get one section, leave null to parse and fetch
+ * @param string|null $path Path to load ini config files from. Defaults to CONFIG
+ * @param string|null $section Only get one section, leave null to parse and fetch
* all sections in the ini file.
*/
- public function __construct($path = null, $section = null)
- {
+ public function __construct(
+ ?string $path = null,
+ ?string $section = null,
+ ) {
if (!$path) {
$path = CONFIG;
}
@@ -100,7 +102,7 @@ public function __construct($path = null, $section = null)
* @throws ConfigureException when files don't exist.
* Or when files contain '..' as this could lead to abusive reads.
*/
- public function read($key)
+ public function read(string $key): array
{
if (str_contains($key, '..')) {
throw new ConfigureException(__d('cake_dev', 'Cannot load configuration files with ../ in them.'));
@@ -135,7 +137,7 @@ public function read($key)
* @param array $values Values to be exploded.
* @return array Array of values exploded
*/
- protected function _parseNestedValues($values)
+ protected function _parseNestedValues(array $values): array
{
foreach ($values as $key => $value) {
if ($value === '1') {
@@ -161,9 +163,9 @@ protected function _parseNestedValues($values)
* @param string $key The identifier to write to. If the key has a . it will be treated
* as a plugin prefix.
* @param array $data The data to convert to ini file.
- * @return int Bytes saved.
+ * @return int|false Bytes saved.
*/
- public function dump($key, $data)
+ public function dump(string $key, array $data): int|false
{
$result = [];
foreach ($data as $k => $value) {
@@ -195,7 +197,7 @@ public function dump($key, $data)
* @param mixed $val Value to export.
* @return string String value for ini file.
*/
- protected function _value($val)
+ protected function _value(mixed $val): string
{
if ($val === null) {
return 'null';
@@ -217,7 +219,7 @@ protected function _value($val)
* as a plugin prefix.
* @return string Full file path
*/
- protected function _getFilePath($key)
+ protected function _getFilePath(string $key): string
{
if (str_ends_with($key, '.ini.php')) {
$key = substr($key, 0, -8);
diff --git a/src/Configure/PhpReader.php b/src/Configure/PhpReader.php
index 1e0f45275c..a85b9df06f 100644
--- a/src/Configure/PhpReader.php
+++ b/src/Configure/PhpReader.php
@@ -41,9 +41,9 @@ class PhpReader implements ConfigReaderInterface
/**
* Constructor for PHP Config file reading.
*
- * @param string $path The path to read config files from. Defaults to CONFIG
+ * @param string|null $path The path to read config files from. Defaults to CONFIG
*/
- public function __construct($path = null)
+ public function __construct(?string $path = null)
{
if (!$path) {
$path = CONFIG;
@@ -63,7 +63,7 @@ public function __construct($path = null)
* @throws ConfigureException when files don't exist or they don't contain `$config`.
* Or when files contain '..' as this could lead to abusive reads.
*/
- public function read($key)
+ public function read(string $key): array
{
if (str_contains($key, '..')) {
throw new ConfigureException(__d('cake_dev', 'Cannot load configuration files with ../ in them.'));
@@ -89,9 +89,9 @@ public function read($key)
* @param string $key The identifier to write to. If the key has a . it will be treated
* as a plugin prefix.
* @param array $data Data to dump.
- * @return int Bytes saved.
+ * @return int|false Bytes saved.
*/
- public function dump($key, $data)
+ public function dump(string $key, array $data): int|false
{
$contents = 'params['connection'])) {
@@ -81,8 +81,9 @@ public function startup()
$out .= __d('cake_console', 'Current ACL Classname: %s', $class) . "\n";
$out .= "--------------------------------------------------\n";
$this->err($out);
+ $this->_stop(1);
- return $this->_stop(1);
+ return;
}
if ($this->command) {
@@ -90,12 +91,14 @@ public function startup()
$this->err(__d('cake_console', 'Your database configuration was not found.'));
$this->err(__d('cake_console', 'Please create app/Config/database.php manually.'));
$this->err(__d('cake_console', 'You can use app/Config/database.php.default as a template.'));
+ $this->_stop(1);
- return $this->_stop(1);
+ return;
}
+
require_once CONFIG . 'database.php';
- if (!in_array($this->command, ['initdb'])) {
+ if ($this->command !== 'initdb') {
$collection = new ComponentCollection();
$this->Acl = new AclComponent($collection);
$controller = new Controller();
@@ -119,7 +122,7 @@ public function main(): void
*
* @return void
*/
- public function create()
+ public function create(): void
{
extract($this->_dataVars());
@@ -154,7 +157,7 @@ public function create()
*
* @return void
*/
- public function delete()
+ public function delete(): void
{
extract($this->_dataVars());
@@ -178,7 +181,7 @@ public function delete()
*
* @return void
*/
- public function setParent()
+ public function setParent(): void
{
extract($this->_dataVars());
$target = $this->parseIdentifier($this->args[1]);
@@ -203,7 +206,7 @@ public function setParent()
*
* @return void
*/
- public function getPath()
+ public function getPath(): void
{
extract($this->_dataVars());
$identifier = $this->parseIdentifier($this->args[1]);
@@ -232,7 +235,7 @@ public function getPath()
* @param int $indent indent level.
* @return void
*/
- protected function _outputNode($class, $node, $indent)
+ protected function _outputNode(string $class, array $node, int $indent): void
{
$indent = str_repeat(' ', $indent);
$data = $node[$class];
@@ -248,7 +251,7 @@ protected function _outputNode($class, $node, $indent)
*
* @return void
*/
- public function check()
+ public function check(): void
{
extract($this->_getParams());
@@ -264,7 +267,7 @@ public function check()
*
* @return void
*/
- public function grant()
+ public function grant(): void
{
extract($this->_getParams());
@@ -280,7 +283,7 @@ public function grant()
*
* @return void
*/
- public function deny()
+ public function deny(): void
{
extract($this->_getParams());
@@ -296,7 +299,7 @@ public function deny()
*
* @return void
*/
- public function inherit()
+ public function inherit(): void
{
extract($this->_getParams());
@@ -312,7 +315,7 @@ public function inherit()
*
* @return void
*/
- public function view()
+ public function view(): void
{
extract($this->_dataVars());
@@ -373,7 +376,7 @@ public function view()
*
* @return mixed
*/
- public function initdb()
+ public function initdb(): mixed
{
return $this->dispatchShell('schema create DbAcl');
}
@@ -538,7 +541,7 @@ public function getOptionParser(): ConsoleOptionParser
*
* @return bool Success
*/
- public function nodeExists()
+ public function nodeExists(): bool
{
if (!isset($this->args[0]) || !isset($this->args[1])) {
return false;
@@ -560,11 +563,14 @@ public function nodeExists()
* Takes an identifier determines its type and returns the result as used by other methods.
*
* @param string $identifier Identifier to parse
- * @return mixed a string for aliases, and an array for model.foreignKey
+ * @return array{
+ * model: string,
+ * foreign_key: string
+ * }|string a string for aliases, and an array for model.foreignKey
*/
- public function parseIdentifier($identifier)
+ public function parseIdentifier(string $identifier): array|string
{
- if (preg_match('/^([\w]+)\.(.*)$/', $identifier, $matches)) {
+ if (preg_match('/^(\w+)\.(.*)$/', $identifier, $matches)) {
return [
'model' => $matches[1],
'foreign_key' => $matches[2],
@@ -580,9 +586,9 @@ public function parseIdentifier($identifier)
*
* @param string $class Class type you want (Aro/Aco)
* @param array|string|null $identifier A mixed identifier for finding the node, otherwise null.
- * @return int Integer of NodeId. Will trigger an error if nothing is found.
+ * @return int|null Integer of NodeId. Will trigger an error if nothing is found.
*/
- protected function _getNodeId($class, $identifier)
+ protected function _getNodeId(string $class, array|string|null $identifier): ?int
{
$node = $this->Acl->{$class}->node($identifier);
if (empty($node)) {
@@ -600,9 +606,15 @@ protected function _getNodeId($class, $identifier)
/**
* get params for standard Acl methods
*
- * @return array aro, aco, action
+ * @return array{
+ * aro: array|string|int,
+ * aco: array|string|int,
+ * action: string,
+ * aroName: string|int,
+ * acoName: string|int
+ * } aro, aco, action
*/
- protected function _getParams()
+ protected function _getParams(): array
{
$aro = is_numeric($this->args[0]) ? (int)$this->args[0] : $this->args[0];
$aco = is_numeric($this->args[1]) ? (int)$this->args[1] : $this->args[1];
@@ -626,10 +638,15 @@ protected function _getParams()
/**
* Build data parameters based on node type
*
- * @param string $type Node type (ARO/ACO)
- * @return array Variables
+ * @param string|null $type Node type (ARO/ACO)
+ * @return array{
+ * secondary_id: string,
+ * data_name: string,
+ * table_name: string,
+ * class: string
+ * } Variables
*/
- protected function _dataVars($type = null)
+ protected function _dataVars(?string $type = null): array
{
if (!$type) {
$type = $this->args[0];
diff --git a/src/Console/Command/ApiShell.php b/src/Console/Command/ApiShell.php
index 383de57bdd..cf5cdc5514 100644
--- a/src/Console/Command/ApiShell.php
+++ b/src/Console/Command/ApiShell.php
@@ -41,14 +41,14 @@ class ApiShell extends AppShell
*
* @var array
*/
- public $paths = [];
+ public array $paths = [];
/**
* Override initialize of the Shell
*
* @return void
*/
- public function initialize()
+ public function initialize(): void
{
$this->paths = array_merge($this->paths, [
'behavior' => CAKE . 'Model' . DS . 'Behavior' . DS,
@@ -77,11 +77,7 @@ public function main(): void
$type = strtolower($this->args[0]);
- if (isset($this->paths[$type])) {
- $path = $this->paths[$type];
- } else {
- $path = $this->paths['core'];
- }
+ $path = $this->paths[$type] ?? $this->paths['core'];
$count = count($this->args);
if ($count > 1) {
@@ -90,6 +86,9 @@ public function main(): void
} elseif ($count) {
$file = $type;
$class = Inflector::camelize($type);
+ } else {
+ $file = null;
+ $class = null;
}
$objects = App::objects('class', $path);
if (in_array($class, $objects)) {
@@ -116,11 +115,12 @@ public function main(): void
$method = $parsed[$this->params['method']];
$this->out($class . '::' . $method['method'] . $method['parameters']);
$this->hr();
- $this->out($method['comment'], true);
+ $this->out($method['comment'], 1);
} else {
$this->out(ucwords($class));
$this->hr();
$i = 0;
+ $list = [];
foreach ($parsed as $method) {
$list[] = ++$i . '. ' . $method['method'] . $method['parameters'];
}
@@ -145,7 +145,7 @@ public function main(): void
$this->hr();
$this->out($class . '::' . $method['method'] . $method['parameters']);
$this->hr();
- $this->out($method['comment'], true);
+ $this->out($method['comment'], 1);
}
}
}
@@ -180,7 +180,7 @@ public function getOptionParser(): ConsoleOptionParser
*
* @return void
*/
- public function help()
+ public function help(): void
{
$head = "Usage: cake api [] [-m ]\n";
$head .= "-----------------------------------------------\n";
@@ -218,15 +218,19 @@ public function help()
* signatures.
*
* @param string $path File path
- * @param string $class Class name
+ * @param class-string $class Class name
* @return array Methods and signatures indexed by method name
*/
- protected function _parseClass($path, $class)
- {
+ protected function _parseClass(
+ string $path,
+ string $class,
+ ): array {
$parsed = [];
if (!class_exists($class) && !include_once $path) {
$this->err(__d('cake_console', '%s could not be found', $path));
+
+ return [];
}
$reflection = new ReflectionClass($class);
diff --git a/src/Console/Command/CommandListShell.php b/src/Console/Command/CommandListShell.php
index 9aab7db0fd..b77825a8a8 100644
--- a/src/Console/Command/CommandListShell.php
+++ b/src/Console/Command/CommandListShell.php
@@ -36,9 +36,9 @@ class CommandListShell extends AppShell
/**
* Contains tasks to load and instantiate
*
- * @var array
+ * @var array|bool|null
*/
- public $tasks = ['Command'];
+ public array|bool|null $tasks = ['Command'];
/**
* startup
diff --git a/src/Console/Command/CompletionShell.php b/src/Console/Command/CompletionShell.php
index 0b27b4042b..15363c9561 100644
--- a/src/Console/Command/CompletionShell.php
+++ b/src/Console/Command/CompletionShell.php
@@ -32,9 +32,9 @@ class CompletionShell extends AppShell
/**
* Contains tasks to load and instantiate
*
- * @var array
+ * @var array|bool|null
*/
- public $tasks = ['Command'];
+ public array|bool|null $tasks = ['Command'];
/**
* Echo no header by overriding the startup method
diff --git a/src/Console/Command/ConsoleShell.php b/src/Console/Command/ConsoleShell.php
index 0416f34916..907e9fad1a 100644
--- a/src/Console/Command/ConsoleShell.php
+++ b/src/Console/Command/ConsoleShell.php
@@ -38,37 +38,42 @@ class ConsoleShell extends AppShell
*
* @var array
*/
- public $associations = ['hasOne', 'hasMany', 'belongsTo', 'hasAndBelongsToMany'];
+ public array $associations = [
+ 'hasOne',
+ 'hasMany',
+ 'belongsTo',
+ 'hasAndBelongsToMany',
+ ];
/**
* Chars that describe invalid commands
*
* @var array
*/
- public $badCommandChars = ['$', ';'];
+ public array $badCommandChars = ['$', ';'];
/**
* Available models
*
* @var array
*/
- public $models = [];
+ public array $models = [];
/**
* _finished
*
* This shell is perpetual, setting this property to true exits the process
*
- * @var mixed
+ * @var bool
*/
- protected $_finished = false;
+ protected bool $_finished = false;
/**
* _methodPatterns
*
- * @var array
+ * @var array
*/
- protected $_methodPatterns = [
+ protected array $_methodPatterns = [
'help' => '/^(help|\?)/',
'_exit' => '/^(quit|exit)/',
'_models' => '/^models/i',
@@ -83,12 +88,17 @@ class ConsoleShell extends AppShell
'_routeToArray' => '/^route\s+(.*)$/i',
];
+ /**
+ * @var Dispatcher|null
+ */
+ public ?Dispatcher $Dispatcher = null;
+
/**
* Override startup of the Shell
*
* @return void
*/
- public function startup()
+ public function startup(): void
{
$this->Dispatcher = new Dispatcher();
$this->models = App::objects('Model');
@@ -197,7 +207,7 @@ public function getOptionParser(): ConsoleOptionParser
*
* @return void
*/
- public function help()
+ public function help(): void
{
$optionParser = $this->getOptionParser();
$this->out($optionParser->epilog());
@@ -212,7 +222,7 @@ public function help()
public function main(?string $command = null): void
{
$this->_finished = false;
- while (!$this->_finished) {
+ while (!$this->_finished) { // @phpstan-ignore-line
if (empty($command)) {
$command = trim($this->in(''));
}
@@ -235,7 +245,7 @@ public function main(?string $command = null): void
* @param string|null $command The command to run.
* @return string|false
*/
- protected function _method(?string $command)
+ protected function _method(?string $command): string|false
{
foreach ($this->_methodPatterns as $method => $pattern) {
if (preg_match($pattern, $command ?? '')) {
@@ -273,10 +283,10 @@ protected function _models(): void
/**
* Bind an association
*
- * @param mixed $command The command to run.
+ * @param string $command The command to run.
* @return void
*/
- protected function _bind($command): void
+ protected function _bind(string $command): void
{
preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp);
@@ -306,10 +316,10 @@ protected function _bind($command): void
/**
* Unbind an association
*
- * @param mixed $command The command to run.
+ * @param string $command The command to run.
* @return void
*/
- protected function _unbind($command): void
+ protected function _unbind(string $command): void
{
preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp);
@@ -349,10 +359,10 @@ protected function _unbind($command): void
/**
* Perform a find
*
- * @param mixed $command The command to run.
+ * @param string $command The command to run.
* @return void
*/
- protected function _find($command): void
+ protected function _find(string $command): void
{
$command = strip_tags($command);
$command = str_replace($this->badCommandChars, '', $command);
@@ -361,10 +371,11 @@ protected function _find($command): void
[$modelToCheck] = explode('->', $command);
if ($this->_isValidModel($modelToCheck)) {
+ /** @var mixed $data */
$data = null;
$findCommand = "\$data = \$this->$command;";
- // phpcs:ignore
- @eval($findCommand);
+
+ @eval($findCommand);// phpcs:ignore
if (is_array($data)) {
foreach ($data as $idx => $results) {
@@ -412,10 +423,10 @@ protected function _find($command): void
/**
* Save a record
*
- * @param mixed $command The command to run.
+ * @param string $command The command to run.
* @return void
*/
- protected function _save($command)
+ protected function _save(string $command): void
{
// Validate the model we're trying to save here
$command = strip_tags($command);
@@ -436,10 +447,10 @@ protected function _save($command)
/**
* Show the columns for a model
*
- * @param mixed $command The command to run.
+ * @param string $command The command to run.
* @return void
*/
- protected function _columns($command)
+ protected function _columns(string $command): void
{
preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp);
@@ -449,10 +460,10 @@ protected function _columns($command)
// Get the column info for this model
$data = null;
$fieldsCommand = "\$data = \$this->{$modelToCheck}->getColumnTypes();";
- // phpcs:ignore
- @eval($fieldsCommand);
- if (is_array($data)) {
+ @eval($fieldsCommand); // phpcs:ignore
+
+ if (is_array($data)) { // @phpstan-ignore-line
foreach ($data as $field => $type) {
$this->out("\t{$field}: {$type}");
}
@@ -491,10 +502,10 @@ protected function _routesShow(): void
/**
* Parse an array URL and show the equivalent URL as a string
*
- * @param mixed $command The command to run.
+ * @param string $command The command to run.
* @return void
*/
- protected function _routeToString($command): void
+ protected function _routeToString(string $command): void
{
preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp);
@@ -507,10 +518,10 @@ protected function _routeToString($command): void
/**
* Parse a string URL and show as an array
*
- * @param mixed $command The command to run.
+ * @param string $command The command to run.
* @return void
*/
- protected function _routeToArray($command): void
+ protected function _routeToArray(string $command): void
{
preg_match($this->_methodPatterns[__FUNCTION__], $command, $tmp);
@@ -523,7 +534,7 @@ protected function _routeToArray($command): void
* @param string $modelToCheck The model to check.
* @return bool true if is an available model, false otherwise
*/
- protected function _isValidModel($modelToCheck): bool
+ protected function _isValidModel(string $modelToCheck): bool
{
return in_array($modelToCheck, $this->models);
}
@@ -538,10 +549,12 @@ protected function _loadRoutes(): bool
{
Router::reload();
extract(Router::getNamedExpressions());
+
// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
if (!@include CONFIG . 'routes.php') {
return false;
}
+
CakePlugin::routes();
Router::parse('/');
diff --git a/src/Console/Command/I18nShell.php b/src/Console/Command/I18nShell.php
index 8d48b8dc18..78d7fdfba8 100644
--- a/src/Console/Command/I18nShell.php
+++ b/src/Console/Command/I18nShell.php
@@ -18,6 +18,7 @@
namespace Cake\Console\Command;
use AppShell;
+use Cake\Console\Command\Task\ExtractTask;
use Cake\Console\ConsoleOptionParser;
use Cake\Core\App;
@@ -27,6 +28,7 @@
* Shell for I18N management.
*
* @package Cake.Console.Command
+ * @property ExtractTask $Extract
*/
class I18nShell extends AppShell
{
@@ -35,34 +37,33 @@ class I18nShell extends AppShell
*
* @var string
*/
- public $dataSource = 'default';
+ public string $dataSource = 'default';
/**
* Contains tasks to load and instantiate
*
- * @var array
+ * @var array|bool|null
*/
- public $tasks = ['Extract'];
+ public array|bool|null $tasks = ['Extract'];
/**
* Override startup of the Shell
*
- * @return mixed
+ * @return void
*/
- public function startup()
+ public function startup(): void
{
$this->_welcome();
if (isset($this->params['datasource'])) {
$this->dataSource = $this->params['datasource'];
}
- if ($this->command && !in_array($this->command, ['help'])) {
+ if ($this->command && $this->command !== 'help') {
if (!config('database')) {
$this->err(__d('cake_console', 'Your database configuration was not found.'));
$this->err(__d('cake_console', 'Please create app/Config/database.php manually.'));
$this->err(__d('cake_console', 'You can use app/Config/database.php.default as a template.'));
-
- return $this->_stop(1);
+ $this->_stop(1);
}
}
}
@@ -108,7 +109,7 @@ public function main(): void
*
* @return void
*/
- public function initdb()
+ public function initdb(): void
{
$this->dispatchShell('schema create i18n');
}
diff --git a/src/Console/Command/SchemaShell.php b/src/Console/Command/SchemaShell.php
index 8aaecbf3ba..b7d8304004 100644
--- a/src/Console/Command/SchemaShell.php
+++ b/src/Console/Command/SchemaShell.php
@@ -22,6 +22,7 @@
use Cake\Core\Configure;
use Cake\Model\CakeSchema;
use Cake\Model\ConnectionManager;
+use Cake\Model\Datasource\DboSource;
use Cake\Utility\CakeText;
use Cake\Utility\File;
use Cake\Utility\Folder;
@@ -60,7 +61,7 @@ class SchemaShell extends AppShell
*
* @return void
*/
- public function startup()
+ public function startup(): void
{
$this->_welcome();
$this->out('Cake Schema Shell');
@@ -134,7 +135,7 @@ public function view(): void
*
* @return void
*/
- public function generate()
+ public function generate(): void
{
$this->out(__d('cake_console', 'Generating Schema...'));
$options = [];
@@ -171,7 +172,7 @@ public function generate()
Configure::write('Cache.disable', $cacheDisable);
- if (!empty($this->params['exclude']) && !empty($content)) {
+ if (!empty($this->params['exclude'])) {
$excluded = CakeText::tokenize($this->params['exclude']);
foreach ($excluded as $table) {
unset($content['tables'][$table]);
@@ -191,7 +192,7 @@ public function generate()
$count = 0;
if (!empty($result[1])) {
foreach ($result[1] as $file) {
- if (preg_match('/' . preg_quote($fileName) . '(?:[_\d]*)?\.php$/', $file)) {
+ if (preg_match('/' . preg_quote($fileName, '/') . '(?:[_\d]*)?\.php$/', $file)) {
$count++;
}
}
@@ -208,7 +209,6 @@ public function generate()
if ($this->Schema->write($content)) {
$this->out(__d('cake_console', 'Schema file: %s generated', $content['file']));
-
$this->_stop();
return;
@@ -225,16 +225,17 @@ public function generate()
* If -write contains a full path name the file will be saved there. If -write only
* contains no DS, that will be used as the file name, in the same dir as the schema file.
*
- * @return string
+ * @return string|null
*/
- public function dump()
+ public function dump(): ?string
{
$write = false;
$schema = $this->Schema->load();
if (!$schema) {
$this->err(__d('cake_console', 'Schema could not be loaded'));
+ $this->_stop();
- return $this->_stop();
+ return null;
}
if (!empty($this->params['write'])) {
if ($this->params['write'] == 1) {
@@ -243,6 +244,7 @@ public function dump()
$write = $this->params['write'];
}
}
+ /** @var DboSource $db */
$db = ConnectionManager::getDataSource($this->Schema->connection);
$contents = "\n\n" . $db->dropSchema($schema) . "\n\n" . $db->createSchema($schema);
@@ -251,19 +253,21 @@ public function dump()
$write .= '.sql';
}
if (str_contains($write, DS)) {
- $File = new File($write, true);
+ $file = new File($write, true);
} else {
- $File = new File($this->Schema->path . DS . $write, true);
+ $file = new File($this->Schema->path . DS . $write, true);
}
- if ($File->write($contents)) {
- $this->out(__d('cake_console', 'SQL dump file created in %s', $File->pwd()));
+ if ($file->write($contents)) {
+ $this->out(__d('cake_console', 'SQL dump file created in %s', $file->pwd()));
+ $this->_stop();
- return $this->_stop();
+ return null;
}
$this->err(__d('cake_console', 'SQL dump could not be created'));
+ $this->_stop();
- return $this->_stop();
+ return null;
}
$this->out($contents);
@@ -275,7 +279,7 @@ public function dump()
*
* @return void
*/
- public function create()
+ public function create(): void
{
[$schema, $table] = $this->_loadSchema();
$this->_create($schema, $table);
@@ -286,7 +290,7 @@ public function create()
*
* @return void
*/
- public function update()
+ public function update(): void
{
[$schema, $table] = $this->_loadSchema();
$this->_update($schema, $table);
@@ -295,9 +299,9 @@ public function update()
/**
* Prepares the Schema objects for database operations.
*
- * @return array{CakeSchema, string|null}
+ * @return array{CakeSchema, string|null}|null
*/
- protected function _loadSchema()
+ protected function _loadSchema(): ?array
{
$name = $plugin = null;
if (!empty($this->params['name'])) {
@@ -328,8 +332,9 @@ protected function _loadSchema()
$this->err(__d('cake_console', 'Error: The chosen schema could not be loaded. Attempted to load:'));
$this->err(__d('cake_console', '- file: %s', $this->Schema->path . DS . $this->Schema->file));
$this->err(__d('cake_console', '- name: %s', $this->Schema->name));
+ $this->_stop(2);
- return $this->_stop(2);
+ return null;
}
$table = null;
if (isset($this->args[1])) {
@@ -347,8 +352,9 @@ protected function _loadSchema()
* @param string|null $table The table name.
* @return void
*/
- protected function _create(CakeSchema $schema, ?string $table = null)
+ protected function _create(CakeSchema $schema, ?string $table = null): void
{
+ /** @var DboSource $db */
$db = ConnectionManager::getDataSource($this->Schema->connection);
$drop = $create = [];
@@ -364,8 +370,9 @@ protected function _create(CakeSchema $schema, ?string $table = null)
}
if (empty($drop) || empty($create)) {
$this->out(__d('cake_console', 'Schema is up to date.'));
+ $this->_stop();
- return $this->_stop();
+ return;
}
$this->out("\n" . __d('cake_console', 'The following table(s) will be dropped.'));
@@ -396,12 +403,13 @@ protected function _create(CakeSchema $schema, ?string $table = null)
* Update database with Schema object
* Should be called via the run method
*
- * @param CakeSchema &$schema The schema instance
- * @param string $table The table name.
+ * @param CakeSchema $schema The schema instance
+ * @param string|null $table The table name.
* @return void
*/
- protected function _update(&$schema, $table = null)
+ protected function _update(CakeSchema $schema, ?string $table = null): void
{
+ /** @var DboSource $db */
$db = ConnectionManager::getDataSource($this->Schema->connection);
$this->out(__d('cake_console', 'Comparing Database to Schema...'));
@@ -416,10 +424,10 @@ protected function _update(&$schema, $table = null)
if (empty($table)) {
foreach ($compare as $table => $changes) {
- if (isset($compare[$table]['create'])) {
+ if (isset($changes['create'])) {
$contents[$table] = $db->createSchema($schema, $table);
} else {
- $contents[$table] = $db->alterSchema([$table => $compare[$table]], $table);
+ $contents[$table] = $db->alterSchema([$table => $changes], $table);
}
}
} elseif (isset($compare[$table])) {
@@ -432,8 +440,9 @@ protected function _update(&$schema, $table = null)
if (empty($contents)) {
$this->out(__d('cake_console', 'Schema is up to date.'));
+ $this->_stop();
- return $this->_stop();
+ return;
}
$this->out("\n" . __d('cake_console', 'The following statements will run.'));
@@ -461,14 +470,18 @@ protected function _update(&$schema, $table = null)
* @param CakeSchema $schema The schema instance.
* @return void
*/
- protected function _run($contents, $event, CakeSchema $schema)
- {
+ protected function _run(
+ array $contents,
+ string $event,
+ CakeSchema $schema,
+ ): void {
if (empty($contents)) {
$this->err(__d('cake_console', 'Sql could not be run'));
return;
}
Configure::write('debug', 2);
+ /** @var DboSource $db */
$db = ConnectionManager::getDataSource($this->Schema->connection);
foreach ($contents as $table => $sql) {
@@ -480,8 +493,9 @@ protected function _run($contents, $event, CakeSchema $schema)
$this->out($sql);
} else {
if (!$schema->before([$event => $table])) {
- return false;
+ return;
}
+
$error = null;
try {
$db->execute($sql);
diff --git a/src/Console/Command/ServerShell.php b/src/Console/Command/ServerShell.php
index 62433f1de9..4e2fa86e4a 100644
--- a/src/Console/Command/ServerShell.php
+++ b/src/Console/Command/ServerShell.php
@@ -48,30 +48,30 @@ class ServerShell extends AppShell
/**
* server host
*
- * @var string
+ * @var string|null
*/
- protected $_host = null;
+ protected ?string $_host = null;
/**
* listen port
*
- * @var string
+ * @var int|null
*/
- protected $_port = null;
+ protected ?int $_port = null;
/**
* document root
*
- * @var string
+ * @var string|null
*/
- protected $_documentRoot = null;
+ protected ?string $_documentRoot = null;
/**
* Override initialize of the Shell
*
* @return void
*/
- public function initialize()
+ public function initialize(): void
{
$this->_host = static::DEFAULT_HOST;
$this->_port = static::DEFAULT_PORT;
@@ -88,7 +88,7 @@ public function initialize()
* @return void
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::startup
*/
- public function startup()
+ public function startup(): void
{
if (!empty($this->params['host'])) {
$this->_host = $this->params['host'];
@@ -116,7 +116,7 @@ public function startup()
*
* @return void
*/
- protected function _welcome()
+ protected function _welcome(): void
{
$this->out();
$this->out(__d('cake_console', 'Welcome to CakePHP %s Console', 'v' . Configure::version()));
diff --git a/src/Console/Command/Task/CommandTask.php b/src/Console/Command/Task/CommandTask.php
index a5e0ff9455..faf60185c5 100644
--- a/src/Console/Command/Task/CommandTask.php
+++ b/src/Console/Command/Task/CommandTask.php
@@ -17,6 +17,7 @@
use AppShell;
use Cake\Console\ConsoleOptionParser;
+use Cake\Console\Shell;
use Cake\Console\TaskCollection;
use Cake\Core\App;
use Cake\Core\CakePlugin;
@@ -142,10 +143,10 @@ public function subCommands($commandName)
/**
* Get Shell instance for the given command
*
- * @param mixed $commandName The command you want.
- * @return mixed
+ * @param string|null $commandName The command you want.
+ * @return Shell|false
*/
- public function getShell($commandName)
+ public function getShell(?string $commandName): Shell|false
{
[$pluginDot, $name] = pluginSplit($commandName, true);
@@ -178,16 +179,16 @@ public function getShell($commandName)
/**
* Get Shell instance for the given command
*
- * @param mixed $commandName The command to get options for.
+ * @param string|null $commandName The command to get options for.
* @return array
*/
- public function options($commandName)
+ public function options(?string $commandName): array
{
- $Shell = $this->getShell($commandName);
- if (!$Shell) {
+ $shell = $this->getShell($commandName);
+ if (!$shell) {
$parser = new ConsoleOptionParser();
} else {
- $parser = $Shell->getOptionParser();
+ $parser = $shell->getOptionParser();
}
$options = [];
diff --git a/src/Console/Command/Task/ExtractTask.php b/src/Console/Command/Task/ExtractTask.php
index 966e93bdb5..bc4d7694a2 100644
--- a/src/Console/Command/Task/ExtractTask.php
+++ b/src/Console/Command/Task/ExtractTask.php
@@ -41,93 +41,93 @@ class ExtractTask extends AppShell
/**
* Paths to use when looking for strings
*
- * @var string
+ * @var array
*/
- protected $_paths = [];
+ protected array $_paths = [];
/**
* Files from where to extract
*
* @var array
*/
- protected $_files = [];
+ protected array $_files = [];
/**
* Merge all domain and category strings into the default.pot file
*
* @var bool
*/
- protected $_merge = false;
+ protected bool $_merge = false;
/**
* Current file being processed
*
- * @var string
+ * @var string|null
*/
- protected $_file = null;
+ protected ?string $_file = null;
/**
* Contains all content waiting to be write
*
- * @var string
+ * @var array
*/
- protected $_storage = [];
+ protected array $_storage = [];
/**
* Extracted tokens
*
* @var array
*/
- protected $_tokens = [];
+ protected array $_tokens = [];
/**
* Extracted strings indexed by category, domain, msgid and context.
*
* @var array
*/
- protected $_translations = [];
+ protected array $_translations = [];
/**
* Destination path
*
- * @var string
+ * @var string|null
*/
- protected $_output = null;
+ protected ?string $_output = null;
/**
* An array of directories to exclude.
*
* @var array
*/
- protected $_exclude = [];
+ protected array $_exclude = [];
/**
* Holds whether this call should extract model validation messages
*
* @var bool
*/
- protected $_extractValidation = true;
+ protected bool $_extractValidation = true;
/**
* Holds the validation string domain to use for validation messages when extracting
*
- * @var bool
+ * @var string
*/
- protected $_validationDomain = 'default';
+ protected string $_validationDomain = 'default';
/**
* Holds whether this call should extract the CakePHP Lib messages
*
* @var bool
*/
- protected $_extractCore = false;
+ protected bool $_extractCore = false;
/**
* Method to interact with the User and get path selections.
*
* @return void
*/
- protected function _getPaths()
+ protected function _getPaths(): void
{
$defaultPath = APP;
while (true) {
@@ -165,7 +165,7 @@ protected function _getPaths()
*
* @return void
*/
- public function execute()
+ public function execute(): void
{
if (!empty($this->params['exclude'])) {
$this->_exclude = explode(',', str_replace('/', DS, $this->params['exclude']));
@@ -269,8 +269,12 @@ public function execute()
* @param array $details The file and line references
* @return void
*/
- protected function _addTranslation($category, $domain, $msgid, $details = [])
- {
+ protected function _addTranslation(
+ string $category,
+ string $domain,
+ string $msgid,
+ array $details = [],
+ ): void {
$context = '';
if (isset($details['msgctxt'])) {
$context = $details['msgctxt'];
@@ -299,7 +303,7 @@ protected function _addTranslation($category, $domain, $msgid, $details = [])
*
* @return void
*/
- protected function _extract()
+ protected function _extract(): void
{
$this->out();
$this->out();
@@ -359,12 +363,12 @@ public function getOptionParser(): ConsoleOptionParser
'default' => false,
'help' => __d('cake_console', 'Ignores validation messages in the $validate property.' .
' If this flag is not set and the command is run from the same app directory,' .
- ' all messages in model validation rules will be extracted as tokens.',),
+ ' all messages in model validation rules will be extracted as tokens.'),
])->addOption('validation-domain', [
'help' => __d('cake_console', 'If set to a value, the localization domain to be used for model validation messages.'),
])->addOption('exclude', [
'help' => __d('cake_console', 'Comma separated list of directories to exclude.' .
- ' Any path containing a path segment with the provided values will be skipped. E.g. test,vendors',),
+ ' Any path containing a path segment with the provided values will be skipped. E.g. test,vendors'),
])->addOption('overwrite', [
'boolean' => true,
'default' => false,
@@ -382,7 +386,7 @@ public function getOptionParser(): ConsoleOptionParser
*
* @return void
*/
- protected function _extractTokens()
+ protected function _extractTokens(): void
{
foreach ($this->_files as $file) {
$this->_file = $file;
@@ -423,7 +427,7 @@ protected function _extractTokens()
* @param array $map Array containing what variables it will find (e.g: category, domain, singular, plural)
* @return void
*/
- protected function _parse($functionName, $map)
+ protected function _parse(string $functionName, array $map): void
{
$count = 0;
$categories = ['LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME', 'LC_MESSAGES'];
@@ -455,12 +459,15 @@ protected function _parse($functionName, $map)
$strings = $this->_getStrings($position, $mapCount);
if ($mapCount === count($strings)) {
- extract(array_combine($map, $strings));
- $category ??= 6;
+ $extract = array_combine($map, $strings);
+ $category = $extract['category'] ?? 6;
+ $singular = $extract['singular'] ?? '';
+ $domain = $extract['domain'] ?? 'default';
+ $context = $extract['context'] ?? null;
+ $plural = $extract['plural'] ?? null;
+
$category = (int)$category;
$categoryName = $categories[$category];
-
- $domain ??= 'default';
$details = [
'file' => $this->_file,
'line' => $line,
@@ -489,7 +496,7 @@ protected function _parse($functionName, $map)
*
* @return void
*/
- protected function _extractValidationMessages()
+ protected function _extractValidationMessages(): void
{
if (!$this->_extractValidation) {
return;
@@ -507,10 +514,10 @@ protected function _extractValidationMessages()
/**
* Extract validation messages from application or plugin models
*
- * @param string $plugin Plugin name or `null` to process application models
+ * @param string|null $plugin Plugin name or `null` to process application models
* @return void
*/
- protected function _extractPluginValidationMessages($plugin = null)
+ protected function _extractPluginValidationMessages(?string $plugin = null): void
{
// Load AppModel (namespace-aware)
App::className('AppModel', 'Model');
@@ -574,14 +581,19 @@ protected function _extractPluginValidationMessages($plugin = null)
* to the translation map
*
* @param string $field the name of the field that is being processed
- * @param array $rules the set of validation rules for the field
+ * @param array|null $rules the set of validation rules for the field
* @param string $file the file name where this validation rule was found
* @param string $domain default domain to bind the validations to
* @param string $category the translation category
* @return void
*/
- protected function _processValidationRules($field, $rules, $file, $domain, $category = 'LC_MESSAGES')
- {
+ protected function _processValidationRules(
+ string $field,
+ ?array $rules,
+ string $file,
+ string $domain,
+ string $category = 'LC_MESSAGES',
+ ): void {
if (!is_array($rules)) {
return;
}
@@ -618,7 +630,7 @@ protected function _processValidationRules($field, $rules, $file, $domain, $cate
*
* @return void
*/
- protected function _buildFiles()
+ protected function _buildFiles(): void
{
$paths = $this->_paths;
$paths[] = realpath(APP) . DS;
@@ -646,11 +658,10 @@ protected function _buildFiles()
if ($context) {
$sentence .= "msgctxt \"{$context}\"\n";
}
+ $sentence .= "msgid \"{$msgid}\"\n";
if ($plural === false) {
- $sentence .= "msgid \"{$msgid}\"\n";
$sentence .= "msgstr \"\"\n\n";
} else {
- $sentence .= "msgid \"{$msgid}\"\n";
$sentence .= "msgid_plural \"{$plural}\"\n";
$sentence .= "msgstr[0] \"\"\n";
$sentence .= "msgstr[1] \"\"\n\n";
@@ -675,8 +686,12 @@ protected function _buildFiles()
* @param string $sentence The sentence to store.
* @return void
*/
- protected function _store($category, $domain, $header, $sentence)
- {
+ protected function _store(
+ string $category,
+ string $domain,
+ string $header,
+ string $sentence,
+ ): void {
if (!isset($this->_storage[$category])) {
$this->_storage[$category] = [];
}
@@ -695,7 +710,7 @@ protected function _store($category, $domain, $header, $sentence)
*
* @return void
*/
- protected function _writeFiles()
+ protected function _writeFiles(): void
{
$overwriteAll = false;
if (!empty($this->params['overwrite'])) {
@@ -745,7 +760,7 @@ protected function _writeFiles()
*
* @return string Translation template header
*/
- protected function _writeHeader()
+ protected function _writeHeader(): string
{
$output = "# LANGUAGE translation of CakePHP Application\n";
$output .= "# Copyright YEAR NAME \n";
@@ -772,10 +787,10 @@ protected function _writeHeader()
* @param int $target Number of strings to extract
* @return array Strings extracted
*/
- protected function _getStrings(&$position, $target)
+ protected function _getStrings(int &$position, int $target): array
{
$strings = [];
- $count = count($strings);
+ $count = 0;
while ($count < $target && ($this->_tokens[$position] === ',' || $this->_tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING || $this->_tokens[$position][0] == T_LNUMBER)) {
$count = count($strings);
if ($this->_tokens[$position][0] == T_CONSTANT_ENCAPSED_STRING && $this->_tokens[$position + 1] === '.') {
@@ -804,7 +819,7 @@ protected function _getStrings(&$position, $target)
* @param string $string String to format
* @return string Formatted string
*/
- protected function _formatString($string)
+ protected function _formatString(string $string): string
{
$quote = substr($string, 0, 1);
$string = substr($string, 1, -1);
@@ -827,8 +842,12 @@ protected function _formatString($string)
* @param int $count Count
* @return void
*/
- protected function _markerError($file, $line, $marker, $count)
- {
+ protected function _markerError(
+ string $file,
+ int $line,
+ string $marker,
+ int $count,
+ ): void {
$this->err(__d('cake_console', "Invalid marker content in %s:%s\n* %s(", $file, $line, $marker));
$count += 2;
$tokenCount = count($this->_tokens);
@@ -836,9 +855,9 @@ protected function _markerError($file, $line, $marker, $count)
while (($tokenCount - $count > 0) && $parenthesis) {
if (is_array($this->_tokens[$count])) {
- $this->err($this->_tokens[$count][1], false);
+ $this->err($this->_tokens[$count][1], 0);
} else {
- $this->err($this->_tokens[$count], false);
+ $this->err($this->_tokens[$count], 0);
if ($this->_tokens[$count] === '(') {
$parenthesis++;
}
@@ -849,7 +868,7 @@ protected function _markerError($file, $line, $marker, $count)
}
$count++;
}
- $this->err("\n", true);
+ $this->err("\n", 1);
}
/**
@@ -863,7 +882,7 @@ protected function _searchFiles(): void
if (!empty($this->_exclude)) {
$exclude = [];
foreach ($this->_exclude as $e) {
- if (DS !== '\\' && $e[0] !== DS) {
+ if (DIRECTORY_SEPARATOR !== '\\' && $e[0] !== DIRECTORY_SEPARATOR) {
$e = DS . $e;
}
$exclude[] = preg_quote($e, '/');
@@ -889,7 +908,7 @@ protected function _searchFiles(): void
*
* @return bool
*/
- protected function _isExtractingApp()
+ protected function _isExtractingApp(): bool
{
return $this->_paths === [APP];
}
@@ -900,7 +919,7 @@ protected function _isExtractingApp()
* @param string $path Path to folder
* @return bool true if it exists and is writable, false otherwise
*/
- protected function _isPathUsable($path)
+ protected function _isPathUsable(string $path): bool
{
return is_dir($path) && is_writable($path);
}
diff --git a/src/Console/Command/TestShell.php b/src/Console/Command/TestShell.php
index c0c73bc76c..cfe3c9b27e 100644
--- a/src/Console/Command/TestShell.php
+++ b/src/Console/Command/TestShell.php
@@ -38,9 +38,9 @@ class TestShell extends Shell
/**
* Dispatcher object for the run.
*
- * @var CakeTestSuiteDispatcher
+ * @var CakeTestSuiteDispatcher|null
*/
- protected $_dispatcher = null;
+ protected ?CakeTestSuiteDispatcher $_dispatcher = null;
/**
* Gets the option parser instance and configures it.
@@ -178,12 +178,12 @@ public function getOptionParser(): ConsoleOptionParser
* @return void
* @throws Exception
*/
- public function initialize()
+ public function initialize(): void
{
$this->_dispatcher = new CakeTestSuiteDispatcher();
$success = $this->_dispatcher->loadTestFramework();
if (!$success) {
- throw new Exception(__d('cake_dev', 'Please install PHPUnit framework v3.7 (http://www.phpunit.de)'));
+ throw new Exception(__d('cake_dev', 'Please install PHPUnit framework v3.7 (https://www.phpunit.de)'));
}
}
@@ -230,7 +230,7 @@ protected function _parseArgs(): ?array
*
* @return array Array of params for CakeTestDispatcher
*/
- protected function _runnerOptions()
+ protected function _runnerOptions(): array
{
$options = [];
$params = $this->params;
@@ -288,7 +288,7 @@ public function main(): void
* @param array $options list of options as constructed by _runnerOptions()
* @return void
*/
- protected function _run($runnerArgs, $options = [])
+ protected function _run(array $runnerArgs, array $options = []): void
{
restore_error_handler();
restore_error_handler();
@@ -302,7 +302,7 @@ protected function _run($runnerArgs, $options = [])
*
* @return void
*/
- public function available()
+ public function available(): void
{
$params = $this->_parseArgs();
$testCases = CakeTestLoader::generateTestList($params);
@@ -361,13 +361,16 @@ public function available()
* Find the test case for the passed file. The file could itself be a test.
*
* @param string $file The file to map.
- * @param string $category The test file category.
+ * @param string|null $category The test file category.
* @param bool $throwOnMissingFile Whether or not to throw an exception.
- * @return array array(type, case)
+ * @return string|false|null array(type, case)
* @throws Exception
*/
- protected function _mapFileToCase($file, $category, $throwOnMissingFile = true)
- {
+ protected function _mapFileToCase(
+ string $file,
+ ?string $category,
+ bool $throwOnMissingFile = true,
+ ): string|false|null {
if (!$category || (!str_ends_with($file, '.php'))) {
return false;
}
@@ -377,7 +380,7 @@ protected function _mapFileToCase($file, $category, $throwOnMissingFile = true)
$file = $_file;
}
- $testFile = $testCase = null;
+ $testFile = null;
// File path
if (preg_match('@(Test|tests|tests_legacy)[\\\/]@', $file)) {
@@ -435,9 +438,8 @@ protected function _mapFileToCase($file, $category, $throwOnMissingFile = true)
$testCase = substr($testFile, 0, -8);
$testCase = str_replace(DS, '/', $testCase);
- $testCase = preg_replace('@.*(?:Test/Case|tests/TestCase)/@', '', $testCase);
- return $testCase;
+ return preg_replace('@.*(?:Test/Case|tests/TestCase)/@', '', $testCase);
}
/**
@@ -446,7 +448,7 @@ protected function _mapFileToCase($file, $category, $throwOnMissingFile = true)
* @param string $file The file to map.
* @return string
*/
- protected function _mapFileToCategory($file)
+ protected function _mapFileToCategory(string $file): string
{
$_file = realpath($file);
if ($_file) {
diff --git a/src/Console/ConsoleErrorHandler.php b/src/Console/ConsoleErrorHandler.php
index e98444703b..6be5f3f253 100644
--- a/src/Console/ConsoleErrorHandler.php
+++ b/src/Console/ConsoleErrorHandler.php
@@ -20,6 +20,8 @@
use Cake\Core\Configure;
use Cake\Error\ErrorHandler;
use Cake\Log\CakeLog;
+use Exception;
+use ParseError;
/**
* Error Handler for Cake console. Does simple printing of the
@@ -32,9 +34,9 @@ class ConsoleErrorHandler
/**
* Standard error stream.
*
- * @var ConsoleOutput
+ * @var ConsoleOutput|null
*/
- public static $stderr;
+ public static ?ConsoleOutput $stderr = null;
/**
* Get the stderr object for the console error handling.
@@ -53,7 +55,7 @@ public static function getStderr()
/**
* Handle an exception in the console environment. Prints a message to stderr.
*
- * @param Exception|ParserError $exception The exception to handle
+ * @param Exception|ParseError $exception The exception to handle
* @return void
*/
public function handleException($exception)
@@ -106,7 +108,7 @@ public function handleError($code, $description, $file = null, $line = null, $co
* @param int $code The exit code.
* @return void
*/
- protected function _stop($code = 0)
+ protected function _stop(string|int $code = 0): void
{
exit($code);
}
diff --git a/src/Console/ConsoleInput.php b/src/Console/ConsoleInput.php
index 42cf6ee58e..bdd73bef00 100644
--- a/src/Console/ConsoleInput.php
+++ b/src/Console/ConsoleInput.php
@@ -41,14 +41,14 @@ class ConsoleInput
*
* @var bool
*/
- protected $_canReadline;
+ protected bool $_canReadline;
/**
* Constructor
*
* @param string $handle The location of the stream to use as input.
*/
- public function __construct($handle = 'php://stdin')
+ public function __construct(string $handle = 'php://stdin')
{
$this->_canReadline = extension_loaded('readline') && $handle === 'php://stdin' ? true : false;
$this->_input = fopen($handle, 'r');
@@ -57,9 +57,9 @@ public function __construct($handle = 'php://stdin')
/**
* Read a value from the stream
*
- * @return mixed The value of the stream
+ * @return string|false The value of the stream
*/
- public function read()
+ public function read(): string|false
{
if ($this->_canReadline) {
$line = readline('');
@@ -79,7 +79,7 @@ public function read()
* @param int $timeout An optional time to wait for data
* @return bool True for data available, false otherwise
*/
- public function dataAvailable($timeout = 0)
+ public function dataAvailable(int $timeout = 0): bool
{
$readFds = [$this->_input];
$writeFds = [];
diff --git a/src/Console/ConsoleInputArgument.php b/src/Console/ConsoleInputArgument.php
index 2cbd223a76..339bac6bec 100644
--- a/src/Console/ConsoleInputArgument.php
+++ b/src/Console/ConsoleInputArgument.php
@@ -34,39 +34,43 @@ class ConsoleInputArgument
*
* @var string
*/
- protected $_name;
+ protected string $_name;
/**
* Help string
*
* @var string
*/
- protected $_help;
+ protected string $_help;
/**
* Is this option required?
*
* @var bool
*/
- protected $_required;
+ protected bool $_required;
/**
* An array of valid choices for this argument.
*
* @var array
*/
- protected $_choices;
+ protected array $_choices;
/**
* Make a new Input Argument
*
- * @param array|string $name The long name of the option, or an array with all the properties.
+ * @param array{name: string}|string $name The long name of the option, or an array with all the properties.
* @param string $help The help text for this option
* @param bool $required Whether this argument is required. Missing required args will trigger exceptions
* @param array $choices Valid choices for this option.
*/
- public function __construct($name, $help = '', $required = false, $choices = [])
- {
+ public function __construct(
+ array|string $name,
+ string $help = '',
+ bool $required = false,
+ array $choices = [],
+ ) {
if (is_array($name) && isset($name['name'])) {
foreach ($name as $key => $value) {
$this->{'_' . $key} = $value;
@@ -82,9 +86,9 @@ public function __construct($name, $help = '', $required = false, $choices = [])
/**
* Get the value of the name attribute.
*
- * @return string Value of this->_name.
+ * @return array|string Value of this->_name.
*/
- public function name()
+ public function name(): array|string
{
return $this->_name;
}
@@ -95,7 +99,7 @@ public function name()
* @param int $width The width to make the name of the option.
* @return string
*/
- public function help($width = 0)
+ public function help(int $width = 0): string
{
$name = $this->_name;
if (strlen($name) < $width) {
@@ -148,11 +152,12 @@ public function isRequired()
* @return bool
* @throws ConsoleException
*/
- public function validChoice($value)
+ public function validChoice(string $value): bool
{
if (empty($this->_choices)) {
return true;
}
+
if (!in_array($value, $this->_choices)) {
throw new ConsoleException(
__d(
@@ -174,12 +179,12 @@ public function validChoice($value)
* @param SimpleXmlElement $parent The parent element.
* @return SimpleXmlElement The parent with this argument appended.
*/
- public function xml(SimpleXmlElement $parent)
+ public function xml(SimpleXmlElement $parent): SimpleXmlElement
{
$option = $parent->addChild('argument');
$option->addAttribute('name', $this->_name);
$option->addAttribute('help', $this->_help);
- $option->addAttribute('required', (int)$this->isRequired());
+ $option->addAttribute('required', (string)(int)$this->isRequired());
$choices = $option->addChild('choices');
foreach ($this->_choices as $valid) {
$choices->addChild('choice', $valid);
diff --git a/src/Console/ConsoleInputOption.php b/src/Console/ConsoleInputOption.php
index 05d4bc2965..ec65e0f09a 100644
--- a/src/Console/ConsoleInputOption.php
+++ b/src/Console/ConsoleInputOption.php
@@ -34,56 +34,62 @@ class ConsoleInputOption
*
* @var string
*/
- protected $_name;
+ protected string $_name;
/**
* Short (1 character) alias for the option.
*
- * @var string
+ * @var string|null
*/
- protected $_short;
+ protected ?string $_short = null;
/**
* Help text for the option.
*
* @var string
*/
- protected $_help;
+ protected string $_help;
/**
* Is the option a boolean option. Boolean options do not consume a parameter.
*
* @var bool
*/
- protected $_boolean;
+ protected bool $_boolean;
/**
* Default value for the option
*
* @var mixed
*/
- protected $_default;
+ protected mixed $_default;
/**
* An array of choices for the option.
*
* @var array
*/
- protected $_choices;
+ protected array $_choices;
/**
* Make a new Input Option
*
- * @param array|string $name The long name of the option, or an array with all the properties.
- * @param string $short The short alias for this option
+ * @param array{name: string}|string $name The long name of the option, or an array with all the properties.
+ * @param string|null $short The short alias for this option
* @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 mixed $default The default value for this option.
* @param array $choices Valid choices for this option.
* @throws ConsoleException
*/
- public function __construct($name, $short = null, $help = '', $boolean = false, $default = '', $choices = [])
- {
+ public function __construct(
+ array|string $name,
+ ?string $short = null,
+ string $help = '',
+ bool $boolean = false,
+ mixed $default = '',
+ array $choices = [],
+ ) {
if (is_array($name) && isset($name['name'])) {
foreach ($name as $key => $value) {
$this->{'_' . $key} = $value;
@@ -96,6 +102,7 @@ public function __construct($name, $short = null, $help = '', $boolean = false,
$this->_default = $default;
$this->_choices = $choices;
}
+
if (strlen($this->_short) > 1) {
throw new ConsoleException(
__d('cake_console', 'Short option "%s" is invalid, short options must be one letter.', $this->_short),
@@ -106,9 +113,9 @@ public function __construct($name, $short = null, $help = '', $boolean = false,
/**
* Get the value of the name attribute.
*
- * @return string Value of this->_name.
+ * @return array|string Value of this->_name.
*/
- public function name()
+ public function name(): array|string
{
return $this->_name;
}
@@ -116,9 +123,9 @@ public function name()
/**
* Get the value of the short attribute.
*
- * @return string Value of this->_short.
+ * @return string|null Value of this->_short.
*/
- public function short()
+ public function short(): ?string
{
return $this->_short;
}
@@ -129,7 +136,7 @@ public function short()
* @param int $width The width to make the name of the option.
* @return string
*/
- public function help($width = 0)
+ public function help(int $width = 0): string
{
$default = $short = '';
if (!empty($this->_default) && $this->_default !== true) {
@@ -173,7 +180,7 @@ public function usage()
*
* @return mixed
*/
- public function defaultValue()
+ public function defaultValue(): mixed
{
return $this->_default;
}
@@ -183,7 +190,7 @@ public function defaultValue()
*
* @return bool
*/
- public function isBoolean()
+ public function isBoolean(): bool
{
return (bool)$this->_boolean;
}
@@ -195,7 +202,7 @@ public function isBoolean()
* @return bool
* @throws ConsoleException
*/
- public function validChoice($value)
+ public function validChoice(string $value): bool
{
if (empty($this->_choices)) {
return true;
@@ -221,7 +228,7 @@ public function validChoice($value)
* @param SimpleXmlElement $parent The parent element.
* @return SimpleXmlElement The parent with this option appended.
*/
- public function xml(SimpleXmlElement $parent)
+ public function xml(SimpleXmlElement $parent): SimpleXmlElement
{
$option = $parent->addChild('option');
$option->addAttribute('name', '--' . $this->_name);
@@ -231,7 +238,7 @@ public function xml(SimpleXmlElement $parent)
}
$option->addAttribute('short', $short);
$option->addAttribute('help', $this->_help);
- $option->addAttribute('boolean', (int)$this->_boolean);
+ $option->addAttribute('boolean', (string)(int)$this->_boolean);
$option->addChild('default', $this->_default);
$choices = $option->addChild('choices');
foreach ($this->_choices as $valid) {
diff --git a/src/Console/ConsoleInputSubcommand.php b/src/Console/ConsoleInputSubcommand.php
index eeff8c6619..d7e03d6dab 100644
--- a/src/Console/ConsoleInputSubcommand.php
+++ b/src/Console/ConsoleInputSubcommand.php
@@ -33,32 +33,35 @@ class ConsoleInputSubcommand
*
* @var string
*/
- protected $_name;
+ protected string $_name;
/**
* Help string for the subcommand
*
* @var string
*/
- protected $_help;
+ protected string $_help;
/**
* The ConsoleOptionParser for this subcommand.
*
- * @var ConsoleOptionParser
+ * @var ConsoleOptionParser|array|null
*/
- protected $_parser;
+ protected ConsoleOptionParser|array|null $_parser = null;
/**
* Make a new Subcommand
*
- * @param array|string $name The long name of the subcommand, or an array with all the properties.
+ * @param array{name: string}|string $name The long name of the subcommand, or an array with all the properties.
* @param string $help The help text for this option
- * @param ConsoleOptionParser|array $parser A parser for this subcommand. Either a ConsoleOptionParser, or an array that can be
+ * @param ConsoleOptionParser|array|null $parser A parser for this subcommand. Either a ConsoleOptionParser, or an array that can be
* used with ConsoleOptionParser::buildFromArray()
*/
- public function __construct($name, $help = '', $parser = null)
- {
+ public function __construct(
+ array|string $name,
+ string $help = '',
+ ConsoleOptionParser|array|null $parser = null,
+ ) {
if (is_array($name) && isset($name['name'])) {
foreach ($name as $key => $value) {
$this->{'_' . $key} = $value;
@@ -68,6 +71,7 @@ public function __construct($name, $help = '', $parser = null)
$this->_help = $help;
$this->_parser = $parser;
}
+
if (is_array($this->_parser)) {
$this->_parser['command'] = $this->_name;
$this->_parser = ConsoleOptionParser::buildFromArray($this->_parser);
@@ -77,9 +81,9 @@ public function __construct($name, $help = '', $parser = null)
/**
* Get the value of the name attribute.
*
- * @return string Value of this->_name.
+ * @return array|string Value of this->_name.
*/
- public function name()
+ public function name(): array|string
{
return $this->_name;
}
@@ -90,7 +94,7 @@ public function name()
* @param int $width The width to make the name of the subcommand.
* @return string
*/
- public function help($width = 0)
+ public function help(int $width = 0): string
{
$name = $this->_name;
if (strlen($name) < $width) {
@@ -103,9 +107,9 @@ public function help($width = 0)
/**
* Get the usage value for this option
*
- * @return mixed Either false or a ConsoleOptionParser
+ * @return ConsoleOptionParser|false Either false or a ConsoleOptionParser
*/
- public function parser()
+ public function parser(): ConsoleOptionParser|false
{
if ($this->_parser instanceof ConsoleOptionParser) {
return $this->_parser;
@@ -120,7 +124,7 @@ public function parser()
* @param SimpleXmlElement $parent The parent element.
* @return SimpleXmlElement The parent with this subcommand appended.
*/
- public function xml(SimpleXmlElement $parent)
+ public function xml(SimpleXmlElement $parent): SimpleXmlElement
{
$command = $parent->addChild('command');
$command->addAttribute('name', $this->_name);
diff --git a/src/Console/ConsoleOptionParser.php b/src/Console/ConsoleOptionParser.php
index 92882bcf3e..9efb29114e 100644
--- a/src/Console/ConsoleOptionParser.php
+++ b/src/Console/ConsoleOptionParser.php
@@ -136,12 +136,14 @@ class ConsoleOptionParser
/**
* Construct an OptionParser so you can define its behavior
*
- * @param string $command The command name this parser is for. The command name is used for generating help.
+ * @param string|null $command The command name this parser is for. The command name is used for generating help.
* @param bool $defaultOptions Whether you want the verbose and quiet options set. Setting
* this to false will prevent the addition of `--verbose` & `--quiet` options.
*/
- public function __construct($command = null, $defaultOptions = true)
- {
+ public function __construct(
+ ?string $command = null,
+ bool $defaultOptions = true,
+ ) {
$this->command($command);
$this->addOption('help', [
@@ -298,10 +300,12 @@ public function epilog(array|string|null $text = null)
* @param ConsoleInputOption|string $name The long name you want to the value to be parsed out as when options are parsed.
* Will also accept an instance of ConsoleInputOption
* @param array $options An array of parameters that define the behavior of the option
- * @return self
+ * @return static
*/
- public function addOption(ConsoleInputOption|string $name, array $options = [])
- {
+ public function addOption(
+ ConsoleInputOption|string $name,
+ array $options = [],
+ ): static {
if ($name instanceof ConsoleInputOption) {
$option = $name;
$name = $option->name();
@@ -340,11 +344,13 @@ public function addOption(ConsoleInputOption|string $name, array $options = [])
*
* @param ConsoleInputArgument|string $name The name of the argument. Will also accept an instance of ConsoleInputArgument
* @param array $params Parameters for the argument, see above.
- * @return self
+ * @return static
*/
- public function addArgument($name, $params = [])
- {
- if (is_object($name) && $name instanceof ConsoleInputArgument) {
+ public function addArgument(
+ ConsoleInputArgument|string $name,
+ array $params = [],
+ ): static {
+ if ($name instanceof ConsoleInputArgument) {
$arg = $name;
$index = count($this->_args);
} else {
@@ -372,9 +378,9 @@ public function addArgument($name, $params = [])
*
* @param array $args Array of arguments to add.
* @see ConsoleOptionParser::addArgument()
- * @return self
+ * @return static
*/
- public function addArguments(array $args)
+ public function addArguments(array $args): static
{
foreach ($args as $name => $params) {
$this->addArgument($name, $params);
@@ -389,9 +395,9 @@ public function addArguments(array $args)
*
* @param array $options Array of options to add.
* @see ConsoleOptionParser::addOption()
- * @return self
+ * @return static
*/
- public function addOptions(array $options)
+ public function addOptions(array $options): static
{
foreach ($options as $name => $params) {
$this->addOption($name, $params);
@@ -413,11 +419,13 @@ public function addOptions(array $options)
*
* @param ConsoleInputSubcommand|string $name Name of the subcommand. Will also accept an instance of ConsoleInputSubcommand
* @param array $options Array of params, see above.
- * @return self
+ * @return static
*/
- public function addSubcommand($name, $options = [])
- {
- if (is_object($name) && $name instanceof ConsoleInputSubcommand) {
+ public function addSubcommand(
+ ConsoleInputSubcommand|string $name,
+ array $options = [],
+ ): static {
+ if ($name instanceof ConsoleInputSubcommand) {
$command = $name;
$name = $command->name();
} else {
@@ -438,9 +446,9 @@ public function addSubcommand($name, $options = [])
* Remove a subcommand from the option parser.
*
* @param string $name The subcommand name to remove.
- * @return self
+ * @return static
*/
- public function removeSubcommand($name)
+ public function removeSubcommand(string $name): static
{
unset($this->_subcommands[$name]);
@@ -451,9 +459,9 @@ public function removeSubcommand($name)
* Add multiple subcommands at once.
*
* @param array $commands Array of subcommands.
- * @return self
+ * @return static
*/
- public function addSubcommands(array $commands)
+ public function addSubcommands(array $commands): static
{
foreach ($commands as $name => $params) {
$this->addSubcommand($name, $params);
@@ -467,7 +475,7 @@ public function addSubcommands(array $commands)
*
* @return array Array of argument descriptions
*/
- public function arguments()
+ public function arguments(): array
{
return $this->_args;
}
@@ -477,7 +485,7 @@ public function arguments()
*
* @return array
*/
- public function options()
+ public function options(): array
{
return $this->_options;
}
@@ -487,7 +495,7 @@ public function options()
*
* @return array
*/
- public function subcommands()
+ public function subcommands(): array
{
return $this->_subcommands;
}
@@ -503,8 +511,10 @@ public function subcommands()
* @return array{array, array} array($params, $args)
* @throws ConsoleException When an invalid parameter is encountered.
*/
- public function parse(array $argv, ?string $command = null): array
- {
+ public function parse(
+ array $argv,
+ ?string $command = null,
+ ): array {
if (isset($this->_subcommands[$command]) && $this->_subcommands[$command]->parser()) {
return $this->_subcommands[$command]->parser()->parse($argv);
}
@@ -553,8 +563,11 @@ public function parse(array $argv, ?string $command = null): array
* @param int $width The width to format user content to. Defaults to 72
* @return string Generated help.
*/
- public function help($subcommand = null, string|bool $format = 'text', int $width = 72): string
- {
+ public function help(
+ ?string $subcommand = null,
+ string|bool $format = 'text',
+ int $width = 72,
+ ): string {
if (
$subcommand &&
isset($this->_subcommands[$subcommand]) &&
@@ -584,8 +597,10 @@ public function help($subcommand = null, string|bool $format = 'text', int $widt
* @param array $params The params to append the parsed value into
* @return array Params with $option added in.
*/
- protected function _parseLongOption($option, $params)
- {
+ protected function _parseLongOption(
+ string $option,
+ array $params,
+ ): array {
$name = substr($option, 2);
if (str_contains($name, '=')) {
[$name, $value] = explode('=', $name, 2);
@@ -605,8 +620,10 @@ protected function _parseLongOption($option, $params)
* @return array Params with $option added in.
* @throws ConsoleException When unknown short options are encountered.
*/
- protected function _parseShortOption($option, $params)
- {
+ protected function _parseShortOption(
+ string $option,
+ array $params,
+ ): array {
$key = substr($option, 1);
if (strlen($key) > 1) {
$flags = str_split($key);
@@ -631,8 +648,10 @@ protected function _parseShortOption($option, $params)
* @return array Params with $option added in.
* @throws ConsoleException
*/
- protected function _parseOption($name, $params)
- {
+ protected function _parseOption(
+ string $name,
+ array $params,
+ ): array {
if (!isset($this->_options[$name])) {
throw new ConsoleException(__d('cake_console', 'Unknown option `%s`', $name));
}
@@ -663,7 +682,7 @@ protected function _parseOption($name, $params)
* @param string $name The name of the option.
* @return bool
*/
- protected function _optionExists($name)
+ protected function _optionExists(string $name): bool
{
if (str_starts_with($name, '--')) {
return isset($this->_options[substr($name, 2)]);
@@ -684,8 +703,10 @@ protected function _optionExists($name)
* @return array Args
* @throws ConsoleException
*/
- protected function _parseArg(string $argument, array $args): array
- {
+ protected function _parseArg(
+ string $argument,
+ array $args,
+ ): array {
if (empty($this->_args)) {
$args[] = $argument;
@@ -709,7 +730,7 @@ protected function _parseArg(string $argument, array $args): array
*
* @return string next token or ''
*/
- protected function _nextToken()
+ protected function _nextToken(): string
{
return $this->_tokens[0] ?? '';
}
diff --git a/src/Console/ConsoleOutput.php b/src/Console/ConsoleOutput.php
index 0065fc592d..7082be4fa6 100644
--- a/src/Console/ConsoleOutput.php
+++ b/src/Console/ConsoleOutput.php
@@ -87,21 +87,21 @@ class ConsoleOutput
*
* @var int
*/
- protected $_lastWritten = 0;
+ protected int $_lastWritten = 0;
/**
* The current output type. Manipulated with ConsoleOutput::outputAs();
*
* @var int
*/
- protected $_outputAs = self::COLOR;
+ protected int $_outputAs = self::COLOR;
/**
* text colors used in colored output.
*
- * @var array
+ * @var array
*/
- protected static $_foregroundColors = [
+ protected static array $_foregroundColors = [
'black' => 30,
'red' => 31,
'green' => 32,
@@ -115,9 +115,9 @@ class ConsoleOutput
/**
* background colors used in colored output.
*
- * @var array
+ * @var array
*/
- protected static $_backgroundColors = [
+ protected static array $_backgroundColors = [
'black' => 40,
'red' => 41,
'green' => 42,
@@ -131,9 +131,9 @@ class ConsoleOutput
/**
* formatting options for colored output
*
- * @var string
+ * @var array
*/
- protected static $_options = [
+ protected static array $_options = [
'bold' => 1,
'underline' => 4,
'blink' => 5,
@@ -144,9 +144,9 @@ class ConsoleOutput
* Styles that are available as tags in console output.
* You can modify these styles with ConsoleOutput::styles()
*
- * @var array
+ * @var array
*/
- protected static $_styles = [
+ protected static array $_styles = [
'emergency' => ['text' => 'red', 'underline' => true],
'alert' => ['text' => 'red', 'underline' => true],
'critical' => ['text' => 'red', 'underline' => true],
@@ -168,14 +168,14 @@ class ConsoleOutput
*
* @param string $stream The identifier of the stream to write output to.
*/
- public function __construct($stream = 'php://stdout')
+ public function __construct(string $stream = 'php://stdout')
{
$this->_output = fopen($stream, 'w');
if (
- (DS === '\\' && !(bool)env('ANSICON') && env('ConEmuANSI') !== 'ON') ||
- $stream === 'php://output' ||
- (function_exists('posix_isatty') && !posix_isatty($this->_output))
+ (DIRECTORY_SEPARATOR === '\\' && !env('ANSICON') && env('ConEmuANSI') !== 'ON')
+ || $stream === 'php://output'
+ || (function_exists('posix_isatty') && !posix_isatty($this->_output))
) {
$this->_outputAs = static::PLAIN;
}
@@ -185,12 +185,14 @@ public function __construct($stream = 'php://stdout')
* Outputs a single or multiple messages to stdout. If no parameters
* are passed, outputs just a newline.
*
- * @param array|string $message A string or an array of strings to output
+ * @param array|string|null $message A string or an array of strings to output
* @param int $newlines Number of newlines to append
- * @return int Returns the number of bytes returned from writing to stdout.
+ * @return int|false|null Returns the number of bytes returned from writing to stdout.
*/
- public function write($message, $newlines = 1)
- {
+ public function write(
+ array|string|null $message,
+ int $newlines = 1,
+ ): int|false|null {
if (is_array($message)) {
$message = implode(static::LF, $message);
}
@@ -212,7 +214,7 @@ public function write($message, $newlines = 1)
* length of the last message output.
* @return void
*/
- public function overwrite($message, $newlines = 1, $size = null)
+ public function overwrite(array|string $message, int $newlines = 1, ?int $size = null): void
{
$size = $size ?: $this->_lastWritten;
// Output backspaces.
@@ -234,7 +236,7 @@ public function overwrite($message, $newlines = 1, $size = null)
* @param string $text Text with styling tags.
* @return string String with color codes added.
*/
- public function styleText($text)
+ public function styleText(string $text): string
{
if ($this->_outputAs == static::RAW) {
return $text;
@@ -258,7 +260,7 @@ public function styleText($text)
* @param array $matches An array of matches to replace.
* @return string
*/
- protected function _replaceTags($matches)
+ protected function _replaceTags(array $matches): string
{
$style = $this->styles($matches['tag']);
if (empty($style)) {
@@ -286,9 +288,9 @@ protected function _replaceTags($matches)
* Writes a message to the output stream.
*
* @param string $message Message to write.
- * @return bool success
+ * @return int|false success
*/
- protected function _write($message)
+ protected function _write(string $message): int|false
{
$this->_lastWritten = fwrite($this->_output, $message);
@@ -314,13 +316,13 @@ protected function _write($message)
*
* `$this->output->styles('annoy', false);`
*
- * @param string $style The style to get or create.
- * @param array $definition The array definition of the style to change or create a style
+ * @param string|null $style The style to get or create.
+ * @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
* styles true will be returned.
*/
- public function styles($style = null, $definition = null)
+ public function styles(?string $style = null, array|false|null $definition = null): mixed
{
if ($style === null && $definition === null) {
return static::$_styles;
@@ -341,15 +343,17 @@ public function styles($style = null, $definition = null)
/**
* Get/Set the output type to use. The output type how formatting tags are treated.
*
- * @param int $type The output type to use. Should be one of the class constants.
- * @return mixed Either null or the value if getting.
+ * @param int|null $type The output type to use. Should be one of the class constants.
+ * @return int|null Either null or the value if getting.
*/
- public function outputAs($type = null)
+ public function outputAs(?int $type = null): ?int
{
if ($type === null) {
return $this->_outputAs;
}
$this->_outputAs = $type;
+
+ return null;
}
/**
diff --git a/src/Console/Helper/BaseShellHelper.php b/src/Console/Helper/BaseShellHelper.php
index 47247a76ba..5c844008b2 100644
--- a/src/Console/Helper/BaseShellHelper.php
+++ b/src/Console/Helper/BaseShellHelper.php
@@ -24,28 +24,28 @@ abstract class BaseShellHelper
*
* @var array
*/
- protected $_defaultConfig = [];
+ protected array $_defaultConfig = [];
/**
* ConsoleOutput instance.
*
- * @var ConsoleOutput
+ * @var ConsoleOutput|null
*/
- protected $_consoleOutput;
+ protected ?ConsoleOutput $_consoleOutput = null;
/**
* Runtime config
*
* @var array
*/
- protected $_config = [];
+ protected array $_config = [];
/**
* Whether the config property has already been configured with defaults
*
* @var bool
*/
- protected $_configInitialized = false;
+ protected bool $_configInitialized = false;
/**
* Constructor.
@@ -62,10 +62,10 @@ public function __construct(ConsoleOutput $consoleOutput, array $config = [])
/**
* Initialize config & store config values
*
- * @param null $config Config values to set
- * @return array|void
+ * @param array|null $config Config values to set
+ * @return array|null
*/
- public function config($config = null)
+ public function config(?array $config = null): ?array
{
if ($config === null) {
return $this->_config;
@@ -76,6 +76,8 @@ public function config($config = null)
} else {
$this->_config = array_merge($this->_config, $config);
}
+
+ return null;
}
/**
@@ -84,5 +86,5 @@ public function config($config = null)
* @param array $args The arguments for the helper.
* @return void
*/
- abstract public function output($args);
+ abstract public function output(array $args): void;
}
diff --git a/src/Console/Helper/ProgressShellHelper.php b/src/Console/Helper/ProgressShellHelper.php
index d6c5eefe23..1497e4ae7a 100644
--- a/src/Console/Helper/ProgressShellHelper.php
+++ b/src/Console/Helper/ProgressShellHelper.php
@@ -27,21 +27,21 @@ class ProgressShellHelper extends BaseShellHelper
*
* @var int
*/
- protected $_progress = 0;
+ protected int $_progress = 0;
/**
* The total number of 'items' to progress through.
*
* @var int
*/
- protected $_total = 0;
+ protected int $_total = 0;
/**
* The width of the bar.
*
* @var int
*/
- protected $_width = 0;
+ protected int $_width = 0;
/**
* Output a progress bar.
@@ -57,7 +57,7 @@ class ProgressShellHelper extends BaseShellHelper
* @return void
* @throws RuntimeException
*/
- public function output($args)
+ public function output(array $args): void
{
$args += ['callback' => null];
if (isset($args[0])) {
@@ -85,7 +85,7 @@ public function output($args)
* @param array $args The initialization data.
* @return void
*/
- public function init(array $args = [])
+ public function init(array $args = []): void
{
$args += ['total' => 100, 'width' => 80];
$this->_progress = 0;
@@ -99,7 +99,7 @@ public function init(array $args = [])
* @param int $num The amount of progress to advance by.
* @return void
*/
- public function increment($num = 1)
+ public function increment(int $num = 1): void
{
$this->_progress = min(max(0, $this->_progress + $num), $this->_total);
}
@@ -109,7 +109,7 @@ public function increment($num = 1)
*
* @return void
*/
- public function draw()
+ public function draw(): void
{
$numberLen = strlen(' 100%');
$complete = round($this->_progress / $this->_total, 2, PHP_ROUND_HALF_UP);
@@ -118,7 +118,7 @@ public function draw()
if ($barLen > 1) {
$bar = str_repeat('=', $barLen - 1) . '>';
}
- $pad = ceil($this->_width - $numberLen - $barLen);
+ $pad = (int)ceil($this->_width - $numberLen - $barLen);
if ($pad > 0) {
$bar .= str_repeat(' ', $pad);
}
diff --git a/src/Console/Helper/TableShellHelper.php b/src/Console/Helper/TableShellHelper.php
index ec9bebe527..bc08d058e3 100644
--- a/src/Console/Helper/TableShellHelper.php
+++ b/src/Console/Helper/TableShellHelper.php
@@ -26,7 +26,7 @@ class TableShellHelper extends BaseShellHelper
*
* @var array
*/
- protected $_defaultConfig = [
+ protected array $_defaultConfig = [
'headers' => true,
'rowSeparator' => false,
'headerStyle' => 'info',
@@ -38,7 +38,7 @@ class TableShellHelper extends BaseShellHelper
* @param array $rows The rows on which the columns width will be calculated on.
* @return array
*/
- protected function _calculateWidths($rows)
+ protected function _calculateWidths(array $rows): array
{
$widths = [];
foreach ($rows as $line) {
@@ -59,7 +59,7 @@ protected function _calculateWidths($rows)
* @param array $widths The widths of each column to output.
* @return void
*/
- protected function _rowSeparator($widths)
+ protected function _rowSeparator(array $widths): void
{
$out = '';
foreach ($widths as $column) {
@@ -77,7 +77,7 @@ protected function _rowSeparator($widths)
* @param array $options Options to be passed.
* @return void
*/
- protected function _render($row, $widths, $options = [])
+ protected function _render(array $row, array $widths, array $options = []): void
{
$out = '';
foreach ($row as $i => $column) {
@@ -94,19 +94,19 @@ protected function _render($row, $widths, $options = [])
/**
* Output a table.
*
- * @param array $rows The data to render out.
+ * @param array $args The data to render out.
* @return void
*/
- public function output($rows)
+ public function output(array $args): void
{
$config = $this->config();
- $widths = $this->_calculateWidths($rows);
+ $widths = $this->_calculateWidths($args);
$this->_rowSeparator($widths);
if ($config['headers'] === true) {
- $this->_render(array_shift($rows), $widths, ['style' => $config['headerStyle']]);
+ $this->_render(array_shift($args), $widths, ['style' => $config['headerStyle']]);
$this->_rowSeparator($widths);
}
- foreach ($rows as $line) {
+ foreach ($args as $line) {
$this->_render($line, $widths);
if ($config['rowSeparator'] === true) {
$this->_rowSeparator($widths);
@@ -124,7 +124,7 @@ public function output($rows)
* @param string $style The style to be applied
* @return string
*/
- protected function _addStyle($text, $style)
+ protected function _addStyle(string $text, string $style): string
{
return '<' . $style . '>' . $text . '' . $style . '>';
}
diff --git a/src/Console/Shell.php b/src/Console/Shell.php
index 6be003e83a..81e0761cfb 100644
--- a/src/Console/Shell.php
+++ b/src/Console/Shell.php
@@ -17,6 +17,7 @@
namespace Cake\Console;
+use Cake\Console\Helper\BaseShellHelper;
use Cake\Core\App;
use Cake\Core\CakeObject;
use Cake\Core\CakePlugin;
@@ -71,117 +72,117 @@ class Shell extends CakeObject
/**
* An instance of ConsoleOptionParser that has been configured for this class.
*
- * @var ConsoleOptionParser
+ * @var ConsoleOptionParser|null
*/
- public $OptionParser;
+ public ?ConsoleOptionParser $OptionParser = null;
/**
* If true, the script will ask for permission to perform actions.
*
* @var bool
*/
- public $interactive = true;
+ public bool $interactive = true;
/**
* Contains command switches parsed from the command line.
*
* @var array
*/
- public $params = [];
+ public array $params = [];
/**
* The command (method/task) that is being run.
*
- * @var string
+ * @var string|null
*/
- public $command;
+ public ?string $command = null;
/**
* Contains arguments parsed from the command line.
*
* @var array
*/
- public $args = [];
+ public array $args = [];
/**
* The name of the shell in camelized.
*
- * @var string
+ * @var string|null
*/
- public $name = null;
+ public ?string $name = null;
/**
* The name of the plugin the shell belongs to.
* Is automatically set by ShellDispatcher when a shell is constructed.
*
- * @var string
+ * @var string|null
*/
- public $plugin = null;
+ public ?string $plugin = null;
/**
* Contains tasks to load and instantiate
*
- * @var array
+ * @var array|bool|null
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::$tasks
*/
- public $tasks = [];
+ public array|bool|null $tasks = [];
/**
* Contains the loaded tasks
*
* @var array
*/
- public $taskNames = [];
+ public array $taskNames = [];
/**
* Contains models to load and instantiate
*
- * @var array
+ * @var array|bool|null
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::$uses
*/
- public $uses = [];
+ public array|bool|null $uses = [];
/**
* This shell's primary model class name, the first model in the $uses property
*
- * @var string
+ * @var string|null
*/
- public $modelClass = null;
+ public ?string $modelClass = null;
/**
* Task Collection for the command, used to create Tasks.
*
- * @var TaskCollection
+ * @var TaskCollection|null
*/
- public $Tasks;
+ public ?TaskCollection $Tasks = null;
/**
* Normalized map of tasks.
*
- * @var string
+ * @var array
*/
- protected $_taskMap = [];
+ protected array $_taskMap = [];
/**
* stdout object.
*
- * @var ConsoleOutput
+ * @var ConsoleOutput|null
*/
- public $stdout;
+ public ?ConsoleOutput $stdout = null;
/**
* stderr object.
*
- * @var ConsoleOutput
+ * @var ConsoleOutput|null
*/
- public $stderr;
+ public ?ConsoleOutput $stderr = null;
/**
* stdin object
*
- * @var ConsoleInput
+ * @var ConsoleInput|null
*/
- public $stdin;
+ public ?ConsoleInput $stdin = null;
/**
* The number of bytes last written to the output stream
@@ -189,25 +190,28 @@ class Shell extends CakeObject
*
* @var int
*/
- protected $_lastWritten = 0;
+ protected int $_lastWritten = 0;
/**
* Contains helpers which have been previously instantiated
*
* @var array
*/
- protected $_helpers = [];
+ protected array $_helpers = [];
/**
* Constructs this Shell instance.
*
- * @param ConsoleOutput $stdout A ConsoleOutput object for stdout.
- * @param ConsoleOutput $stderr A ConsoleOutput object for stderr.
- * @param ConsoleInput $stdin A ConsoleInput object for stdin.
+ * @param ConsoleOutput|null $stdout A ConsoleOutput object for stdout.
+ * @param ConsoleOutput|null $stderr A ConsoleOutput object for stderr.
+ * @param ConsoleInput|null $stdin A ConsoleInput object for stdin.
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell
*/
- public function __construct($stdout = null, $stderr = null, $stdin = null)
- {
+ public function __construct(
+ ?ConsoleOutput $stdout = null,
+ ?ConsoleOutput $stderr = null,
+ ?ConsoleInput $stdin = null,
+ ) {
if (!$this->name) {
$className = static::class;
// Remove namespace from class name to get base name
@@ -240,7 +244,7 @@ public function __construct($stdout = null, $stderr = null, $stdin = null)
* @return void
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::initialize
*/
- public function initialize()
+ public function initialize(): void
{
$this->_loadModels();
$this->loadTasks();
@@ -256,7 +260,7 @@ public function initialize()
* @return void
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::startup
*/
- public function startup()
+ public function startup(): void
{
$this->_welcome();
}
@@ -266,7 +270,7 @@ public function startup()
*
* @return void
*/
- protected function _welcome()
+ protected function _welcome(): void
{
$this->out();
$this->out(__d('cake_console', 'Welcome to CakePHP %s Console', 'v' . Configure::version()));
@@ -281,7 +285,7 @@ protected function _welcome()
*
* @return bool
*/
- protected function _loadModels()
+ protected function _loadModels(): bool
{
if (is_array($this->uses)) {
[, $this->modelClass] = pluginSplit(current($this->uses));
@@ -299,7 +303,7 @@ protected function _loadModels()
* @param string $name The name of the model to look for.
* @return bool
*/
- public function __isset($name): bool
+ public function __isset(string $name): bool
{
if (is_array($this->uses)) {
foreach ($this->uses as $modelClass) {
@@ -307,7 +311,7 @@ public function __isset($name): bool
if ($name === $class) {
try {
return $this->loadModel($modelClass);
- } catch (MissingModelException $e) {
+ } catch (MissingModelException) {
}
}
}
@@ -319,13 +323,15 @@ public function __isset($name): bool
/**
* Loads and instantiates models required by this shell.
*
- * @param string $modelClass Name of model class to load
- * @param mixed $id Initial ID the instanced model class should have
+ * @param string|null $modelClass Name of model class to load
+ * @param string|int|null $id Initial ID the instanced model class should have
* @return bool true when single model found and instance created, error returned if model not found.
* @throws MissingModelException if the model class cannot be found.
*/
- public function loadModel($modelClass = null, $id = null): bool
- {
+ public function loadModel(
+ ?string $modelClass = null,
+ string|int|null $id = null,
+ ): bool {
if ($modelClass === null) {
$modelClass = $this->modelClass;
}
@@ -355,7 +361,7 @@ public function loadModel($modelClass = null, $id = null): bool
*
* @return bool
*/
- public function loadTasks()
+ public function loadTasks(): bool
{
if ($this->tasks === true || empty($this->tasks) || empty($this->Tasks)) {
return true;
@@ -373,7 +379,7 @@ public function loadTasks()
* @return bool Success
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::hasTask
*/
- public function hasTask($task)
+ public function hasTask(string $task): bool
{
return isset($this->_taskMap[Inflector::camelize($task)]);
}
@@ -385,7 +391,7 @@ public function hasTask($task)
* @return bool
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::hasMethod
*/
- public function hasMethod(string $name)
+ public function hasMethod(string $name): bool
{
try {
$method = new ReflectionMethod($this, $name);
@@ -421,10 +427,10 @@ public function hasMethod(string $name)
* `return $this->dispatchShell('schema', 'create', 'i18n', '--dry');`
*
* @param mixed ...$args Arguments to pass to the shell
- * @return mixed The return of the other shell.
+ * @return bool The return of the other shell.
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::dispatchShell
*/
- public function dispatchShell(...$args)
+ public function dispatchShell(mixed ...$args): bool
{
if (isset($args[0]) && is_string($args[0]) && count($args) === 1) {
$args = explode(' ', $args[0]);
@@ -451,11 +457,13 @@ public function dispatchShell(...$args)
* @param string $command The command name to run on this shell. If this argument is empty,
* and the shell has a `main()` method, that will be called instead.
* @param array $argv Array of arguments to run the shell with. This array should be missing the shell name.
- * @return int|bool
+ * @return int|bool|null
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::runCommand
*/
- public function runCommand(string $command, $argv)
- {
+ public function runCommand(
+ string $command,
+ array $argv,
+ ): int|bool|null {
$isTask = $this->hasTask($command);
$isMethod = $this->hasMethod($command);
$isMain = $this->hasMethod('main');
@@ -523,7 +531,7 @@ public function main(): void
* @param string $command The command to get help for.
* @return int|bool
*/
- protected function _displayHelp($command)
+ protected function _displayHelp(string $command): int|bool
{
$format = 'text';
if (!empty($this->args[0]) && $this->args[0] === 'xml') {
@@ -547,9 +555,8 @@ protected function _displayHelp($command)
public function getOptionParser(): ConsoleOptionParser
{
$name = ($this->plugin ? $this->plugin . '.' : '') . $this->name;
- $parser = new ConsoleOptionParser($name);
- return $parser;
+ return new ConsoleOptionParser($name);
}
/**
@@ -558,7 +565,7 @@ public function getOptionParser(): ConsoleOptionParser
* @param string $name The property name to access.
* @return Shell Object of Task
*/
- public function __get($name)
+ public function __get(string $name): mixed
{
if (empty($this->{$name}) && in_array($name, $this->taskNames)) {
$properties = $this->_taskMap[$name];
@@ -578,7 +585,7 @@ public function __get($name)
* @param string $name The name of the parameter to get.
* @return string|bool|null Value. Will return null if it doesn't exist.
*/
- public function param($name)
+ public function param(string $name): string|bool|null
{
if (!isset($this->params[$name])) {
return null;
@@ -591,13 +598,16 @@ public function param($name)
* Prompts the user for input, and returns it.
*
* @param string $prompt Prompt text.
- * @param array|string $options Array or string of options.
- * @param string $default Default input value.
- * @return mixed Either the default value, or the user-provided input.
+ * @param array|string|null $options Array or string of options.
+ * @param string|null $default Default input value.
+ * @return string|int|null Either the default value, or the user-provided input.
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::in
*/
- public function in($prompt, $options = null, $default = null)
- {
+ public function in(
+ string $prompt,
+ array|string|null $options = null,
+ ?string $default = null,
+ ): string|int|null {
if (!$this->interactive) {
return $default;
}
@@ -631,12 +641,15 @@ public function in($prompt, $options = null, $default = null)
* Prompts the user for input, and returns it.
*
* @param string $prompt Prompt text.
- * @param array|string $options Array or string of options.
- * @param string $default Default input value.
+ * @param array|string|null $options Array or string of options.
+ * @param string|null $default Default input value.
* @return string|int the default value, or the user-provided input.
*/
- protected function _getInput($prompt, $options, $default)
- {
+ protected function _getInput(
+ string $prompt,
+ array|string|null $options,
+ ?string $default,
+ ): string|int {
if (!is_array($options)) {
$printOptions = '';
} else {
@@ -657,7 +670,7 @@ protected function _getInput($prompt, $options, $default)
}
$result = trim($result);
- if ($default !== null && ($result === '' || $result === null)) {
+ if ($default !== null && $result === '') {
return $default;
}
@@ -680,7 +693,7 @@ protected function _getInput($prompt, $options, $default)
* @see CakeText::wrap()
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::wrapText
*/
- public function wrapText($text, $options = [])
+ public function wrapText(string $text, array|string|int $options = []): string
{
return CakeText::wrap($text, $options);
}
@@ -696,14 +709,17 @@ public function wrapText($text, $options = [])
* present in most shells. Using Shell::QUIET for a message means it will always display.
* While using Shell::VERBOSE means it will only display when verbose output is toggled.
*
- * @param array|string $message A string or an array of strings to output
+ * @param array|string|null $message A string or an array of strings to output
* @param int $newlines Number of newlines to append
* @param int $level The message's output level, see above.
- * @return int|bool|null Returns the number of bytes returned from writing to stdout.
+ * @return int|bool Returns the number of bytes returned from writing to stdout.
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::out
*/
- public function out($message = null, $newlines = 1, $level = Shell::NORMAL): int|bool|null
- {
+ public function out(
+ array|string|null $message = null,
+ int $newlines = 1,
+ int $level = Shell::NORMAL,
+ ): int|bool {
$currentLevel = Shell::NORMAL;
if (!empty($this->params['verbose'])) {
$currentLevel = Shell::VERBOSE;
@@ -728,13 +744,16 @@ public function out($message = null, $newlines = 1, $level = Shell::NORMAL): int
*
* **Warning** You cannot overwrite text that contains newlines.
*
- * @param array|string $message The message to output.
+ * @param array|string|null $message The message to output.
* @param int $newlines Number of newlines to append.
- * @param int $size The number of bytes to overwrite. Defaults to the length of the last message output.
+ * @param int|null $size The number of bytes to overwrite. Defaults to the length of the last message output.
* @return int|bool Returns the number of bytes returned from writing to stdout.
*/
- public function overwrite($message, $newlines = 1, $size = null)
- {
+ public function overwrite(
+ array|string|null $message,
+ int $newlines = 1,
+ ?int $size = null,
+ ): int|bool {
$size = $size ?: $this->_lastWritten;
// Output backspaces.
@@ -758,12 +777,12 @@ public function overwrite($message, $newlines = 1, $size = null)
* Outputs a single or multiple error messages to stderr. If no parameters
* are passed outputs just a newline.
*
- * @param array|string $message A string or an array of strings to output
+ * @param array|string|null $message A string or an array of strings to output
* @param int $newlines Number of newlines to append
* @return void
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::err
*/
- public function err($message = null, $newlines = 1)
+ public function err(array|string|null $message = null, int $newlines = 1): void
{
$this->stderr->write($message, $newlines);
}
@@ -775,7 +794,7 @@ public function err($message = null, $newlines = 1)
* @return string
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::nl
*/
- public function nl($multiplier = 1)
+ public function nl(int $multiplier = 1): string
{
return str_repeat(ConsoleOutput::LF, $multiplier);
}
@@ -788,7 +807,7 @@ public function nl($multiplier = 1)
* @return void
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::hr
*/
- public function hr($newlines = 0, $width = 63)
+ public function hr(int $newlines = 0, int $width = 63): void
{
$this->out(null, $newlines);
$this->out(str_repeat('-', $width));
@@ -800,11 +819,11 @@ public function hr($newlines = 0, $width = 63)
* and exits the application with status code 1
*
* @param string $title Title of the error
- * @param string $message An optional error message
+ * @param string|null $message An optional error message
* @return int
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::error
*/
- public function error($title, $message = null)
+ public function error(string $title, ?string $message = null): int
{
$this->err(__d('cake_console', 'Error: %s', $title));
@@ -822,10 +841,10 @@ public function error($title, $message = null)
* @return void
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::clear
*/
- public function clear()
+ public function clear(): void
{
if (empty($this->params['noclear'])) {
- if (DS === '/') {
+ if (DIRECTORY_SEPARATOR === '/') {
passthru('clear');
} else {
passthru('cls');
@@ -841,7 +860,7 @@ public function clear()
* @return bool Success
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::createFile
*/
- public function createFile($path, $contents)
+ public function createFile(string $path, string $contents): bool
{
$this->out();
@@ -882,7 +901,7 @@ public function createFile($path, $contents)
* @return BaseShellHelper Instance of helper class
* @throws RuntimeException If invalid class name is provided
*/
- public function helper($name)
+ public function helper(string $name): BaseShellHelper
{
if (isset($this->_helpers[$name])) {
return $this->_helpers[$name];
@@ -906,7 +925,7 @@ public function helper($name)
*
* @return bool Success
*/
- protected function _checkUnitTest()
+ protected function _checkUnitTest(): bool
{
if (class_exists(TestCase::class)) {
return true;
@@ -918,7 +937,7 @@ protected function _checkUnitTest()
if ($result) {
$this->out();
- $this->out(__d('cake_console', 'You can download PHPUnit from %s', 'http://phpunit.de'));
+ $this->out(__d('cake_console', 'You can download PHPUnit from %s', 'https://phpunit.de'));
}
return $result;
@@ -931,9 +950,9 @@ protected function _checkUnitTest()
* @return string short path
* @link https://book.cakephp.org/2.0/en/console-and-shells.html#Shell::shortPath
*/
- public function shortPath($file)
+ public function shortPath(string $file): string
{
- $shortPath = str_replace(ROOT, null, $file);
+ $shortPath = str_replace(ROOT, '', $file);
$shortPath = str_replace('..' . DS, '', $shortPath);
return str_replace(DS . DS, DS, $shortPath);
@@ -945,7 +964,7 @@ public function shortPath($file)
* @param string $name Controller class name
* @return string Path to controller
*/
- protected function _controllerPath($name)
+ protected function _controllerPath(string $name): string
{
return Inflector::underscore($name);
}
@@ -956,7 +975,7 @@ protected function _controllerPath($name)
* @param string $name Controller class name
* @return string Controller plural name
*/
- protected function _controllerName($name)
+ protected function _controllerName(string $name): string
{
return Inflector::pluralize(Inflector::camelize($name));
}
@@ -967,7 +986,7 @@ protected function _controllerName($name)
* @param string $name Name
* @return string Camelized and singularized model name
*/
- protected function _modelName($name)
+ protected function _modelName(string $name): string
{
return Inflector::camelize(Inflector::singularize($name));
}
@@ -978,7 +997,7 @@ protected function _modelName($name)
* @param string $name Model class name
* @return string Singular model key
*/
- protected function _modelKey($name)
+ protected function _modelKey(string $name): string
{
return Inflector::underscore($name) . '_id';
}
@@ -989,7 +1008,7 @@ protected function _modelKey($name)
* @param string $key Foreign key
* @return string Model name
*/
- protected function _modelNameFromKey($key)
+ protected function _modelNameFromKey(string $key): string
{
return Inflector::camelize(str_replace('_id', '', $key));
}
@@ -1000,7 +1019,7 @@ protected function _modelNameFromKey($key)
* @param string $name The plural underscored value.
* @return string name
*/
- protected function _singularName($name)
+ protected function _singularName(string $name): string
{
return Inflector::variable(Inflector::singularize($name));
}
@@ -1011,7 +1030,7 @@ protected function _singularName($name)
* @param string $name Name to use
* @return string Plural name for views
*/
- protected function _pluralName($name)
+ protected function _pluralName(string $name): string
{
return Inflector::variable(Inflector::pluralize($name));
}
@@ -1022,7 +1041,7 @@ protected function _pluralName($name)
* @param string $name Controller name
* @return string Singular human name
*/
- protected function _singularHumanName($name)
+ protected function _singularHumanName(string $name): string
{
return Inflector::humanize(Inflector::underscore(Inflector::singularize($name)));
}
@@ -1033,7 +1052,7 @@ protected function _singularHumanName($name)
* @param string $name Controller name
* @return string Plural human name
*/
- protected function _pluralHumanName($name)
+ protected function _pluralHumanName(string $name): string
{
return Inflector::humanize(Inflector::underscore($name));
}
@@ -1044,7 +1063,7 @@ protected function _pluralHumanName($name)
* @param string $pluginName Name of the plugin you want ie. DebugKit
* @return string path path to the correct plugin.
*/
- protected function _pluginPath($pluginName)
+ protected function _pluginPath(string $pluginName): string
{
if (CakePlugin::loaded($pluginName)) {
return CakePlugin::path($pluginName);
@@ -1111,7 +1130,7 @@ protected function _configureStdErrLogger(): void
* @param string $logger The name of the logger to check
* @return bool
*/
- protected function _loggerIsConfigured($logger): bool
+ protected function _loggerIsConfigured(string $logger): bool
{
$configured = CakeLog::configured();
diff --git a/src/Console/ShellDispatcher.php b/src/Console/ShellDispatcher.php
index e45df20e65..d7ec91f4fc 100644
--- a/src/Console/ShellDispatcher.php
+++ b/src/Console/ShellDispatcher.php
@@ -39,14 +39,14 @@ class ShellDispatcher
*
* @var array
*/
- public $params = [];
+ public array $params = [];
/**
* Contains arguments parsed from the command line.
*
* @var array
*/
- public $args = [];
+ public array $args = [];
/**
* Constructor
@@ -57,7 +57,7 @@ class ShellDispatcher
* @param array $args the argv from PHP
* @param bool $bootstrap Should the environment be bootstrapped.
*/
- public function __construct($args = [], $bootstrap = true)
+ public function __construct(array $args = [], bool $bootstrap = true)
{
set_time_limit(0);
$this->parseParams($args);
@@ -72,9 +72,9 @@ public function __construct($args = [], $bootstrap = true)
* Run the dispatcher
*
* @param array $argv The argv from PHP
- * @return never
+ * @return void
*/
- public static function run($argv)
+ public static function run(array $argv): void
{
$dispatcher = new ShellDispatcher($argv);
$dispatcher->_stop($dispatcher->dispatch() === false ? 1 : 0);
@@ -85,12 +85,12 @@ public static function run($argv)
*
* @return void
*/
- protected function _initConstants()
+ protected function _initConstants(): void
{
if (function_exists('ini_set')) {
- ini_set('html_errors', false);
- ini_set('implicit_flush', true);
- ini_set('max_execution_time', 0);
+ ini_set('html_errors', false); // @phpstan-ignore-line
+ ini_set('implicit_flush', true); // @phpstan-ignore-line
+ ini_set('max_execution_time', 0); // @phpstan-ignore-line
}
if (!defined('CAKEPHP_SHELL')) {
@@ -106,7 +106,7 @@ protected function _initConstants()
* @return void
* @throws CakeException
*/
- protected function _initEnvironment()
+ protected function _initEnvironment(): void
{
if (!$this->_bootstrap()) {
$message = "Unable to load CakePHP core.\nMake sure " . DS . 'src' . DS . 'Cake exists in ' . CAKE_CORE_INCLUDE_PATH;
@@ -129,7 +129,7 @@ protected function _initEnvironment()
*
* @return bool Success.
*/
- protected function _bootstrap()
+ protected function _bootstrap(): bool
{
if (!defined('ROOT')) {
define('ROOT', $this->params['root']);
@@ -182,7 +182,7 @@ protected function _bootstrap()
*
* @return void
*/
- public function setErrorHandlers()
+ public function setErrorHandlers(): void
{
$error = Configure::read('Error');
$exception = Configure::read('Exception');
@@ -208,7 +208,7 @@ public function setErrorHandlers()
* @return bool
* @throws MissingShellMethodException
*/
- public function dispatch()
+ public function dispatch(): bool
{
$shiftArgs = $this->shiftArgs();
@@ -250,8 +250,9 @@ public function dispatch()
if (method_exists($shell, 'main')) {
$shell->startup();
+ $shell->main();
- return $shell->main();
+ return true;
}
}
@@ -264,10 +265,10 @@ public function dispatch()
* All paths in the loaded shell paths are searched.
*
* @param string $shell Optionally the name of a plugin
- * @return mixed An object
+ * @return object An object
* @throws MissingShellException when errors are encountered.
*/
- protected function _getShell($shell)
+ protected function _getShell(string $shell): object
{
[$plugin, $shell] = pluginSplit($shell, true);
@@ -292,10 +293,10 @@ protected function _getShell($shell)
]);
}
- $Shell = new $class();
- $Shell->plugin = trim($plugin, '.');
+ $_shell = new $class();
+ $_shell->plugin = trim($plugin, '.');
- return $Shell;
+ return $_shell;
}
/**
@@ -304,7 +305,7 @@ protected function _getShell($shell)
* @param array $args Parameters to parse
* @return void
*/
- public function parseParams($args)
+ public function parseParams(array $args): void
{
$this->_parsePaths($args);
@@ -357,7 +358,7 @@ public function parseParams($args)
/**
* @return array
*/
- protected function _getDefaults()
+ protected function _getDefaults(): array
{
if (InstalledVersions::isInstalled('pieceofcake2/app')) {
$root = realpath(InstalledVersions::getInstallPath('pieceofcake2/app'));
@@ -388,7 +389,7 @@ protected function _getDefaults()
* @param string $path absolute or relative path.
* @return bool
*/
- protected function _isAbsolutePath($path)
+ protected function _isAbsolutePath(string $path): bool
{
return $path[0] === '/' || $this->_isWindowsPath($path);
}
@@ -399,7 +400,7 @@ protected function _isAbsolutePath($path)
* @param string $path absolute path.
* @return bool
*/
- protected function _isWindowsPath($path)
+ protected function _isWindowsPath(string $path): bool
{
return preg_match('/([a-z])(:)/i', $path) == 1;
}
@@ -410,11 +411,10 @@ protected function _isWindowsPath($path)
* @param array $args The argv to parse.
* @return void
*/
- protected function _parsePaths($args)
+ protected function _parsePaths(array $args): void
{
$parsed = [];
$keys = ['-working', '--working', '-app', '--app', '-root', '--root', '-webroot', '--webroot'];
- $args = (array)$args;
foreach ($keys as $key) {
while (($index = array_search($key, $args)) !== false) {
$keyname = str_replace('-', '', $key);
@@ -432,7 +432,7 @@ protected function _parsePaths($args)
*
* @return mixed Null if there are no arguments otherwise the shifted argument
*/
- public function shiftArgs()
+ public function shiftArgs(): mixed
{
return array_shift($this->args);
}
@@ -442,7 +442,7 @@ public function shiftArgs()
*
* @return void
*/
- public function help()
+ public function help(): void
{
$this->args = array_merge(['command_list'], $this->args);
$this->dispatch();
@@ -452,9 +452,9 @@ public function help()
* Stop execution of the current script
*
* @param string|int $status see http://php.net/exit for values
- * @return never
+ * @return void
*/
- protected function _stop($status = 0)
+ protected function _stop(string|int $status = 0): void
{
exit($status);
}
diff --git a/src/Console/TaskCollection.php b/src/Console/TaskCollection.php
index fd16dff8e5..b4918a080d 100644
--- a/src/Console/TaskCollection.php
+++ b/src/Console/TaskCollection.php
@@ -35,23 +35,28 @@ class TaskCollection extends ObjectCollection
*
* @var Shell
*/
- protected $_Shell;
+ protected Shell $_Shell;
/**
* The directory inside each shell path that contains tasks.
*
* @var string
*/
- public $taskPathPrefix = 'tasks/';
+ public string $taskPathPrefix = 'tasks/';
+
+ /**
+ * @var array
+ */
+ protected array $_loaded = [];
/**
* Constructor
*
- * @param Shell $Shell The shell this task collection is attached to.
+ * @param Shell $shell The shell this task collection is attached to.
*/
- public function __construct(Shell $Shell)
+ public function __construct(Shell $shell)
{
- $this->_Shell = $Shell;
+ $this->_Shell = $shell;
}
/**
@@ -59,39 +64,39 @@ public function __construct(Shell $Shell)
*
* You can alias your task as an existing task by setting the 'className' key, i.e.,
* ```
- * public $tasks = array(
- * 'DbConfig' => array(
- * 'className' => 'Bakeplus.DbConfigure'
- * );
- * );
+ * public $tasks = [
+ * 'DbConfig' => [
+ * 'className' => 'Bakeplus.DbConfigure',
+ * ];
+ * ];
* ```
* All calls to the `DbConfig` task would use `DbConfigure` found in the `Bakeplus` plugin instead.
*
- * @param string $task Task name to load
- * @param array $settings Settings for the task.
- * @return AppShell A task object, Either the existing loaded task or a new one.
+ * @param string $name Task name to load
+ * @param array $options Settings for the task.
+ * @return Shell A task object, Either the existing loaded task or a new one.
* @throws MissingTaskException when the task could not be found
*/
- public function load($task, $settings = [])
+ public function load($name, array $options = []): Shell
{
- if (is_array($settings) && isset($settings['className'])) {
- $alias = $task;
- $task = $settings['className'];
+ if (isset($options['className'])) {
+ $alias = $name;
+ $name = $options['className'];
}
- [$plugin, $name] = pluginSplit($task, true);
+ [$plugin, $_name] = pluginSplit($name, true);
if (!isset($alias)) {
- $alias = $name;
+ $alias = $_name;
}
if (isset($this->_loaded[$alias])) {
return $this->_loaded[$alias];
}
- $taskClass = App::className($task, 'Console/Command/Task', 'Task');
+ $taskClass = App::className($name, 'Console/Command/Task', 'Task');
if (!$taskClass) {
throw new MissingTaskException([
- 'class' => $name . 'Task',
+ 'class' => $_name . 'Task',
'plugin' => $plugin ? substr($plugin, 0, -1) : null,
]);
}
diff --git a/src/Controller/CakeErrorController.php b/src/Controller/CakeErrorController.php
index de988a0cb6..59ea6241d2 100644
--- a/src/Controller/CakeErrorController.php
+++ b/src/Controller/CakeErrorController.php
@@ -21,6 +21,7 @@
namespace Cake\Controller;
use AppController;
+use Cake\Controller\Component\RequestHandlerComponent;
use Cake\Core\App;
use Cake\Network\CakeRequest;
use Cake\Network\CakeResponse;
@@ -40,25 +41,30 @@ class CakeErrorController extends AppController
/**
* Uses Property
*
- * @var array|bool
+ * @var array|bool|null
*/
- public array|bool $uses = [];
+ public array|bool|null $uses = [];
/**
* Constructor
*
- * @param CakeRequest $request Request instance.
- * @param CakeResponse $response Response instance.
+ * @param CakeRequest|null $request Request instance.
+ * @param CakeResponse|null $response Response instance.
*/
- public function __construct($request = null, $response = null)
- {
+ public function __construct(
+ ?CakeRequest $request = null,
+ ?CakeResponse $response = null,
+ ) {
parent::__construct($request, $response);
+
$this->constructClasses();
if (
count(Router::extensions()) &&
!$this->Components->attached('RequestHandler')
) {
- $this->RequestHandler = $this->Components->load('RequestHandler');
+ /** @var RequestHandlerComponent $requestHandler */
+ $requestHandler = $this->Components->load('RequestHandler');
+ $this->RequestHandler = $requestHandler;
}
if ($this->Components->enabled('Auth')) {
$this->Components->disable('Auth');
diff --git a/src/Controller/Component.php b/src/Controller/Component.php
index f3e90fe48b..05c8c4b963 100644
--- a/src/Controller/Component.php
+++ b/src/Controller/Component.php
@@ -16,7 +16,10 @@
namespace Cake\Controller;
+use Cake\Controller\Component\Acl\PhpAco;
+use Cake\Controller\Component\Acl\PhpAro;
use Cake\Core\CakeObject;
+use Cake\Model\Model;
/**
* Base class for an individual Component. Components provide reusable bits of
@@ -69,6 +72,20 @@ class Component extends CakeObject
*/
protected array $_componentMap = [];
+ /**
+ * Aro Object
+ *
+ * @var PhpAro|Model|null
+ */
+ public PhpAro|Model|null $Aro = null;
+
+ /**
+ * Aco Object
+ *
+ * @var PhpAco|Model|null
+ */
+ public PhpAco|Model|null $Aco = null;
+
/**
* Constructor
*
@@ -91,7 +108,7 @@ public function __construct(ComponentCollection $collection, array $settings = [
* @param string $name Name of component to get.
* @return mixed A Component object or null.
*/
- public function __get(string $name)
+ public function __get(string $name): mixed
{
if (isset($this->_componentMap[$name]) && !isset($this->{$name})) {
$settings = (array)$this->_componentMap[$name]['settings'] + ['enabled' => false];
@@ -100,6 +117,8 @@ public function __get(string $name)
if (isset($this->{$name})) {
return $this->{$name};
}
+
+ return null;
}
/**
@@ -109,7 +128,7 @@ public function __get(string $name)
* @return void
* @link https://book.cakephp.org/2.0/en/controllers/components.html#Component::initialize
*/
- public function initialize(Controller $controller)
+ public function initialize(Controller $controller): void
{
}
@@ -161,13 +180,18 @@ public function shutdown(Controller $controller)
* be used as the new URL to redirect to.
*
* @param Controller $controller Controller with components to beforeRedirect
- * @param array|string $url Either the string or URL array that is being redirected to.
- * @param int $status The status code of the redirect
+ * @param array|string|null $url Either the string or URL array that is being redirected to.
+ * @param array|int|null $status The status code of the redirect
* @param bool $exit Will the script exit.
- * @return array|null Either an array or null.
+ * @return array|string|false|null Either an array or null.
* @link https://book.cakephp.org/2.0/en/controllers/components.html#Component::beforeRedirect
*/
- public function beforeRedirect(Controller $controller, $url, $status = null, $exit = true)
- {
+ public function beforeRedirect(
+ Controller $controller,
+ array|string|null $url,
+ array|int|null $status = null,
+ bool $exit = true,
+ ): array|string|false|null {
+ return null;
}
}
diff --git a/src/Controller/Component/Acl/AclInterface.php b/src/Controller/Component/Acl/AclInterface.php
index 8fd011c29d..af840f3f62 100644
--- a/src/Controller/Component/Acl/AclInterface.php
+++ b/src/Controller/Component/Acl/AclInterface.php
@@ -17,6 +17,7 @@
namespace Cake\Controller\Component\Acl;
use Cake\Controller\Component;
+use Cake\Model\Model;
/**
* Access Control List interface.
@@ -29,42 +30,58 @@ interface AclInterface
/**
* Empty method to be overridden in subclasses
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
+ * @param Model|array|string $aro ARO The requesting object identifier.
+ * @param Model|array|string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return bool Success
*/
- public function check($aro, $aco, $action = '*');
+ public function check(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool;
/**
* Allow methods are used to grant an ARO access to an ACO.
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
- * @param string $action Action (defaults to *)
+ * @param Model|array|string|null $aro ARO The requesting object identifier.
+ * @param Model|array|string|null $aco ACO The controlled object identifier.
+ * @param array|string $action Action (defaults to *)
* @return bool Success
*/
- public function allow($aro, $aco, $action = '*');
+ public function allow(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ array|string $action = '*',
+ ): bool;
/**
* Deny methods are used to remove permission from an ARO to access an ACO.
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
+ * @param Model|array|string $aro ARO The requesting object identifier.
+ * @param Model|array|string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return bool Success
*/
- public function deny($aro, $aco, $action = '*');
+ public function deny(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool;
/**
* Inherit methods modify the permission for an ARO to be that of its parent object.
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
+ * @param Model|array|string $aro ARO The requesting object identifier.
+ * @param Model|array|string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return bool Success
*/
- public function inherit($aro, $aco, $action = '*');
+ public function inherit(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool;
/**
* Initialization method for the Acl implementation
@@ -72,5 +89,5 @@ public function inherit($aro, $aco, $action = '*');
* @param Component $component The AclComponent instance.
* @return void
*/
- public function initialize(Component $component);
+ public function initialize(Component $component): void;
}
diff --git a/src/Controller/Component/Acl/DbAcl.php b/src/Controller/Component/Acl/DbAcl.php
index 55388a69a8..0fd0c3b557 100644
--- a/src/Controller/Component/Acl/DbAcl.php
+++ b/src/Controller/Component/Acl/DbAcl.php
@@ -18,6 +18,8 @@
use Cake\Controller\Component;
use Cake\Core\CakeObject;
+use Cake\Model\Aco;
+use Cake\Model\Aro;
use Cake\Model\Model;
use Cake\Model\Permission;
use Cake\Utility\ClassRegistry;
@@ -44,19 +46,19 @@
class DbAcl extends CakeObject implements AclInterface
{
/**
- * @var Model
+ * @var Permission
*/
- public Model $Permission;
+ public Permission $Permission;
/**
- * @var Model
+ * @var Aro|Model
*/
- public Model $Aro;
+ public Aro|Model $Aro;
/**
- * @var Model
+ * @var Aco|Model
*/
- public Model $Aco;
+ public Aco|Model $Aco;
/**
* Constructor
@@ -65,7 +67,9 @@ public function __construct()
{
parent::__construct();
- $this->Permission = ClassRegistry::init(['class' => Permission::class, 'alias' => 'Permission']);
+ /** @var Permission $permission */
+ $permission = ClassRegistry::init(['class' => Permission::class, 'alias' => 'Permission']);
+ $this->Permission = $permission;
$this->Aro = $this->Permission->Aro;
$this->Aco = $this->Permission->Aco;
}
@@ -76,7 +80,7 @@ public function __construct()
* @param Component $component The AclComponent instance.
* @return void
*/
- public function initialize(Component $component)
+ public function initialize(Component $component): void
{
$component->Aro = $this->Aro;
$component->Aco = $this->Aco;
@@ -85,96 +89,117 @@ public function initialize(Component $component)
/**
* Checks if the given $aro has access to action $action in $aco
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
+ * @param Model|array|string|null $aro ARO The requesting object identifier.
+ * @param Model|array|string|null $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return bool Success (true if ARO has access to action in ACO, false otherwise)
* @link https://book.cakephp.org/2.0/en/core-libraries/components/access-control-lists.html#checking-permissions-the-acl-component
*/
- public function check($aro, $aco, $action = '*')
- {
+ public function check(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool {
return $this->Permission->check($aro, $aco, $action);
}
/**
* Allow $aro to have access to action $actions in $aco
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
- * @param string $action Action (defaults to *)
+ * @param Model|array|string|null $aro ARO The requesting object identifier.
+ * @param Model|array|string|null $aco ACO The controlled object identifier.
+ * @param array|string $action Action (defaults to *)
* @param int $value Value to indicate access type (1 to give access, -1 to deny, 0 to inherit)
* @return bool Success
* @link https://book.cakephp.org/2.0/en/core-libraries/components/access-control-lists.html#assigning-permissions
*/
- public function allow($aro, $aco, $action = '*', $value = 1)
- {
+ public function allow(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ array|string $action = '*',
+ int $value = 1,
+ ): bool {
return $this->Permission->allow($aro, $aco, $action, $value);
}
/**
* Deny access for $aro to action $action in $aco
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
+ * @param Model|array|string|null $aro ARO The requesting object identifier.
+ * @param Model|array|string|null $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return bool Success
* @link https://book.cakephp.org/2.0/en/core-libraries/components/access-control-lists.html#assigning-permissions
*/
- public function deny($aro, $aco, $action = '*')
- {
+ public function deny(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool {
return $this->allow($aro, $aco, $action, -1);
}
/**
* Let access for $aro to action $action in $aco be inherited
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
+ * @param Model|array|string|null $aro ARO The requesting object identifier.
+ * @param Model|array|string|null $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return bool Success
*/
- public function inherit($aro, $aco, $action = '*')
- {
+ public function inherit(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool {
return $this->allow($aro, $aco, $action, 0);
}
/**
* Allow $aro to have access to action $actions in $aco
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
+ * @param Model|array|string|null $aro ARO The requesting object identifier.
+ * @param Model|array|string|null $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return bool Success
* @see allow()
*/
- public function grant($aro, $aco, $action = '*')
- {
+ public function grant(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool {
return $this->allow($aro, $aco, $action);
}
/**
* Deny access for $aro to action $action in $aco
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
+ * @param Model|array|string|null $aro ARO The requesting object identifier.
+ * @param Model|array|string|null $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return bool Success
* @see deny()
*/
- public function revoke($aro, $aco, $action = '*')
- {
+ public function revoke(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool {
return $this->deny($aro, $aco, $action);
}
/**
* Get an array of access-control links between the given Aro and Aco
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
- * @return array Indexed array with: 'aro', 'aco' and 'link'
+ * @param Model|array|string|null $aro ARO The requesting object identifier.
+ * @param Model|array|string|null $aco ACO The controlled object identifier.
+ * @return array|false Indexed array with: 'aro', 'aco' and 'link'
*/
- public function getAclLink($aro, $aco)
- {
+ public function getAclLink(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ ): array|false {
return $this->Permission->getAclLink($aro, $aco);
}
@@ -184,7 +209,7 @@ public function getAclLink($aro, $aco)
* @param array $keys Permission model info
* @return array ACO keys
*/
- protected function _getAcoKeys($keys)
+ protected function _getAcoKeys(array $keys): array
{
return $this->Permission->getAcoKeys($keys);
}
diff --git a/src/Controller/Component/Acl/IniAcl.php b/src/Controller/Component/Acl/IniAcl.php
index 3d6af81a39..4df12f99e3 100644
--- a/src/Controller/Component/Acl/IniAcl.php
+++ b/src/Controller/Component/Acl/IniAcl.php
@@ -19,6 +19,7 @@
use Cake\Configure\IniReader;
use Cake\Controller\Component;
use Cake\Core\CakeObject;
+use Cake\Model\Model;
use Cake\Utility\Hash;
/**
@@ -32,9 +33,9 @@ class IniAcl extends CakeObject implements AclInterface
/**
* Array with configuration, parsed from ini file
*
- * @var array
+ * @var array|null
*/
- public $config = null;
+ public ?array $config = null;
/**
* The Hash::extract() path to the user/aro identifier in the
@@ -43,7 +44,7 @@ class IniAcl extends CakeObject implements AclInterface
*
* @var string
*/
- public $userPath = 'User.username';
+ public string $userPath = 'User.username';
/**
* Initialize method
@@ -51,46 +52,55 @@ class IniAcl extends CakeObject implements AclInterface
* @param Component $component The AclComponent instance.
* @return void
*/
- public function initialize(Component $component)
+ public function initialize(Component $component): void
{
}
/**
* No op method, allow cannot be done with IniAcl
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
- * @param string $action Action (defaults to *)
+ * @param Model|array|string|null $aro ARO The requesting object identifier.
+ * @param Model|array|string|null $aco ACO The controlled object identifier.
+ * @param array|string $action Action (defaults to *)
* @return bool Success
*/
- public function allow($aro, $aco, $action = '*')
- {
+ public function allow(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ array|string $action = '*',
+ ): bool {
return false;
}
/**
* No op method, deny cannot be done with IniAcl
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
+ * @param Model|array|string|null $aro ARO The requesting object identifier.
+ * @param Model|array|string|null $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return bool Success
*/
- public function deny($aro, $aco, $action = '*')
- {
+ public function deny(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool {
return false;
}
/**
* No op method, inherit cannot be done with IniAcl
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
+ * @param Model|array|string|null $aro ARO The requesting object identifier.
+ * @param Model|array|string|null $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return bool Success
*/
- public function inherit($aro, $aco, $action = '*')
- {
+ public function inherit(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool {
return false;
}
@@ -99,13 +109,16 @@ public function inherit($aro, $aco, $action = '*')
* ACO (access control object).Looks at the acl.ini.php file for permissions
* (see instructions in /config/acl.ini.php).
*
- * @param string $aro ARO
- * @param string $aco ACO
+ * @param Model|array|string|null $aro ARO
+ * @param Model|array|string|null $aco ACO
* @param string $action Action
* @return bool Success
*/
- public function check($aro, $aco, $action = null)
- {
+ public function check(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool {
if (!$this->config) {
$this->config = $this->readConfigFile(CONFIG . 'acl.ini.php');
}
@@ -165,7 +178,7 @@ public function check($aro, $aco, $action = null)
* @param string $filename File
* @return array INI section structure
*/
- public function readConfigFile($filename)
+ public function readConfigFile(string $filename): array
{
$iniFile = new IniReader(dirname($filename) . DS);
@@ -178,7 +191,7 @@ public function readConfigFile($filename)
* @param array $array Array to trim
* @return array Trimmed array
*/
- public function arrayTrim($array)
+ public function arrayTrim(array $array): array
{
foreach ($array as $key => $value) {
$array[$key] = trim($value);
diff --git a/src/Controller/Component/Acl/PhpAcl.php b/src/Controller/Component/Acl/PhpAcl.php
index 19e2e331cc..1a5b072138 100644
--- a/src/Controller/Component/Acl/PhpAcl.php
+++ b/src/Controller/Component/Acl/PhpAcl.php
@@ -22,8 +22,7 @@
use Cake\Controller\Component;
use Cake\Core\CakeObject;
use Cake\Error\AclException;
-use Cake\Utility\Hash;
-use Cake\Utility\Inflector;
+use Cake\Model\Model;
/**
* PhpAcl implements an access control system using a plain PHP configuration file.
@@ -54,21 +53,21 @@ class PhpAcl extends CakeObject implements AclInterface
*
* @var array
*/
- public $options = [];
+ public array $options = [];
/**
* Aro Object
*
- * @var PhpAro
+ * @var PhpAro|Model|null
*/
- public $Aro = null;
+ public PhpAro|Model|null $Aro = null;
/**
* Aco Object
*
- * @var PhpAco
+ * @var PhpAco|Model|null
*/
- public $Aco = null;
+ public PhpAco|Model|null $Aco = null;
/**
* Constructor
@@ -86,20 +85,20 @@ public function __construct()
/**
* Initialize method
*
- * @param AclComponent $Component Component instance
+ * @param Component $component Component instance
* @return void
*/
- public function initialize(Component $Component)
+ public function initialize(Component $component): void
{
- if (!empty($Component->settings['adapter'])) {
- $this->options = $Component->settings['adapter'] + $this->options;
+ if (!empty($component->settings['adapter'])) {
+ $this->options = $component->settings['adapter'] + $this->options;
}
$Reader = new PhpReader(dirname($this->options['config']) . DS);
$config = $Reader->read(basename($this->options['config']));
$this->build($config);
- $Component->Aco = $this->Aco;
- $Component->Aro = $this->Aro;
+ $component->Aco = $this->Aco;
+ $component->Aro = $this->Aro;
}
/**
@@ -109,7 +108,7 @@ public function initialize(Component $Component)
* @return void
* @throws AclException When required keys are missing.
*/
- public function build(array $config)
+ public function build(array $config): void
{
if (empty($config['roles'])) {
throw new AclException(__d('cake_dev', '"roles" section not found in configuration.'));
@@ -121,7 +120,7 @@ public function build(array $config)
$rules['allow'] = !empty($config['rules']['allow']) ? $config['rules']['allow'] : [];
$rules['deny'] = !empty($config['rules']['deny']) ? $config['rules']['deny'] : [];
- $roles = !empty($config['roles']) ? $config['roles'] : [];
+ $roles = $config['roles'];
$map = !empty($config['map']) ? $config['map'] : [];
$alias = !empty($config['alias']) ? $config['alias'] : [];
@@ -132,39 +131,52 @@ public function build(array $config)
/**
* No op method, allow cannot be done with PhpAcl
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
- * @param string $action Action (defaults to *)
+ * @param Model|array|string|null $aro ARO The requesting object identifier.
+ * @param Model|array|string|null $aco ACO The controlled object identifier.
+ * @param array|string $action Action (defaults to *)
* @return bool Success
*/
- public function allow($aro, $aco, $action = '*')
- {
- return $this->Aco->access($this->Aro->resolve($aro), $aco, $action, 'allow');
+ public function allow(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ array|string $action = '*',
+ ): bool {
+ $this->Aco->access($this->Aro->resolve($aro), $aco, $action, 'allow');
+
+ return false;
}
/**
* deny ARO access to ACO
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
+ * @param Model|array|string|null $aro ARO The requesting object identifier.
+ * @param Model|array|string|null $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return bool Success
*/
- public function deny($aro, $aco, $action = '*')
- {
- return $this->Aco->access($this->Aro->resolve($aro), $aco, $action, 'deny');
+ public function deny(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool {
+ $this->Aco->access($this->Aro->resolve($aro), $aco, $action, 'deny');
+
+ return false;
}
/**
* No op method
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
+ * @param Model|array|string|null $aro ARO The requesting object identifier.
+ * @param Model|array|string|null $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return bool Success
*/
- public function inherit($aro, $aco, $action = '*')
- {
+ public function inherit(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool {
return false;
}
@@ -172,13 +184,16 @@ public function inherit($aro, $aco, $action = '*')
* Main ACL check function. Checks to see if the ARO (access request object) has access to the
* ACO (access control object).
*
- * @param string $aro ARO
- * @param string $aco ACO
+ * @param Model|array|string|null $aro ARO
+ * @param Model|array|string|null $aco ACO
* @param string $action Action
* @return bool true if access is granted, false otherwise
*/
- public function check($aro, $aco, $action = '*')
- {
+ public function check(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool {
$allow = $this->options['policy'];
$prioritizedAros = $this->Aro->roles($aro);
@@ -207,380 +222,3 @@ public function check($aro, $aco, $action = '*')
return $allow;
}
}
-
-/**
- * Access Control Object
- */
-class PhpAco
-{
- /**
- * holds internal ACO representation
- *
- * @var array
- */
- protected $_tree = [];
-
- /**
- * map modifiers for ACO paths to their respective PCRE pattern
- *
- * @var array
- */
- public static $modifiers = [
- '*' => '.*',
- ];
-
- /**
- * Constructor
- *
- * @param array $rules Rules array
- */
- public function __construct(array $rules = [])
- {
- foreach (['allow', 'deny'] as $type) {
- if (empty($rules[$type])) {
- $rules[$type] = [];
- }
- }
-
- $this->build($rules['allow'], $rules['deny']);
- }
-
- /**
- * return path to the requested ACO with allow and deny rules attached on each level
- *
- * @param string $aco ACO string
- * @return array
- */
- public function path($aco)
- {
- $aco = $this->resolve($aco);
- $path = [];
- $level = 0;
- $root = $this->_tree;
- $stack = [[$root, 0]];
-
- while (!empty($stack)) {
- [$root, $level] = array_pop($stack);
-
- if (empty($path[$level])) {
- $path[$level] = [];
- }
-
- foreach ($root as $node => $elements) {
- $pattern = '/^' . str_replace(array_keys(static::$modifiers), array_values(static::$modifiers), $node) . '$/';
-
- if ($node == $aco[$level] || preg_match($pattern, $aco[$level])) {
- // merge allow/denies with $path of current level
- foreach (['allow', 'deny'] as $policy) {
- if (!empty($elements[$policy])) {
- if (empty($path[$level][$policy])) {
- $path[$level][$policy] = [];
- }
- $path[$level][$policy] = array_merge($path[$level][$policy], $elements[$policy]);
- }
- }
-
- // traverse
- if (!empty($elements['children']) && isset($aco[$level + 1])) {
- array_push($stack, [$elements['children'], $level + 1]);
- }
- }
- }
- }
-
- return $path;
- }
-
- /**
- * allow/deny ARO access to ARO
- *
- * @param string $aro ARO string
- * @param string $aco ACO string
- * @param string $action Action string
- * @param string $type access type
- * @return void
- */
- public function access($aro, $aco, $action, $type = 'deny')
- {
- $aco = $this->resolve($aco);
- $depth = count($aco);
- $root = $this->_tree;
- $tree = &$root;
-
- foreach ($aco as $i => $node) {
- if (!isset($tree[$node])) {
- $tree[$node] = [
- 'children' => [],
- ];
- }
-
- if ($i < $depth - 1) {
- $tree = &$tree[$node]['children'];
- } else {
- if (empty($tree[$node][$type])) {
- $tree[$node][$type] = [];
- }
-
- $tree[$node][$type] = array_merge(is_array($aro) ? $aro : [$aro], $tree[$node][$type]);
- }
- }
-
- $this->_tree = &$root;
- }
-
- /**
- * resolve given ACO string to a path
- *
- * @param string $aco ACO string
- * @return array path
- */
- public function resolve($aco)
- {
- if (is_array($aco)) {
- return array_map('strtolower', $aco);
- }
-
- // strip multiple occurrences of '/'
- $aco = preg_replace('#/+#', '/', $aco);
- // make case insensitive
- $aco = ltrim(strtolower($aco), '/');
-
- return array_filter(array_map('trim', explode('/', $aco)));
- }
-
- /**
- * build a tree representation from the given allow/deny informations for ACO paths
- *
- * @param array $allow ACO allow rules
- * @param array $deny ACO deny rules
- * @return void
- */
- public function build(array $allow, array $deny = [])
- {
- $this->_tree = [];
-
- foreach ($allow as $dotPath => $aros) {
- if (is_string($aros)) {
- $aros = array_map('trim', explode(',', $aros));
- }
-
- $this->access($aros, $dotPath, null, 'allow');
- }
-
- foreach ($deny as $dotPath => $aros) {
- if (is_string($aros)) {
- $aros = array_map('trim', explode(',', $aros));
- }
-
- $this->access($aros, $dotPath, null, 'deny');
- }
- }
-}
-
-/**
- * Access Request Object
- */
-class PhpAro
-{
- /**
- * role to resolve to when a provided ARO is not listed in
- * the internal tree
- *
- * @var string
- */
- public const DEFAULT_ROLE = 'Role/default';
-
- /**
- * map external identifiers. E.g. if
- *
- * array('User' => array('username' => 'jeff', 'role' => 'editor'))
- *
- * is passed as an ARO to one of the methods of AclComponent, PhpAcl
- * will check if it can be resolved to an User or a Role defined in the
- * configuration file.
- *
- * @var array
- * @see app/Config/acl.php
- */
- public $map = [
- 'User' => 'User/username',
- 'Role' => 'User/role',
- ];
-
- /**
- * aliases to map
- *
- * @var array
- */
- public $aliases = [];
-
- /**
- * internal ARO representation
- *
- * @var array
- */
- protected $_tree = [];
-
- /**
- * Constructor
- *
- * @param array $aro The aro data
- * @param array $map The identifier mappings
- * @param array $aliases The aliases to map.
- */
- public function __construct(array $aro = [], array $map = [], array $aliases = [])
- {
- if (!empty($map)) {
- $this->map = $map;
- }
-
- $this->aliases = $aliases;
- $this->build($aro);
- }
-
- /**
- * From the perspective of the given ARO, walk down the tree and
- * collect all inherited AROs levelwise such that AROs from different
- * branches with equal distance to the requested ARO will be collected at the same
- * index. The resulting array will contain a prioritized list of (list of) roles ordered from
- * the most distant AROs to the requested one itself.
- *
- * @param array|string $aro An ARO identifier
- * @return array prioritized AROs
- */
- public function roles($aro)
- {
- $aros = [];
- $aro = $this->resolve($aro);
- $stack = [[$aro, 0]];
-
- while (!empty($stack)) {
- [$element, $depth] = array_pop($stack);
- $aros[$depth][] = $element;
-
- foreach ($this->_tree as $node => $children) {
- if (in_array($element, $children)) {
- array_push($stack, [$node, $depth + 1]);
- }
- }
- }
-
- return array_reverse($aros);
- }
-
- /**
- * resolve an ARO identifier to an internal ARO string using
- * the internal mapping information.
- *
- * @param array|string $aro ARO identifier (User.jeff, array('User' => ...), etc)
- * @return string internal aro string (e.g. User/jeff, Role/default)
- */
- public function resolve($aro)
- {
- foreach ($this->map as $aroGroup => $map) {
- [$model, $field] = explode('/', $map, 2);
- $mapped = '';
-
- if (is_array($aro)) {
- if (isset($aro['model']) && isset($aro['foreign_key']) && $aro['model'] === $aroGroup) {
- $mapped = $aroGroup . '/' . $aro['foreign_key'];
- } elseif (isset($aro[$model][$field])) {
- $mapped = $aroGroup . '/' . $aro[$model][$field];
- } elseif (isset($aro[$field])) {
- $mapped = $aroGroup . '/' . $aro[$field];
- }
- } elseif (is_string($aro)) {
- $aro = ltrim($aro, '/');
-
- if (!str_contains($aro, '/')) {
- $mapped = $aroGroup . '/' . $aro;
- } else {
- [$aroModel, $aroValue] = explode('/', $aro, 2);
-
- $aroModel = Inflector::camelize($aroModel);
-
- if ($aroModel === $model || $aroModel === $aroGroup) {
- $mapped = $aroGroup . '/' . $aroValue;
- }
- }
- }
-
- if (isset($this->_tree[$mapped])) {
- return $mapped;
- }
-
- // is there a matching alias defined (e.g. Role/1 => Role/admin)?
- if (!empty($this->aliases[$mapped])) {
- return $this->aliases[$mapped];
- }
- }
-
- return static::DEFAULT_ROLE;
- }
-
- /**
- * adds a new ARO to the tree
- *
- * @param array $aro one or more ARO records
- * @return void
- */
- public function addRole(array $aro)
- {
- foreach ($aro as $role => $inheritedRoles) {
- if (!isset($this->_tree[$role])) {
- $this->_tree[$role] = [];
- }
-
- if (!empty($inheritedRoles)) {
- if (is_string($inheritedRoles)) {
- $inheritedRoles = array_map('trim', explode(',', $inheritedRoles));
- }
-
- foreach ($inheritedRoles as $dependency) {
- // detect cycles
- $roles = $this->roles($dependency);
-
- if (in_array($role, Hash::flatten($roles))) {
- $path = '';
-
- foreach ($roles as $roleDependencies) {
- $path .= implode('|', (array)$roleDependencies) . ' -> ';
- }
-
- trigger_error(__d('cake_dev', 'cycle detected when inheriting %s from %s. Path: %s', $role, $dependency, $path . $role));
- continue;
- }
-
- if (!isset($this->_tree[$dependency])) {
- $this->_tree[$dependency] = [];
- }
-
- $this->_tree[$dependency][] = $role;
- }
- }
- }
- }
-
- /**
- * adds one or more aliases to the internal map. Overwrites existing entries.
- *
- * @param array $alias alias from => to (e.g. Role/13 -> Role/editor)
- * @return void
- */
- public function addAlias(array $alias)
- {
- $this->aliases = $alias + $this->aliases;
- }
-
- /**
- * build an ARO tree structure for internal processing
- *
- * @param array $aros array of AROs as key and their inherited AROs as values
- * @return void
- */
- public function build(array $aros)
- {
- $this->_tree = [];
- $this->addRole($aros);
- }
-}
diff --git a/src/Controller/Component/Acl/PhpAco.php b/src/Controller/Component/Acl/PhpAco.php
new file mode 100644
index 0000000000..630f338450
--- /dev/null
+++ b/src/Controller/Component/Acl/PhpAco.php
@@ -0,0 +1,171 @@
+ '.*',
+ ];
+
+ /**
+ * Constructor
+ *
+ * @param array $rules Rules array
+ */
+ public function __construct(array $rules = [])
+ {
+ foreach (['allow', 'deny'] as $type) {
+ if (empty($rules[$type])) {
+ $rules[$type] = [];
+ }
+ }
+
+ $this->build($rules['allow'], $rules['deny']);
+ }
+
+ /**
+ * return path to the requested ACO with allow and deny rules attached on each level
+ *
+ * @param array|string $aco ACO string
+ * @return array
+ */
+ public function path(array|string $aco): array
+ {
+ $aco = $this->resolve($aco);
+ $path = [];
+ $root = $this->_tree;
+ $stack = [[$root, 0]];
+
+ while (!empty($stack)) {
+ [$root, $level] = array_pop($stack);
+
+ if (empty($path[$level])) {
+ $path[$level] = [];
+ }
+
+ foreach ($root as $node => $elements) {
+ $pattern = '/^' . str_replace(array_keys(static::$modifiers), array_values(static::$modifiers), $node) . '$/';
+
+ if ($node == $aco[$level] || preg_match($pattern, $aco[$level])) {
+ // merge allow/denies with $path of current level
+ foreach (['allow', 'deny'] as $policy) {
+ if (!empty($elements[$policy])) {
+ if (empty($path[$level][$policy])) {
+ $path[$level][$policy] = [];
+ }
+ $path[$level][$policy] = array_merge($path[$level][$policy], $elements[$policy]);
+ }
+ }
+
+ // traverse
+ if (!empty($elements['children']) && isset($aco[$level + 1])) {
+ $stack[] = [$elements['children'], $level + 1];
+ }
+ }
+ }
+ }
+
+ return $path;
+ }
+
+ /**
+ * allow/deny ARO access to ARO
+ *
+ * @param array|string $aro ARO string
+ * @param array|string $aco ACO string
+ * @param string|null $action Action string
+ * @param string $type access type
+ * @return void
+ */
+ public function access(array|string $aro, array|string $aco, ?string $action, string $type = 'deny'): void
+ {
+ $aco = $this->resolve($aco);
+ $depth = count($aco);
+ $root = $this->_tree;
+ $tree = &$root;
+
+ foreach ($aco as $i => $node) {
+ if (!isset($tree[$node])) {
+ $tree[$node] = [
+ 'children' => [],
+ ];
+ }
+
+ if ($i < $depth - 1) {
+ $tree = &$tree[$node]['children'];
+ } else {
+ if (empty($tree[$node][$type])) {
+ $tree[$node][$type] = [];
+ }
+
+ $tree[$node][$type] = array_merge(is_array($aro) ? $aro : [$aro], $tree[$node][$type]);
+ }
+ }
+
+ $this->_tree = &$root;
+ }
+
+ /**
+ * resolve given ACO string to a path
+ *
+ * @param array|string $aco ACO string
+ * @return array path
+ */
+ public function resolve(array|string $aco): array
+ {
+ if (is_array($aco)) {
+ return array_map('strtolower', $aco);
+ }
+
+ // strip multiple occurrences of '/'
+ $aco = preg_replace('#/+#', '/', $aco);
+ // make case insensitive
+ $aco = ltrim(strtolower($aco), '/');
+
+ return array_filter(array_map('trim', explode('/', $aco)));
+ }
+
+ /**
+ * build a tree representation from the given allow/deny informations for ACO paths
+ *
+ * @param array $allow ACO allow rules
+ * @param array $deny ACO deny rules
+ * @return void
+ */
+ public function build(array $allow, array $deny = []): void
+ {
+ $this->_tree = [];
+
+ foreach ($allow as $dotPath => $aros) {
+ if (is_string($aros)) {
+ $aros = array_map('trim', explode(',', $aros));
+ }
+
+ $this->access($aros, $dotPath, null, 'allow');
+ }
+
+ foreach ($deny as $dotPath => $aros) {
+ if (is_string($aros)) {
+ $aros = array_map('trim', explode(',', $aros));
+ }
+
+ $this->access($aros, $dotPath, null, 'deny');
+ }
+ }
+}
diff --git a/src/Controller/Component/Acl/PhpAro.php b/src/Controller/Component/Acl/PhpAro.php
new file mode 100644
index 0000000000..15968de323
--- /dev/null
+++ b/src/Controller/Component/Acl/PhpAro.php
@@ -0,0 +1,214 @@
+ array('username' => 'jeff', 'role' => 'editor'))
+ *
+ * is passed as an ARO to one of the methods of AclComponent, PhpAcl
+ * will check if it can be resolved to an User or a Role defined in the
+ * configuration file.
+ *
+ * @var array
+ * @see app/Config/acl.php
+ */
+ public array $map = [
+ 'User' => 'User/username',
+ 'Role' => 'User/role',
+ ];
+
+ /**
+ * aliases to map
+ *
+ * @var array
+ */
+ public array $aliases = [];
+
+ /**
+ * internal ARO representation
+ *
+ * @var array
+ */
+ protected array $_tree = [];
+
+ /**
+ * Constructor
+ *
+ * @param array $aro The aro data
+ * @param array $map The identifier mappings
+ * @param array $aliases The aliases to map.
+ */
+ public function __construct(array $aro = [], array $map = [], array $aliases = [])
+ {
+ if (!empty($map)) {
+ $this->map = $map;
+ }
+
+ $this->aliases = $aliases;
+ $this->build($aro);
+ }
+
+ /**
+ * From the perspective of the given ARO, walk down the tree and
+ * collect all inherited AROs levelwise such that AROs from different
+ * branches with equal distance to the requested ARO will be collected at the same
+ * index. The resulting array will contain a prioritized list of (list of) roles ordered from
+ * the most distant AROs to the requested one itself.
+ *
+ * @param array|string $aro An ARO identifier
+ * @return array prioritized AROs
+ */
+ public function roles(array|string $aro): array
+ {
+ $aros = [];
+ $aro = $this->resolve($aro);
+ $stack = [[$aro, 0]];
+
+ while (!empty($stack)) {
+ [$element, $depth] = array_pop($stack);
+ $aros[$depth][] = $element;
+
+ foreach ($this->_tree as $node => $children) {
+ if (in_array($element, $children)) {
+ $stack[] = [$node, $depth + 1];
+ }
+ }
+ }
+
+ return array_reverse($aros);
+ }
+
+ /**
+ * resolve an ARO identifier to an internal ARO string using
+ * the internal mapping information.
+ *
+ * @param array|string $aro ARO identifier (User.jeff, array('User' => ...), etc)
+ * @return string internal aro string (e.g. User/jeff, Role/default)
+ */
+ public function resolve(array|string $aro): string
+ {
+ foreach ($this->map as $aroGroup => $map) {
+ [$model, $field] = explode('/', $map, 2);
+ $mapped = '';
+
+ if (is_array($aro)) {
+ if (isset($aro['model']) && isset($aro['foreign_key']) && $aro['model'] === $aroGroup) {
+ $mapped = $aroGroup . '/' . $aro['foreign_key'];
+ } elseif (isset($aro[$model][$field])) {
+ $mapped = $aroGroup . '/' . $aro[$model][$field];
+ } elseif (isset($aro[$field])) {
+ $mapped = $aroGroup . '/' . $aro[$field];
+ }
+ } elseif (is_string($aro)) {
+ $aro = ltrim($aro, '/');
+
+ if (!str_contains($aro, '/')) {
+ $mapped = $aroGroup . '/' . $aro;
+ } else {
+ [$aroModel, $aroValue] = explode('/', $aro, 2);
+
+ $aroModel = Inflector::camelize($aroModel);
+
+ if ($aroModel === $model || $aroModel === $aroGroup) {
+ $mapped = $aroGroup . '/' . $aroValue;
+ }
+ }
+ }
+
+ if (isset($this->_tree[$mapped])) {
+ return $mapped;
+ }
+
+ // is there a matching alias defined (e.g. Role/1 => Role/admin)?
+ if (!empty($this->aliases[$mapped])) {
+ return $this->aliases[$mapped];
+ }
+ }
+
+ return static::DEFAULT_ROLE;
+ }
+
+ /**
+ * adds a new ARO to the tree
+ *
+ * @param array $aro one or more ARO records
+ * @return void
+ */
+ public function addRole(array $aro): void
+ {
+ foreach ($aro as $role => $inheritedRoles) {
+ if (!isset($this->_tree[$role])) {
+ $this->_tree[$role] = [];
+ }
+
+ if (!empty($inheritedRoles)) {
+ if (is_string($inheritedRoles)) {
+ $inheritedRoles = array_map('trim', explode(',', $inheritedRoles));
+ }
+
+ foreach ($inheritedRoles as $dependency) {
+ // detect cycles
+ $roles = $this->roles($dependency);
+
+ if (in_array($role, Hash::flatten($roles))) {
+ $path = '';
+
+ foreach ($roles as $roleDependencies) {
+ $path .= implode('|', (array)$roleDependencies) . ' -> ';
+ }
+
+ trigger_error(__d('cake_dev', 'cycle detected when inheriting %s from %s. Path: %s', $role, $dependency, $path . $role));
+ continue;
+ }
+
+ if (!isset($this->_tree[$dependency])) {
+ $this->_tree[$dependency] = [];
+ }
+
+ $this->_tree[$dependency][] = $role;
+ }
+ }
+ }
+ }
+
+ /**
+ * adds one or more aliases to the internal map. Overwrites existing entries.
+ *
+ * @param array $alias alias from => to (e.g. Role/13 -> Role/editor)
+ * @return void
+ */
+ public function addAlias(array $alias): void
+ {
+ $this->aliases = $alias + $this->aliases;
+ }
+
+ /**
+ * build an ARO tree structure for internal processing
+ *
+ * @param array $aros array of AROs as key and their inherited AROs as values
+ * @return void
+ */
+ public function build(array $aros): void
+ {
+ $this->_tree = [];
+ $this->addRole($aros);
+ }
+}
diff --git a/src/Controller/Component/AclComponent.php b/src/Controller/Component/AclComponent.php
index f14aff28dc..5dc648ea99 100644
--- a/src/Controller/Component/AclComponent.php
+++ b/src/Controller/Component/AclComponent.php
@@ -18,10 +18,13 @@
use Cake\Controller\Component;
use Cake\Controller\Component\Acl\AclInterface;
+use Cake\Controller\Component\Acl\PhpAco;
+use Cake\Controller\Component\Acl\PhpAro;
use Cake\Controller\ComponentCollection;
use Cake\Core\App;
use Cake\Core\Configure;
use Cake\Error\CakeException;
+use Cake\Model\Model;
/**
* Access Control List factory class.
@@ -38,23 +41,23 @@ class AclComponent extends Component
/**
* Instance of an ACL class
*
- * @var AclInterface
+ * @var AclInterface|null
*/
- protected $_Instance = null;
+ protected ?AclInterface $_Instance = null;
/**
* Aro object.
*
- * @var string
+ * @var PhpAro|Model|null
*/
- public $Aro;
+ public PhpAro|Model|null $Aro = null;
/**
* Aco object
*
- * @var string
+ * @var PhpAco|Model|null
*/
- public $Aco;
+ public PhpAco|Model|null $Aco = null;
/**
* Constructor. Will return an instance of the correct ACL class as defined in `Configure::read('Acl.classname')`
@@ -63,7 +66,7 @@ class AclComponent extends Component
* @param array $settings Settings list.
* @throws CakeException when Acl.classname could not be loaded.
*/
- public function __construct(ComponentCollection $collection, $settings = [])
+ public function __construct(ComponentCollection $collection, array $settings = [])
{
parent::__construct($collection, $settings);
$name = Configure::read('Acl.classname');
@@ -86,11 +89,11 @@ public function __construct(ComponentCollection $collection, $settings = [])
*
* Will call the initialize method on the adapter if setting a new one.
*
- * @param AclInterface|string $adapter Instance of AclInterface or a string name of the class to use. (optional)
+ * @param AclInterface|string|null $adapter Instance of AclInterface or a string name of the class to use. (optional)
* @return AclInterface|null Either null, or the adapter implementation.
* @throws CakeException when the given class is not an instance of AclInterface
*/
- public function adapter($adapter = null)
+ public function adapter(AclInterface|string|null $adapter = null): ?AclInterface
{
if ($adapter) {
if (is_string($adapter)) {
@@ -112,13 +115,16 @@ public function adapter($adapter = null)
* Pass-thru function for ACL check instance. Check methods
* are used to check whether or not an ARO can access an ACO
*
- * @param Model|array|string $aro ARO The requesting object identifier. See `AclNode::node()` for possible formats
- * @param Model|array|string $aco ACO The controlled object identifier. See `AclNode::node()` for possible formats
+ * @param Model|array|string|null $aro ARO The requesting object identifier. See `AclNode::node()` for possible formats
+ * @param Model|array|string|null $aco ACO The controlled object identifier. See `AclNode::node()` for possible formats
* @param string $action Action (defaults to *)
* @return bool Success
*/
- public function check($aro, $aco, $action = '*')
- {
+ public function check(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool {
return $this->_Instance->check($aro, $aco, $action);
}
@@ -126,13 +132,16 @@ public function check($aro, $aco, $action = '*')
* Pass-thru function for ACL allow instance. Allow methods
* are used to grant an ARO access to an ACO.
*
- * @param Model|array|string $aro ARO The requesting object identifier. See `AclNode::node()` for possible formats
- * @param Model|array|string $aco ACO The controlled object identifier. See `AclNode::node()` for possible formats
- * @param string $action Action (defaults to *)
+ * @param Model|array|string|null $aro ARO The requesting object identifier. See `AclNode::node()` for possible formats
+ * @param Model|array|string|null $aco ACO The controlled object identifier. See `AclNode::node()` for possible formats
+ * @param array|string $action Action (defaults to *)
* @return bool Success
*/
- public function allow($aro, $aco, $action = '*')
- {
+ public function allow(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ array|string $action = '*',
+ ): bool {
return $this->_Instance->allow($aro, $aco, $action);
}
@@ -140,13 +149,16 @@ public function allow($aro, $aco, $action = '*')
* Pass-thru function for ACL deny instance. Deny methods
* are used to remove permission from an ARO to access an ACO.
*
- * @param Model|array|string $aro ARO The requesting object identifier. See `AclNode::node()` for possible formats
- * @param Model|array|string $aco ACO The controlled object identifier. See `AclNode::node()` for possible formats
+ * @param Model|array|string|null $aro ARO The requesting object identifier. See `AclNode::node()` for possible formats
+ * @param Model|array|string|null $aco ACO The controlled object identifier. See `AclNode::node()` for possible formats
* @param string $action Action (defaults to *)
* @return bool Success
*/
- public function deny($aro, $aco, $action = '*')
- {
+ public function deny(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool {
return $this->_Instance->deny($aro, $aco, $action);
}
@@ -154,27 +166,33 @@ public function deny($aro, $aco, $action = '*')
* Pass-thru function for ACL inherit instance. Inherit methods
* modify the permission for an ARO to be that of its parent object.
*
- * @param Model|array|string $aro ARO The requesting object identifier. See `AclNode::node()` for possible formats
- * @param Model|array|string $aco ACO The controlled object identifier. See `AclNode::node()` for possible formats
+ * @param Model|array|string|null $aro ARO The requesting object identifier. See `AclNode::node()` for possible formats
+ * @param Model|array|string|null $aco ACO The controlled object identifier. See `AclNode::node()` for possible formats
* @param string $action Action (defaults to *)
* @return bool Success
*/
- public function inherit($aro, $aco, $action = '*')
- {
+ public function inherit(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool {
return $this->_Instance->inherit($aro, $aco, $action);
}
/**
* Pass-thru function for ACL grant instance. An alias for AclComponent::allow()
*
- * @param Model|array|string $aro ARO The requesting object identifier. See `AclNode::node()` for possible formats
- * @param Model|array|string $aco ACO The controlled object identifier. See `AclNode::node()` for possible formats
+ * @param Model|array|string|null $aro ARO The requesting object identifier. See `AclNode::node()` for possible formats
+ * @param Model|array|string|null $aco ACO The controlled object identifier. See `AclNode::node()` for possible formats
* @param string $action Action (defaults to *)
* @return bool Success
* @deprecated 3.0.0 Will be removed in 3.0.
*/
- public function grant($aro, $aco, $action = '*')
- {
+ public function grant(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool {
trigger_error(__d('cake_dev', '%s is deprecated, use %s instead', 'AclComponent::grant()', 'allow()'), E_USER_WARNING);
return $this->_Instance->allow($aro, $aco, $action);
@@ -183,14 +201,17 @@ public function grant($aro, $aco, $action = '*')
/**
* Pass-thru function for ACL grant instance. An alias for AclComponent::deny()
*
- * @param Model|array|string $aro ARO The requesting object identifier. See `AclNode::node()` for possible formats
- * @param Model|array|string $aco ACO The controlled object identifier. See `AclNode::node()` for possible formats
+ * @param Model|array|string|null $aro ARO The requesting object identifier. See `AclNode::node()` for possible formats
+ * @param Model|array|string|null $aco ACO The controlled object identifier. See `AclNode::node()` for possible formats
* @param string $action Action (defaults to *)
* @return bool Success
* @deprecated 3.0.0 Will be removed in 3.0.
*/
- public function revoke($aro, $aco, $action = '*')
- {
+ public function revoke(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool {
trigger_error(__d('cake_dev', '%s is deprecated, use %s instead', 'AclComponent::revoke()', 'deny()'), E_USER_WARNING);
return $this->_Instance->deny($aro, $aco, $action);
diff --git a/src/Controller/Component/Auth/AbstractPasswordHasher.php b/src/Controller/Component/Auth/AbstractPasswordHasher.php
index 209ae62d46..bb8477d0f9 100644
--- a/src/Controller/Component/Auth/AbstractPasswordHasher.php
+++ b/src/Controller/Component/Auth/AbstractPasswordHasher.php
@@ -28,14 +28,14 @@ abstract class AbstractPasswordHasher
*
* @var array
*/
- protected $_config = [];
+ protected array $_config = [];
/**
* Constructor
*
* @param array $config Array of config.
*/
- public function __construct($config = [])
+ public function __construct(array $config = [])
{
$this->config($config);
}
@@ -43,10 +43,10 @@ public function __construct($config = [])
/**
* Get/Set the config
*
- * @param array $config Sets config, if null returns existing config
+ * @param array|null $config Sets config, if null returns existing config
* @return array Returns configs
*/
- public function config($config = null)
+ public function config(?array $config = null)
{
if (is_array($config)) {
$this->_config = array_merge($this->_config, $config);
@@ -58,19 +58,19 @@ public function config($config = null)
/**
* Generates password hash.
*
- * @param array|string $password Plain text password to hash or array of data
+ * @param string $password Plain text password to hash or array of data
* required to generate password hash.
* @return string Password hash
*/
- abstract public function hash($password);
+ abstract public function hash(string $password): string;
/**
* Check hash. Generate hash from user provided password string or data array
* and check against existing hash.
*
- * @param array|string $password Plain text password to hash or data array.
+ * @param string $password Plain text password to hash or data array.
* @param string $hashedPassword Existing hashed password.
* @return bool True if hashes match else false.
*/
- abstract public function check($password, $hashedPassword);
+ abstract public function check(string $password, string $hashedPassword): bool;
}
diff --git a/src/Controller/Component/Auth/ActionsAuthorize.php b/src/Controller/Component/Auth/ActionsAuthorize.php
index 7b89fe5495..5e3340f29a 100644
--- a/src/Controller/Component/Auth/ActionsAuthorize.php
+++ b/src/Controller/Component/Auth/ActionsAuthorize.php
@@ -14,6 +14,7 @@
namespace Cake\Controller\Component\Auth;
+use Cake\Controller\Component\AclComponent;
use Cake\Network\CakeRequest;
/**
@@ -34,11 +35,12 @@ class ActionsAuthorize extends BaseAuthorize
* @param CakeRequest $request The request needing authorization.
* @return bool
*/
- public function authorize($user, CakeRequest $request)
+ public function authorize(array $user, CakeRequest $request): bool
{
- $Acl = $this->_Collection->load('Acl');
+ /** @var AclComponent $acl */
+ $acl = $this->_Collection->load('Acl');
$user = [$this->settings['userModel'] => $user];
- return $Acl->check($user, $this->action($request));
+ return $acl->check($user, $this->action($request));
}
}
diff --git a/src/Controller/Component/Auth/BaseAuthenticate.php b/src/Controller/Component/Auth/BaseAuthenticate.php
index f1667fc8e0..470eb08cf9 100644
--- a/src/Controller/Component/Auth/BaseAuthenticate.php
+++ b/src/Controller/Component/Auth/BaseAuthenticate.php
@@ -18,6 +18,7 @@
use Cake\Core\App;
use Cake\Error\CakeException;
use Cake\Event\CakeEventListener;
+use Cake\Model\Model;
use Cake\Network\CakeRequest;
use Cake\Network\CakeResponse;
use Cake\Utility\ClassRegistry;
@@ -47,7 +48,7 @@ abstract class BaseAuthenticate implements CakeEventListener
*
* @var array
*/
- public $settings = [
+ public array $settings = [
'fields' => [
'username' => 'username',
'password' => 'password',
@@ -65,14 +66,14 @@ abstract class BaseAuthenticate implements CakeEventListener
*
* @var ComponentCollection
*/
- protected $_Collection;
+ protected ComponentCollection $_Collection;
/**
* Password hasher instance.
*
- * @var AbstractPasswordHasher
+ * @var AbstractPasswordHasher|null
*/
- protected $_passwordHasher;
+ protected ?AbstractPasswordHasher $_passwordHasher = null;
/**
* Implemented events
@@ -90,7 +91,7 @@ public function implementedEvents(): array
* @param ComponentCollection $collection The Component collection used on this request.
* @param array $settings Array of settings to use.
*/
- public function __construct(ComponentCollection $collection, $settings)
+ public function __construct(ComponentCollection $collection, array $settings)
{
$this->_Collection = $collection;
$this->settings = Hash::merge($this->settings, $settings);
@@ -107,11 +108,13 @@ public function __construct(ComponentCollection $collection, $settings)
* helps mitigate timing attacks that are attempting to find valid usernames.
*
* @param array|string $username The username/identifier, or an array of find conditions.
- * @param string $password The password, only used if $username param is string.
- * @return array|bool Either false on failure, or an array of user data.
+ * @param string|null $password The password, only used if $username param is string.
+ * @return array|false Either false on failure, or an array of user data.
*/
- protected function _findUser($username, $password = null)
- {
+ protected function _findUser(
+ array|string $username,
+ ?string $password = null,
+ ): array|false {
$userModel = $this->settings['userModel'];
[, $model] = pluginSplit($userModel);
$fields = $this->settings['fields'];
@@ -133,7 +136,9 @@ protected function _findUser($username, $password = null)
$userFields[] = $model . '.' . $fields['password'];
}
- $result = ClassRegistry::init($userModel)->find('first', [
+ /** @var Model $_model */
+ $_model = ClassRegistry::init($userModel);
+ $result = $_model->find('first', [
'conditions' => $conditions,
'recursive' => $this->settings['recursive'],
'fields' => $userFields,
@@ -165,7 +170,7 @@ protected function _findUser($username, $password = null)
* @throws CakeException If password hasher class not found or
* it does not extend AbstractPasswordHasher
*/
- public function passwordHasher()
+ public function passwordHasher(): AbstractPasswordHasher
{
if ($this->_passwordHasher) {
return $this->_passwordHasher;
@@ -200,7 +205,7 @@ public function passwordHasher()
* @return string The hashed form of the password.
* @deprecated 3.0.0 Since 2.4. Use a PasswordHasher class instead.
*/
- protected function _password($password)
+ protected function _password(string $password): string
{
return Security::hash($password, null, true);
}
@@ -210,9 +215,12 @@ protected function _password($password)
*
* @param CakeRequest $request Request to get authentication information from.
* @param CakeResponse $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(CakeRequest $request, CakeResponse $response);
+ abstract public function authenticate(
+ CakeRequest $request,
+ CakeResponse $response,
+ ): array|false;
/**
* Allows you to hook into AuthComponent::logout(),
@@ -221,10 +229,10 @@ abstract public function authenticate(CakeRequest $request, CakeResponse $respon
* All attached authentication objects will have this method
* called when a user logs out.
*
- * @param array $user The user about to be logged out.
+ * @param array|null $user The user about to be logged out.
* @return void
*/
- public function logout($user)
+ public function logout(?array $user): void
{
}
@@ -233,9 +241,9 @@ public function logout($user)
* systems like basic and digest auth.
*
* @param CakeRequest $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(CakeRequest $request)
+ public function getUser(CakeRequest $request): array|false
{
return false;
}
@@ -245,10 +253,11 @@ public function getUser(CakeRequest $request)
*
* @param CakeRequest $request A request object.
* @param CakeResponse $response A response object.
- * @return mixed Either true to indicate the unauthenticated request has been
+ * @return bool|null Either true to indicate the unauthenticated request has been
* dealt with and no more action is required by AuthComponent or void (default).
*/
- public function unauthenticated(CakeRequest $request, CakeResponse $response)
+ public function unauthenticated(CakeRequest $request, CakeResponse $response): ?bool
{
+ return null;
}
}
diff --git a/src/Controller/Component/Auth/BaseAuthorize.php b/src/Controller/Component/Auth/BaseAuthorize.php
index 2518660922..c11445c2c4 100644
--- a/src/Controller/Component/Auth/BaseAuthorize.php
+++ b/src/Controller/Component/Auth/BaseAuthorize.php
@@ -33,16 +33,16 @@ abstract class BaseAuthorize
/**
* Controller for the request.
*
- * @var Controller
+ * @var Controller|null
*/
- protected $_Controller = null;
+ protected ?Controller $_Controller = null;
/**
* Component collection instance for getting more components.
*
* @var ComponentCollection
*/
- protected $_Collection;
+ protected ComponentCollection $_Collection;
/**
* Settings for authorize objects.
@@ -54,7 +54,7 @@ abstract class BaseAuthorize
*
* @var array
*/
- public $settings = [
+ public array $settings = [
'actionPath' => null,
'actionMap' => [
'index' => 'read',
@@ -71,9 +71,9 @@ abstract class BaseAuthorize
* Constructor
*
* @param ComponentCollection $collection The controller for this request.
- * @param string $settings An array of settings. This class does not use any settings.
+ * @param array $settings An array of settings. This class does not use any settings.
*/
- public function __construct(ComponentCollection $collection, $settings = [])
+ public function __construct(ComponentCollection $collection, array $settings = [])
{
$this->_Collection = $collection;
$controller = $collection->getController();
@@ -88,21 +88,18 @@ public function __construct(ComponentCollection $collection, $settings = [])
* @param CakeRequest $request Request instance.
* @return bool
*/
- abstract public function authorize($user, CakeRequest $request);
+ abstract public function authorize(array $user, CakeRequest $request): bool;
/**
* Accessor to the controller object.
*
- * @param Controller $controller null to get, a controller to set.
- * @return mixed
+ * @param Controller|null $controller null to get, a controller to set.
+ * @return Controller|bool|null
* @throws CakeException
*/
- public function controller(?Controller $controller = null)
+ public function controller(?Controller $controller = null): Controller|bool|null
{
if ($controller) {
- if (!$controller instanceof Controller) {
- throw new CakeException(__d('cake_dev', '$controller needs to be an instance of Controller'));
- }
$this->_Controller = $controller;
return true;
@@ -119,8 +116,10 @@ public function controller(?Controller $controller = null)
* @param string $path Path format.
* @return string the action path for the given request.
*/
- public function action(CakeRequest $request, $path = '/:plugin/:controller/:action')
- {
+ public function action(
+ CakeRequest $request,
+ string $path = '/:plugin/:controller/:action',
+ ): string {
$plugin = empty($request['plugin']) ? null : Inflector::camelize($request['plugin']) . '/';
$path = str_replace(
[':controller', ':action', ':plugin/'],
@@ -160,10 +159,10 @@ public function action(CakeRequest $request, $path = '/:plugin/:controller/:acti
* create a custom admin CRUD operation for administration functions similarly if needed.
*
* @param array $map Either an array of mappings, or undefined to get current values.
- * @return mixed Either the current mappings or null when setting.
+ * @return array|null Either the current mappings or null when setting.
* @see AuthComponent::mapActions()
*/
- public function mapActions($map = [])
+ public function mapActions(array $map = []): ?array
{
if (empty($map)) {
return $this->settings['actionMap'];
@@ -177,5 +176,7 @@ public function mapActions($map = [])
$this->settings['actionMap'][$action] = $type;
}
}
+
+ return null;
}
}
diff --git a/src/Controller/Component/Auth/BasicAuthenticate.php b/src/Controller/Component/Auth/BasicAuthenticate.php
index 29ec8e6a00..ed625d61ce 100644
--- a/src/Controller/Component/Auth/BasicAuthenticate.php
+++ b/src/Controller/Component/Auth/BasicAuthenticate.php
@@ -61,7 +61,7 @@ class BasicAuthenticate extends BaseAuthenticate
* @param ComponentCollection $collection The Component collection used on this request.
* @param array $settings An array of settings.
*/
- public function __construct(ComponentCollection $collection, $settings)
+ public function __construct(ComponentCollection $collection, array $settings)
{
parent::__construct($collection, $settings);
if (empty($this->settings['realm'])) {
@@ -75,9 +75,9 @@ public function __construct(ComponentCollection $collection, $settings)
*
* @param CakeRequest $request The request to authenticate with.
* @param CakeResponse $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(CakeRequest $request, CakeResponse $response)
+ public function authenticate(CakeRequest $request, CakeResponse $response): array|false
{
return $this->getUser($request);
}
@@ -86,9 +86,9 @@ public function authenticate(CakeRequest $request, CakeResponse $response)
* Get a user based on information in the request. Used by cookie-less auth for stateless clients.
*
* @param CakeRequest $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(CakeRequest $request)
+ public function getUser(CakeRequest $request): array|false
{
$username = env('PHP_AUTH_USER');
$pass = env('PHP_AUTH_PW');
@@ -111,10 +111,10 @@ public function getUser(CakeRequest $request)
*
* @param CakeRequest $request A request object.
* @param CakeResponse $response A response object.
- * @return void
+ * @return bool|null
* @throws UnauthorizedException
*/
- public function unauthenticated(CakeRequest $request, CakeResponse $response)
+ public function unauthenticated(CakeRequest $request, CakeResponse $response): ?bool
{
$Exception = new UnauthorizedException();
$Exception->responseHeader([$this->loginHeaders()]);
@@ -126,7 +126,7 @@ public function unauthenticated(CakeRequest $request, CakeResponse $response)
*
* @return string Headers for logging in.
*/
- public function loginHeaders()
+ public function loginHeaders(): string
{
return sprintf('WWW-Authenticate: Basic realm="%s"', $this->settings['realm']);
}
diff --git a/src/Controller/Component/Auth/BlowfishPasswordHasher.php b/src/Controller/Component/Auth/BlowfishPasswordHasher.php
index 2b1d1f80cd..a2e8b5f058 100644
--- a/src/Controller/Component/Auth/BlowfishPasswordHasher.php
+++ b/src/Controller/Component/Auth/BlowfishPasswordHasher.php
@@ -31,7 +31,7 @@ class BlowfishPasswordHasher extends AbstractPasswordHasher
* @return string Password hash
* @link https://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#using-bcrypt-for-passwords
*/
- public function hash($password)
+ public function hash(string $password): string
{
return Security::hash($password, 'blowfish', false);
}
@@ -43,7 +43,7 @@ public function hash($password)
* @param string $hashedPassword Existing hashed password.
* @return bool True if hashes match else false.
*/
- public function check($password, $hashedPassword)
+ public function check(string $password, string $hashedPassword): bool
{
return $hashedPassword === Security::hash($password, 'blowfish', $hashedPassword);
}
diff --git a/src/Controller/Component/Auth/ControllerAuthorize.php b/src/Controller/Component/Auth/ControllerAuthorize.php
index 40cbed7554..d8aafb1fc5 100644
--- a/src/Controller/Component/Auth/ControllerAuthorize.php
+++ b/src/Controller/Component/Auth/ControllerAuthorize.php
@@ -43,11 +43,11 @@ class ControllerAuthorize extends BaseAuthorize
/**
* Get/set the controller this authorize object will be working with. Also checks that isAuthorized is implemented.
*
- * @param Controller $controller null to get, a controller to set.
- * @return mixed
+ * @param Controller|null $controller null to get, a controller to set.
+ * @return Controller|bool
* @throws CakeException
*/
- public function controller(?Controller $controller = null)
+ public function controller(?Controller $controller = null): Controller|bool
{
if ($controller) {
if (!method_exists($controller, 'isAuthorized')) {
@@ -65,8 +65,8 @@ public function controller(?Controller $controller = null)
* @param CakeRequest $request Request instance.
* @return bool
*/
- public function authorize($user, CakeRequest $request)
+ public function authorize(array $user, CakeRequest $request): bool
{
- return (bool)$this->_Controller->isAuthorized($user);
+ return method_exists($this->_Controller, 'isAuthorized') && $this->_Controller->isAuthorized($user);
}
}
diff --git a/src/Controller/Component/Auth/CrudAuthorize.php b/src/Controller/Component/Auth/CrudAuthorize.php
index af980d6440..ffaab5c03b 100644
--- a/src/Controller/Component/Auth/CrudAuthorize.php
+++ b/src/Controller/Component/Auth/CrudAuthorize.php
@@ -14,6 +14,7 @@
namespace Cake\Controller\Component\Auth;
+use Cake\Controller\Component\AclComponent;
use Cake\Controller\ComponentCollection;
use Cake\Network\CakeRequest;
use Cake\Routing\Router;
@@ -39,9 +40,9 @@ class CrudAuthorize extends BaseAuthorize
* Sets up additional actionMap values that match the configured `Routing.prefixes`.
*
* @param ComponentCollection $collection The component collection from the controller.
- * @param string $settings An array of settings. This class does not use any settings.
+ * @param array $settings An array of settings. This class does not use any settings.
*/
- public function __construct(ComponentCollection $collection, $settings = [])
+ public function __construct(ComponentCollection $collection, array $settings = [])
{
parent::__construct($collection, $settings);
$this->_setPrefixMappings();
@@ -52,7 +53,7 @@ public function __construct(ComponentCollection $collection, $settings = [])
*
* @return void
*/
- protected function _setPrefixMappings()
+ protected function _setPrefixMappings(): void
{
$crud = ['create', 'read', 'update', 'delete'];
$map = array_combine($crud, $crud);
@@ -83,7 +84,7 @@ protected function _setPrefixMappings()
* @param CakeRequest $request The request needing authorization.
* @return bool
*/
- public function authorize($user, CakeRequest $request)
+ public function authorize(array $user, CakeRequest $request): bool
{
if (!isset($this->settings['actionMap'][$request->params['action']])) {
trigger_error(
@@ -99,9 +100,10 @@ public function authorize($user, CakeRequest $request)
return false;
}
$user = [$this->settings['userModel'] => $user];
- $Acl = $this->_Collection->load('Acl');
+ /** @var AclComponent $acl */
+ $acl = $this->_Collection->load('Acl');
- return $Acl->check(
+ return $acl->check(
$user,
$this->action($request, ':controller'),
$this->settings['actionMap'][$request->params['action']],
diff --git a/src/Controller/Component/Auth/DigestAuthenticate.php b/src/Controller/Component/Auth/DigestAuthenticate.php
index ec7bc70889..7da061b869 100644
--- a/src/Controller/Component/Auth/DigestAuthenticate.php
+++ b/src/Controller/Component/Auth/DigestAuthenticate.php
@@ -76,7 +76,7 @@ class DigestAuthenticate extends BasicAuthenticate
*
* @var array
*/
- public $settings = [
+ public array $settings = [
'fields' => [
'username' => 'username',
'password' => 'password',
@@ -114,9 +114,9 @@ public function __construct(ComponentCollection $collection, $settings)
* Get a user based on information in the request. Used by cookie-less auth for stateless clients.
*
* @param CakeRequest $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(CakeRequest $request)
+ public function getUser(CakeRequest $request): array|false
{
$digest = $this->_getDigest();
if (empty($digest)) {
@@ -144,7 +144,7 @@ public function getUser(CakeRequest $request)
*
* @return array|bool|null Array of digest information.
*/
- protected function _getDigest()
+ protected function _getDigest(): array|bool|null
{
$digest = env('PHP_AUTH_DIGEST');
if (empty($digest) && function_exists('apache_request_headers')) {
@@ -166,7 +166,7 @@ protected function _getDigest()
* @param string $digest The raw digest authentication headers.
* @return array|null An array of digest authentication headers
*/
- public function parseAuthData($digest)
+ public function parseAuthData(string $digest): ?array
{
if (str_starts_with($digest, 'Digest ')) {
$digest = substr($digest, 7);
@@ -194,7 +194,7 @@ public function parseAuthData($digest)
* @param string $password The digest hash password generated with DigestAuthenticate::password()
* @return string Response hash
*/
- public function generateResponseHash($digest, $password)
+ public function generateResponseHash(array $digest, string $password): string
{
return md5(
$password .
@@ -211,8 +211,11 @@ public function generateResponseHash($digest, $password)
* @param string $realm The realm the password is for.
* @return string the hashed password that can later be used with Digest authentication.
*/
- public static function password($username, $password, $realm)
- {
+ public static function password(
+ string $username,
+ string $password,
+ string $realm,
+ ): string {
return md5($username . ':' . $realm . ':' . $password);
}
@@ -221,7 +224,7 @@ public static function password($username, $password, $realm)
*
* @return string Headers for logging in.
*/
- public function loginHeaders()
+ public function loginHeaders(): string
{
$options = [
'realm' => $this->settings['realm'],
diff --git a/src/Controller/Component/Auth/FormAuthenticate.php b/src/Controller/Component/Auth/FormAuthenticate.php
index a413cebcc6..34ffcb3dd0 100644
--- a/src/Controller/Component/Auth/FormAuthenticate.php
+++ b/src/Controller/Component/Auth/FormAuthenticate.php
@@ -46,7 +46,7 @@ class FormAuthenticate extends BaseAuthenticate
* @param array $fields The fields to be checked.
* @return bool False if the fields have not been supplied. True if they exist.
*/
- protected function _checkFields(CakeRequest $request, $model, $fields)
+ protected function _checkFields(CakeRequest $request, string $model, array $fields): bool
{
if (empty($request->data[$model])) {
return false;
@@ -68,9 +68,9 @@ protected function _checkFields(CakeRequest $request, $model, $fields)
*
* @param CakeRequest $request The request that contains login information.
* @param CakeResponse $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(CakeRequest $request, CakeResponse $response)
+ public function authenticate(CakeRequest $request, CakeResponse $response): array|false
{
$userModel = $this->settings['userModel'];
[, $model] = pluginSplit($userModel);
diff --git a/src/Controller/Component/Auth/SimplePasswordHasher.php b/src/Controller/Component/Auth/SimplePasswordHasher.php
index f84d468fb4..219440fd8b 100644
--- a/src/Controller/Component/Auth/SimplePasswordHasher.php
+++ b/src/Controller/Component/Auth/SimplePasswordHasher.php
@@ -29,7 +29,7 @@ class SimplePasswordHasher extends AbstractPasswordHasher
*
* @var array
*/
- protected $_config = ['hashType' => null];
+ protected array $_config = ['hashType' => null];
/**
* Generates password hash.
@@ -38,7 +38,7 @@ class SimplePasswordHasher extends AbstractPasswordHasher
* @return string Password hash
* @link https://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#hashing-passwords
*/
- public function hash($password)
+ public function hash(string $password): string
{
return Security::hash($password, $this->_config['hashType'], true);
}
@@ -50,7 +50,7 @@ public function hash($password)
* @param string $hashedPassword Existing hashed password.
* @return bool True if hashes match else false.
*/
- public function check($password, $hashedPassword)
+ public function check(string $password, string $hashedPassword): bool
{
return $hashedPassword === $this->hash($password);
}
diff --git a/src/Controller/Component/AuthComponent.php b/src/Controller/Component/AuthComponent.php
index d9b3cb4c0a..2e9e4e4e65 100644
--- a/src/Controller/Component/AuthComponent.php
+++ b/src/Controller/Component/AuthComponent.php
@@ -21,6 +21,8 @@
namespace Cake\Controller\Component;
use Cake\Controller\Component;
+use Cake\Controller\Component\Auth\BaseAuthenticate;
+use Cake\Controller\Component\Auth\BaseAuthorize;
use Cake\Controller\Controller;
use Cake\Core\App;
use Cake\Core\Configure;
@@ -42,6 +44,9 @@
*
* @package Cake.Controller.Component
* @link https://book.cakephp.org/2.0/en/core-libraries/components/authentication.html
+ * @property SessionComponent $Session
+ * @property FlashComponent $Flash
+ * @property RequestHandlerComponent $RequestHandler
*/
class AuthComponent extends Component
{
@@ -57,7 +62,11 @@ class AuthComponent extends Component
*
* @var array
*/
- public array $components = ['Session', 'Flash', 'RequestHandler'];
+ public array $components = [
+ 'Session',
+ 'Flash',
+ 'RequestHandler',
+ ];
/**
* An array of authentication objects to use for authenticating users. You can configure
@@ -88,17 +97,17 @@ class AuthComponent extends Component
*
* You can also use AuthComponent::ALL instead of the string 'all'.
*
- * @var array
+ * @var array|string
* @link https://book.cakephp.org/2.0/en/core-libraries/components/authentication.html
*/
- public $authenticate = ['Form'];
+ public array|string $authenticate = ['Form'];
/**
* Objects that will be used for authentication checks.
*
* @var array
*/
- protected $_authenticateObjects = [];
+ protected array $_authenticateObjects = [];
/**
* An array of authorization objects to use for authorizing users. You can configure
@@ -128,25 +137,25 @@ class AuthComponent extends Component
*
* You can also use AuthComponent::ALL instead of the string 'all'
*
- * @var mixed
+ * @var array|string|false|null
* @link https://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#authorization
*/
- public $authorize = false;
+ public array|string|false|null $authorize = false;
/**
* Objects that will be used for authorization checks.
*
* @var array
*/
- protected $_authorizeObjects = [];
+ protected array $_authorizeObjects = [];
/**
* The name of an optional view element to render when an Ajax request is made
* with an invalid or expired session
*
- * @var string
+ * @var string|null
*/
- public $ajaxLogin = null;
+ public ?string $ajaxLogin = null;
/**
* Settings to use when Auth needs to do a flash message with SessionComponent::setFlash().
@@ -158,7 +167,7 @@ class AuthComponent extends Component
*
* @var array
*/
- public $flash = [
+ public array $flash = [
'element' => 'default',
'key' => 'auth',
'params' => [],
@@ -171,7 +180,7 @@ class AuthComponent extends Component
*
* @var string
*/
- public static $sessionKey = 'Auth.User';
+ public static string $sessionKey = 'Auth.User';
/**
* The current user, used for stateless authentication when
@@ -179,15 +188,15 @@ class AuthComponent extends Component
*
* @var array
*/
- protected static $_user = [];
+ protected static array $_user = [];
/**
* A URL (defined as a string or array) to the controller action that handles
* logins. Defaults to `/users/login`.
*
- * @var mixed
+ * @var array|string
*/
- public $loginAction = [
+ public array|string $loginAction = [
'controller' => 'users',
'action' => 'login',
'plugin' => null,
@@ -199,30 +208,30 @@ class AuthComponent extends Component
* redirected back after a successful login. If this session value is not
* set, redirectUrl() method will return the URL specified in $loginRedirect.
*
- * @var mixed
+ * @var array|string|null
* @link https://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#AuthComponent::$loginRedirect
*/
- public $loginRedirect = null;
+ public array|string|null $loginRedirect = null;
/**
* The default action to redirect to after the user is logged out. While AuthComponent does
* not handle post-logout redirection, a redirect URL will be returned from AuthComponent::logout().
* Defaults to AuthComponent::$loginAction.
*
- * @var mixed
+ * @var array|string|null
* @see AuthComponent::$loginAction
* @see AuthComponent::logout()
*/
- public $logoutRedirect = null;
+ public array|string|null $logoutRedirect = null;
/**
* Error to display when user attempts to access an object or action to which they do not have
* access.
*
- * @var string|bool
+ * @var string|bool|null
* @link https://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#AuthComponent::$authError
*/
- public $authError = null;
+ public string|bool|null $authError = null;
/**
* Controls handling of unauthorized access.
@@ -231,9 +240,9 @@ class AuthComponent extends Component
* - If set to a string or array the value is used as a URL to redirect to.
* - If set to false a ForbiddenException exception is thrown instead of redirecting.
*
- * @var mixed
+ * @var array|string|bool
*/
- public $unauthorizedRedirect = true;
+ public array|string|bool $unauthorizedRedirect = true;
/**
* Controller actions for which user validation is not required.
@@ -241,28 +250,28 @@ class AuthComponent extends Component
* @var array
* @see AuthComponent::allow()
*/
- public $allowedActions = [];
+ public array $allowedActions = [];
/**
* Request object
*
- * @var CakeRequest
+ * @var CakeRequest|null
*/
- public $request;
+ public ?CakeRequest $request = null;
/**
* Response object
*
- * @var CakeResponse
+ * @var CakeResponse|null
*/
- public $response;
+ public ?CakeResponse $response = null;
/**
* Method list for bound controller.
*
* @var array
*/
- protected $_methods = [];
+ protected array $_methods = [];
/**
* Initializes AuthComponent for use in the controller.
@@ -270,7 +279,7 @@ class AuthComponent extends Component
* @param Controller $controller A reference to the instantiating controller object
* @return void
*/
- public function initialize(Controller $controller)
+ public function initialize(Controller $controller): void
{
$this->request = $controller->request;
$this->response = $controller->response;
@@ -333,7 +342,7 @@ public function startup(Controller $controller): bool
* @param Controller $controller A reference to the instantiating controller object
* @return bool True if action is accessible without authentication else false
*/
- protected function _isAllowed(Controller $controller)
+ protected function _isAllowed(Controller $controller): bool
{
$action = strtolower($controller->request->params['action']);
if (in_array($action, array_map('strtolower', $this->allowedActions))) {
@@ -355,7 +364,7 @@ protected function _isAllowed(Controller $controller)
* @param Controller $controller A reference to the controller object.
* @return bool True if current action is login action else false.
*/
- protected function _unauthenticated(Controller $controller)
+ protected function _unauthenticated(Controller $controller): bool
{
if (empty($this->_authenticateObjects)) {
$this->constructAuthenticate();
@@ -382,8 +391,9 @@ protected function _unauthenticated(Controller $controller)
return false;
}
+
+ $controller->response->statusCode(403);
if (!empty($this->ajaxLogin)) {
- $controller->response->statusCode(403);
$controller->viewPath = 'Elements';
$response = $controller->render($this->ajaxLogin, $this->RequestHandler->ajaxLayout);
$response->send();
@@ -391,7 +401,7 @@ protected function _unauthenticated(Controller $controller)
return false;
}
- $controller->response->statusCode(403);
+
$controller->response->send();
$this->_stop();
@@ -404,7 +414,7 @@ protected function _unauthenticated(Controller $controller)
* @param Controller $controller A reference to the controller object.
* @return bool True if current action is login action else false.
*/
- protected function _isLoginAction(Controller $controller)
+ protected function _isLoginAction(Controller $controller): bool
{
$url = '';
if (isset($controller->request->url)) {
@@ -450,7 +460,7 @@ protected function _unauthorized(Controller $controller): bool
*
* @return bool True
*/
- protected function _setDefaults()
+ protected function _setDefaults(): bool
{
$defaults = [
'logoutRedirect' => $this->loginAction,
@@ -476,8 +486,10 @@ protected function _setDefaults()
* @param CakeRequest|null $request The request to authenticate for. If empty, the current request will be used.
* @return bool True if $user is authorized, otherwise false
*/
- public function isAuthorized($user = null, ?CakeRequest $request = null)
- {
+ public function isAuthorized(
+ ?array $user = null,
+ ?CakeRequest $request = null,
+ ): bool {
if (empty($user) && !static::user()) {
return false;
}
@@ -502,10 +514,10 @@ public function isAuthorized($user = null, ?CakeRequest $request = null)
/**
* Loads the authorization objects configured.
*
- * @return mixed Either null when authorize is empty, or the loaded authorization objects.
+ * @return array|null Either null when authorize is empty, or the loaded authorization objects.
* @throws CakeException
*/
- public function constructAuthorize()
+ public function constructAuthorize(): ?array
{
if (empty($this->authorize)) {
return null;
@@ -550,14 +562,14 @@ public function constructAuthorize()
* @return void
* @link https://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#making-actions-public
*/
- public function allow(...$actions): void
+ public function allow(array|string|null ...$actions): void
{
if (empty($actions) || $actions[0] === null) {
$this->allowedActions = $this->_methods;
return;
}
- if (isset($actions[0]) && is_array($actions[0])) {
+ if (is_array($actions[0])) {
$actions = $actions[0];
}
$this->allowedActions = array_merge($this->allowedActions, $actions);
@@ -577,14 +589,14 @@ public function allow(...$actions): void
* @see AuthComponent::allow()
* @link https://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#making-actions-require-authorization
*/
- public function deny(...$actions): void
+ public function deny(array|string|null ...$actions): void
{
if (empty($actions) || $actions[0] === null) {
$this->allowedActions = [];
return;
}
- if (isset($actions[0]) && is_array($actions[0])) {
+ if (is_array($actions[0])) {
$actions = $actions[0];
}
foreach ($actions as $action) {
@@ -604,12 +616,12 @@ public function deny(...$actions): void
* attached authorize objects.
*
* @param array $map Actions to map
- * @return array
+ * @return array|null
* @see BaseAuthorize::mapActions()
* @link https://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#mapping-actions-when-using-crudauthorize
* @deprecated 3.0.0 Map actions using `actionMap` config key on authorize objects instead
*/
- public function mapActions($map = [])
+ public function mapActions(array $map = []): ?array
{
if (empty($this->_authorizeObjects)) {
$this->constructAuthorize();
@@ -622,7 +634,7 @@ public function mapActions($map = [])
return $mappedActions;
}
- return [];
+ return null;
}
/**
@@ -637,7 +649,7 @@ public function mapActions($map = [])
* @return bool True on login success, false on failure
* @link https://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#identifying-users-and-logging-them-in
*/
- public function login($user = null)
+ public function login(?array $user = null): bool
{
$this->_setDefaults();
@@ -671,7 +683,7 @@ public function login($user = null)
* @see AuthComponent::$logoutRedirect
* @link https://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#logging-users-out
*/
- public function logout()
+ public function logout(): string
{
$this->_setDefaults();
if (empty($this->_authenticateObjects)) {
@@ -700,7 +712,7 @@ public function logout()
* @return mixed|null User record. or null if no user is logged in.
* @link https://book.cakephp.org/2.0/en/core-libraries/components/authentication.html#accessing-the-logged-in-user
*/
- public static function user($key = null)
+ public static function user(?string $key = null): mixed
{
if (!empty(static::$_user)) {
$user = static::$_user;
@@ -722,7 +734,7 @@ public static function user($key = null)
*
* @return bool True if a user can be found, false if one cannot.
*/
- protected function _getUser()
+ protected function _getUser(): bool
{
$user = static::user();
if ($user) {
@@ -750,10 +762,10 @@ protected function _getUser()
* Backwards compatible alias for AuthComponent::redirectUrl().
*
* @param array|string|null $url Optional URL to write as the login redirect URL.
- * @return string Redirect URL
+ * @return string|null Redirect URL
* @deprecated 3.0.0 Since 2.3.0, use AuthComponent::redirectUrl() instead
*/
- public function redirect($url = null)
+ public function redirect(array|string|null $url = null): ?string
{
return $this->redirectUrl($url);
}
@@ -776,7 +788,7 @@ public function redirect($url = null)
* @param array|string|null $url Optional URL to write as the login redirect URL.
* @return string Redirect URL
*/
- public function redirectUrl($url = null)
+ public function redirectUrl(array|string|null $url = null): string
{
if ($url !== null) {
$redir = $url;
@@ -808,7 +820,7 @@ public function redirectUrl($url = null)
* @param CakeResponse $response The response
* @return array|bool User record data, or false, if the user could not be identified.
*/
- public function identify(CakeRequest $request, CakeResponse $response)
+ public function identify(CakeRequest $request, CakeResponse $response): array|bool
{
if (empty($this->_authenticateObjects)) {
$this->constructAuthenticate();
@@ -826,10 +838,10 @@ public function identify(CakeRequest $request, CakeResponse $response)
/**
* Loads the configured authentication objects.
*
- * @return mixed Either null on empty authenticate value, or an array of loaded objects.
+ * @return array|null Either null on empty authenticate value, or an array of loaded objects.
* @throws CakeException
*/
- public function constructAuthenticate()
+ public function constructAuthenticate(): ?array
{
if (empty($this->authenticate)) {
return null;
@@ -876,7 +888,7 @@ public function constructAuthenticate()
* @return string Hashed password
* @deprecated 3.0.0 Since 2.4. Use Security::hash() directly or a password hasher object.
*/
- public static function password($password)
+ public static function password(string $password): string
{
return Security::hash($password, null, true);
}
@@ -887,7 +899,7 @@ public static function password($password)
* @return bool true if the user is logged in, false otherwise
* @deprecated 3.0.0 Since 2.5. Use AuthComponent::user() directly.
*/
- public function loggedIn()
+ public function loggedIn(): bool
{
return (bool)static::user();
}
@@ -895,10 +907,10 @@ public function loggedIn()
/**
* Set a flash message. Uses the Session component, and values from AuthComponent::$flash.
*
- * @param string $message The message to set.
+ * @param string|false $message The message to set.
* @return void
*/
- public function flash($message)
+ public function flash(string|false $message): void
{
if ($message === false) {
return;
diff --git a/src/Controller/Component/CookieComponent.php b/src/Controller/Component/CookieComponent.php
index cef9141140..abfebae5e1 100644
--- a/src/Controller/Component/CookieComponent.php
+++ b/src/Controller/Component/CookieComponent.php
@@ -286,10 +286,10 @@ public function read($key = null)
/**
* Returns true if given variable is set in cookie.
*
- * @param string $key Variable name to check for
+ * @param string|null $key Variable name to check for
* @return bool True if variable is there
*/
- public function check($key = null)
+ public function check(?string $key = null): bool
{
if (empty($key)) {
return false;
diff --git a/src/Controller/Component/EmailComponent.php b/src/Controller/Component/EmailComponent.php
index 9696c8f2d3..f731a375bc 100644
--- a/src/Controller/Component/EmailComponent.php
+++ b/src/Controller/Component/EmailComponent.php
@@ -40,30 +40,30 @@ class EmailComponent extends Component
/**
* Recipient of the email
*
- * @var string
+ * @var array|string|null
*/
- public $to = null;
+ public array|string|null $to = null;
/**
* The mail which the email is sent from
*
- * @var string
+ * @var string|null
*/
- public $from = null;
+ public ?string $from = null;
/**
* The email the recipient will reply to
*
- * @var string
+ * @var string|null
*/
- public $replyTo = null;
+ public ?string $replyTo = null;
/**
* The read receipt email
*
- * @var string
+ * @var string|null
*/
- public $readReceipt = null;
+ public ?string $readReceipt = null;
/**
* The mail that will be used in case of any errors like
@@ -71,9 +71,9 @@ class EmailComponent extends Component
* - Remote user has exceeded his quota
* - Unknown user
*
- * @var string
+ * @var string|null
*/
- public $return = null;
+ public ?string $return = null;
/**
* Carbon Copy
@@ -81,9 +81,9 @@ class EmailComponent extends Component
* List of email's that should receive a copy of the email.
* The Recipient WILL be able to see this list
*
- * @var array
+ * @var array|string
*/
- public $cc = [];
+ public array|string $cc = [];
/**
* Blind Carbon Copy
@@ -91,25 +91,25 @@ class EmailComponent extends Component
* List of email's that should receive a copy of the email.
* The Recipient WILL NOT be able to see this list
*
- * @var array
+ * @var array|string
*/
- public $bcc = [];
+ public array|string $bcc = [];
/**
* The date to put in the Date: header. This should be a date
* conforming with the RFC2822 standard. Leave null, to have
* today's date generated.
*
- * @var string
+ * @var string|null
*/
- public $date = null;
+ public ?string $date = null;
/**
* The subject of the email
*
- * @var string
+ * @var string|null
*/
- public $subject = null;
+ public ?string $subject = null;
/**
* Associative array of a user defined headers
@@ -117,30 +117,30 @@ class EmailComponent extends Component
*
* @var array
*/
- public $headers = [];
+ public array $headers = [];
/**
* List of additional headers
*
* These will NOT be used if you are using safemode and mail()
*
- * @var string
+ * @var string|null
*/
- public $additionalParams = null;
+ public ?string $additionalParams = null;
/**
* Layout for the View
*
* @var string
*/
- public $layout = 'default';
+ public string $layout = 'default';
/**
* Template for the view
*
- * @var string
+ * @var string|null
*/
- public $template = null;
+ public ?string $template = null;
/**
* Line feed character(s) to be used when sending using mail() function
@@ -151,7 +151,7 @@ class EmailComponent extends Component
*
* @var string
*/
- public $lineFeed = PHP_EOL;
+ public string $lineFeed = PHP_EOL;
/**
* What format should the email be sent in
@@ -163,7 +163,7 @@ class EmailComponent extends Component
*
* @var string
*/
- public $sendAs = 'text';
+ public string $sendAs = 'text';
/**
* What method should the email be sent by
@@ -175,14 +175,14 @@ class EmailComponent extends Component
*
* @var string
*/
- public $delivery = 'mail';
+ public string $delivery = 'mail';
/**
* charset the email is sent in
*
* @var string
*/
- public $charset = 'utf-8';
+ public string $charset = 'utf-8';
/**
* List of files that should be attached to the email.
@@ -191,21 +191,21 @@ class EmailComponent extends Component
*
* @var array
*/
- public $attachments = [];
+ public array $attachments = [];
/**
* What mailer should EmailComponent identify itself as
*
* @var string
*/
- public $xMailer = 'CakePHP Email Component';
+ public string $xMailer = 'CakePHP Email Component';
/**
* The list of paths to search if an attachment isn't absolute
*
* @var array
*/
- public $filePaths = [];
+ public array $filePaths = [];
/**
* List of options to use for smtp mail method
@@ -220,21 +220,21 @@ class EmailComponent extends Component
*
* @var array
*/
- public $smtpOptions = [];
+ public array $smtpOptions = [];
/**
* Contains the rendered plain text message if one was sent.
*
- * @var string
+ * @var string|null
*/
- public $textMessage = null;
+ public ?string $textMessage = null;
/**
* Contains the rendered HTML message if one was sent.
*
- * @var string
+ * @var string|null
*/
- public $htmlMessage = null;
+ public ?string $htmlMessage = null;
/**
* Whether to generate a Message-ID header for the
@@ -247,14 +247,17 @@ class EmailComponent extends Component
*
* @var mixed
*/
- public $messageId = true;
+ public mixed $messageId = true;
/**
* Controller reference
*
- * @var Controller
+ * @var Controller|null
*/
- protected $_controller = null;
+ protected ?Controller $_controller = null;
+
+ protected array $_header = [];
+ protected array $_message = [];
/**
* Constructor
@@ -262,7 +265,7 @@ class EmailComponent extends Component
* @param ComponentCollection $collection A ComponentCollection this component can use to lazy load its components
* @param array $settings Array of configuration settings.
*/
- public function __construct(ComponentCollection $collection, $settings = [])
+ public function __construct(ComponentCollection $collection, array $settings = [])
{
$this->_controller = $collection->getController();
parent::__construct($collection, $settings);
@@ -274,7 +277,7 @@ public function __construct(ComponentCollection $collection, $settings = [])
* @param Controller $controller Instantiating controller
* @return void
*/
- public function initialize(Controller $controller)
+ public function initialize(Controller $controller): void
{
if (Configure::read('App.encoding') !== null) {
$this->charset = Configure::read('App.encoding');
@@ -284,13 +287,13 @@ public function initialize(Controller $controller)
/**
* Send an email using the specified content, template and layout
*
- * @param array|string $content Either an array of text lines, or a string with contents
+ * @param array|string|null $content Either an array of text lines, or a string with contents
* If you are rendering a template this variable will be sent to the templates as `$content`
- * @param string $template Template to use when sending email
- * @param string $layout Layout to use to enclose email body
+ * @param string|null $template Template to use when sending email
+ * @param string|null $layout Layout to use to enclose email body
* @return array Success
*/
- public function send($content = null, $template = null, $layout = null)
+ public function send(array|string|null $content = null, ?string $template = null, ?string $layout = null): array
{
$lib = new CakeEmail();
$lib->charset = $this->charset;
@@ -372,7 +375,7 @@ public function send($content = null, $template = null, $layout = null)
*
* @return void
*/
- public function reset()
+ public function reset(): void
{
$this->template = null;
$this->to = [];
@@ -396,7 +399,7 @@ public function reset()
*
* @return array
*/
- protected function _formatAttachFiles()
+ protected function _formatAttachFiles(): array
{
$files = [];
foreach ($this->attachments as $filename => $attachment) {
@@ -418,16 +421,14 @@ protected function _formatAttachFiles()
* @param string $attachment Attachment file name to find
* @return string|null Path to located file
*/
- protected function _findFiles($attachment)
+ protected function _findFiles(string $attachment): ?string
{
if (file_exists($attachment)) {
return $attachment;
}
foreach ($this->filePaths as $path) {
if (file_exists($path . DS . $attachment)) {
- $file = $path . DS . $attachment;
-
- return $file;
+ return $path . DS . $attachment;
}
}
@@ -440,12 +441,12 @@ protected function _findFiles($attachment)
* @param array $addresses Address to format.
* @return array
*/
- protected function _formatAddresses($addresses)
+ protected function _formatAddresses(array $addresses): array
{
$formatted = [];
foreach ($addresses as $address) {
- if (preg_match('/((.*))?\s?<(.+)>/', $address, $matches) && !empty($matches[2])) {
- $formatted[$this->_strip($matches[3])] = $matches[2];
+ if (preg_match('/(.*)?\s?<(.+)>/', $address, $matches) && !empty($matches[1])) {
+ $formatted[$this->_strip($matches[2])] = $matches[1];
} else {
$address = $this->_strip($address);
$formatted[$address] = $address;
@@ -463,17 +464,16 @@ protected function _formatAddresses($addresses)
* @param bool $message Set to true to indicate main message content
* @return string Stripped value
*/
- protected function _strip($value, $message = false)
+ protected function _strip(string $value, bool $message = false): string
{
- $search = '%0a|%0d|Content-(?:Type|Transfer-Encoding)\:';
- $search .= '|charset\=|mime-version\:|multipart/mixed|(?:[^a-z]to|b?cc)\:.*';
-
+ $search = '(?:%0a|%0d|Content-(?:Type|Transfer-Encoding)\:|charset\=|mime-version\:|multipart/mixed|(?:[^a-z]to|b?cc)\:.*';
if ($message !== true) {
$search .= '|\r|\n';
}
- $search = '#(?:' . $search . ')#i';
- while (preg_match($search, $value)) {
- $value = preg_replace($search, '', $value);
+ $search .= ')';
+
+ while (preg_match('#' . $search . '#i', $value)) {
+ $value = preg_replace('#' . $search . '#i', '', $value);
}
return $value;
diff --git a/src/Controller/Component/FlashComponent.php b/src/Controller/Component/FlashComponent.php
index 198f88378b..8aeaf05bad 100644
--- a/src/Controller/Component/FlashComponent.php
+++ b/src/Controller/Component/FlashComponent.php
@@ -40,7 +40,7 @@ class FlashComponent extends Component
*
* @var array
*/
- protected $_defaultConfig = [
+ protected array $_defaultConfig = [
'key' => 'flash',
'element' => 'default',
'params' => [],
@@ -53,8 +53,9 @@ class FlashComponent extends Component
* @param ComponentCollection $collection The ComponentCollection object
* @param array $settings Settings passed via controller
*/
- public function __construct(ComponentCollection $collection, $settings = [])
+ public function __construct(ComponentCollection $collection, array $settings = [])
{
+ parent::__construct($collection, $settings);
$this->_defaultConfig = Hash::merge($this->_defaultConfig, $settings);
}
@@ -69,14 +70,14 @@ public function __construct(ComponentCollection $collection, $settings = [])
* - `element` The element used to render the flash message. Default to 'default'.
* - `params` An array of variables to make available when using an element
*
- * @param string $message Message to be flashed. If an instance
+ * @param Exception|string $message Message to be flashed. If an instance
* of Exception the exception message will be used and code will be set
* in params.
* @param array $options An array of options.
* @return void
*/
- public function set($message, $options = [])
+ public function set(Exception|string $message, array $options = []): void
{
$options += $this->_defaultConfig;
diff --git a/src/Controller/Component/PaginatorComponent.php b/src/Controller/Component/PaginatorComponent.php
index 2cc70bde60..63c5499675 100644
--- a/src/Controller/Component/PaginatorComponent.php
+++ b/src/Controller/Component/PaginatorComponent.php
@@ -20,6 +20,7 @@
use Cake\Controller\Component;
use Cake\Controller\ComponentCollection;
+use Cake\Controller\Controller;
use Cake\Error\MissingModelException;
use Cake\Error\NotFoundException;
use Cake\Model\Model;
@@ -112,6 +113,8 @@ class PaginatorComponent extends Component
'queryScope' => null,
];
+ public ?Controller $Controller = null;
+
/**
* A list of parameters users are allowed to set using request parameters. Modifying
* this list will allow users to have more influence over pagination,
@@ -119,8 +122,11 @@ class PaginatorComponent extends Component
*
* @var array
*/
- public $whitelist = [
- 'limit', 'sort', 'page', 'direction',
+ public array $whitelist = [
+ 'limit',
+ 'sort',
+ 'page',
+ 'direction',
];
/**
@@ -129,7 +135,7 @@ class PaginatorComponent extends Component
* @param ComponentCollection $collection A ComponentCollection this component can use to lazy load its components
* @param array $settings Array of configuration settings.
*/
- public function __construct(ComponentCollection $collection, $settings = [])
+ public function __construct(ComponentCollection $collection, array $settings = [])
{
$settings = array_merge($this->settings, (array)$settings);
$this->Controller = $collection->getController();
@@ -139,8 +145,8 @@ public function __construct(ComponentCollection $collection, $settings = [])
/**
* Handles automatic pagination of model records.
*
- * @param Model|string $object Model to paginate (e.g: model instance, or 'Model', or 'Model.InnerModel')
- * @param array|string $scope Additional find conditions to use while paginating
+ * @param Model|array|string|null $object Model to paginate (e.g: model instance, or 'Model', or 'Model.InnerModel')
+ * @param array|string|null $scope Additional find conditions to use while paginating
* @param array $whitelist List of allowed fields for ordering. This allows you to prevent ordering
* on non-indexed, or undesirable columns. See PaginatorComponent::validateSort() for additional details
* on how the whitelisting and sort field validation works.
@@ -148,8 +154,11 @@ public function __construct(ComponentCollection $collection, $settings = [])
* @throws MissingModelException
* @throws NotFoundException
*/
- public function paginate($object = null, $scope = [], $whitelist = [])
- {
+ public function paginate(
+ Model|array|string|null $object = null,
+ array|string|null $scope = [],
+ array $whitelist = [],
+ ): array {
if (is_array($object)) {
$whitelist = $scope;
$scope = $object;
@@ -166,8 +175,6 @@ public function paginate($object = null, $scope = [], $whitelist = [])
$options = $this->validateSort($object, $options, $whitelist);
$options = $this->checkLimit($options);
- $conditions = $fields = $order = $limit = $page = $recursive = null;
-
if (!isset($options['conditions'])) {
$options['conditions'] = [];
}
@@ -180,6 +187,12 @@ public function paginate($object = null, $scope = [], $whitelist = [])
}
extract($options);
+ $conditions = $options['conditions'] ?? [];
+ $fields = $options['fields'] ?? [];
+ $order = $options['order'] ?? null;
+ $limit = $options['limit'] ?? null;
+ $page = $options['page'] ?? 1;
+ $recursive = $options['recursive'] ?? null;
if (is_array($scope) && !empty($scope)) {
$conditions = array_merge($conditions, $scope);
@@ -212,11 +225,13 @@ public function paginate($object = null, $scope = [], $whitelist = [])
// The cast behavior is undefined for values outside int range, but must remain
// consistent with previous PHP versions for page number validation
set_error_handler(function () {
+ return true;
}, E_WARNING);
- if ((int)$page < 1) {
+ $page = (int)$page;
+ if ($page < 1) {
$page = 1;
}
- $page = $options['page'] = (int)$page;
+ $options['page'] = $page;
restore_error_handler();
if ($object->hasMethod('paginate')) {
@@ -295,10 +310,10 @@ public function paginate($object = null, $scope = [], $whitelist = [])
/**
* Get the object pagination will occur on.
*
- * @param Model|string $object The object you are looking for.
+ * @param Model|string|null $object The object you are looking for.
* @return mixed The model object to paginate on.
*/
- protected function _getObject($object)
+ protected function _getObject(Model|string|null $object): mixed
{
if (is_string($object)) {
$assoc = null;
@@ -318,7 +333,7 @@ protected function _getObject($object)
return $this->Controller->{$this->Controller->modelClass}->{$object};
}
}
- if (empty($object) || $object === null) {
+ if (empty($object)) {
if (isset($this->Controller->{$this->Controller->modelClass})) {
return $this->Controller->{$this->Controller->modelClass};
}
@@ -353,9 +368,11 @@ protected function _getObject($object)
* that key's settings will be used for pagination instead of the general ones.
* @return array Array of merged options.
*/
- public function mergeOptions($alias)
+ public function mergeOptions(string $alias): array
{
$defaults = $this->getDefaults($alias);
+ $request = [];
+
switch ($defaults['paramType']) {
case 'named':
$request = $this->Controller->request->params['named'];
@@ -367,6 +384,7 @@ public function mergeOptions($alias)
if ($defaults['queryScope']) {
$request = Hash::get($request, $defaults['queryScope'], []);
}
+
$request = array_intersect_key($request, array_flip($this->whitelist));
return array_merge($defaults, $request);
@@ -377,9 +395,16 @@ public function mergeOptions($alias)
* will be used.
*
* @param string $alias Model name to get default settings for.
- * @return array An array of pagination defaults for a model, or the general settings.
+ * @return array{
+ * page: int,
+ * limit: int,
+ * maxLimit: int,
+ * paramType: string,
+ * queryScope: mixed|null,
+ * 0?: mixed
+ * } An array of pagination defaults for a model, or the general settings.
*/
- public function getDefaults($alias)
+ public function getDefaults(string $alias): array
{
$defaults = $this->settings;
if (isset($this->settings[$alias])) {
@@ -412,7 +437,7 @@ public function getDefaults($alias)
* @param array $whitelist The list of columns that can be used for sorting. If empty all keys are allowed.
* @return array An array of options with sort + direction removed and replaced with order if possible.
*/
- public function validateSort(Model $object, array $options, array $whitelist = [])
+ public function validateSort(Model $object, array $options, array $whitelist = []): array
{
if (empty($options['order']) && is_array($object->order)) {
$options['order'] = $object->order;
@@ -473,7 +498,7 @@ public function validateSort(Model $object, array $options, array $whitelist = [
* @param array $options An array of options with a limit key to be checked.
* @return array An array of options for pagination
*/
- public function checkLimit(array $options)
+ public function checkLimit(array $options): array
{
$options['limit'] = (int)$options['limit'];
if (empty($options['limit']) || $options['limit'] < 1) {
diff --git a/src/Controller/Component/RequestHandlerComponent.php b/src/Controller/Component/RequestHandlerComponent.php
index 5f51851bbd..0775f373c7 100644
--- a/src/Controller/Component/RequestHandlerComponent.php
+++ b/src/Controller/Component/RequestHandlerComponent.php
@@ -53,50 +53,50 @@ class RequestHandlerComponent extends Component
* @var string
* @see RequestHandler::setAjax()
*/
- public $ajaxLayout = 'ajax';
+ public string $ajaxLayout = 'ajax';
/**
* Determines whether or not callbacks will be fired on this component
*
* @var bool
*/
- public $enabled = true;
+ public bool $enabled = true;
/**
* Holds the reference to Controller::$request
*
- * @var CakeRequest
+ * @var CakeRequest|null
*/
- public $request;
+ public ?CakeRequest $request = null;
/**
* Holds the reference to Controller::$response
*
- * @var CakeResponse
+ * @var CakeResponse|null
*/
- public $response;
+ public ?CakeResponse $response = null;
/**
* Contains the file extension parsed out by the Router
*
- * @var string
+ * @var string|null
* @see Router::parseExtensions()
*/
- public $ext = null;
+ public ?string $ext = null;
/**
* Array of parameters parsed from the URL.
*
* @var array|null
*/
- public $params = null;
+ public ?array $params = null;
/**
* The template to use when rendering the given content type.
*
- * @var string
+ * @var string|null
*/
- protected $_renderType = null;
+ protected ?string $_renderType = null;
/**
* A mapping between extensions and deserializers for request bodies of that type.
@@ -104,7 +104,7 @@ class RequestHandlerComponent extends Component
*
* @var array
*/
- protected $_inputTypeMap = [
+ protected array $_inputTypeMap = [
'json' => ['json_decode', true],
];
@@ -114,7 +114,7 @@ class RequestHandlerComponent extends Component
*
* @var array
*/
- protected $_viewClassMap = [
+ protected array $_viewClassMap = [
'json' => 'Json',
'xml' => 'Xml',
];
@@ -125,7 +125,7 @@ class RequestHandlerComponent extends Component
* @param ComponentCollection $collection ComponentCollection object.
* @param array $settings Array of settings.
*/
- public function __construct(ComponentCollection $collection, $settings = [])
+ public function __construct(ComponentCollection $collection, array $settings = [])
{
parent::__construct($collection, $settings + ['checkHttpCache' => true]);
$this->addInputType('xml', [[$this, 'convertXml']]);
@@ -145,7 +145,7 @@ public function __construct(ComponentCollection $collection, $settings = [])
* @return void
* @see Router::parseExtensions()
*/
- public function initialize(Controller $controller)
+ public function initialize(Controller $controller): void
{
if (isset($this->request->params['ext'])) {
$this->ext = $this->request->params['ext'];
@@ -173,7 +173,7 @@ public function initialize(Controller $controller)
*
* @return void
*/
- protected function _setExtension()
+ protected function _setExtension(): void
{
$accept = $this->request->parseAccept();
if (empty($accept)) {
@@ -218,7 +218,7 @@ protected function _setExtension()
* @param Controller $controller A reference to the controller
* @return void
*/
- public function startup(Controller $controller)
+ public function startup(Controller $controller): void
{
$controller->request->params['isAjax'] = $this->request->is('ajax');
$isRecognized = (
@@ -249,7 +249,7 @@ public function startup(Controller $controller)
* @param string $xml XML string.
* @return array Xml array data
*/
- public function convertXml($xml)
+ public function convertXml(string $xml): array
{
try {
$xml = Xml::build($xml, ['readFile' => false]);
@@ -268,19 +268,21 @@ public function convertXml($xml)
* Modifies the $_POST and $_SERVER['REQUEST_METHOD'] to simulate a new GET request.
*
* @param Controller $controller A reference to the controller
- * @param array|string $url A string or array containing the redirect location
- * @param array|int $status HTTP Status for redirect
+ * @param array|string|null $url A string or array containing the redirect location
+ * @param array|int|null $status HTTP Status for redirect
* @param bool $exit Whether to exit script, defaults to `true`.
- * @return void
- */
- public function beforeRedirect(Controller $controller, $url, $status = null, $exit = true)
- {
- if (!$this->request->is('ajax')) {
- return;
- }
- if (empty($url)) {
- return;
+ * @return array|string|false|null
+ */
+ public function beforeRedirect(
+ Controller $controller,
+ array|string|null $url,
+ array|int|null $status = null,
+ bool $exit = true,
+ ): array|string|false|null {
+ if (!$this->request->is('ajax') || empty($url)) {
+ return null;
}
+
$_SERVER['REQUEST_METHOD'] = 'GET';
foreach ($_POST as $key => $val) {
unset($_POST[$key]);
@@ -298,6 +300,8 @@ public function beforeRedirect(Controller $controller, $url, $status = null, $ex
$this->response->body($this->requestAction($url, ['return', 'bare' => false]));
$this->response->send();
$this->_stop();
+
+ return null;
}
/**
@@ -321,10 +325,10 @@ public function beforeRender(Controller $controller): ?bool
/**
* Returns true if the current HTTP request is Ajax, false otherwise
*
- * @return bool True if call is Ajax
+ * @return mixed|bool True if call is Ajax
* @deprecated 3.0.0 Use `$this->request->is('ajax')` instead.
*/
- public function isAjax()
+ public function isAjax(): mixed
{
return $this->request->is('ajax');
}
@@ -332,10 +336,10 @@ public function isAjax()
/**
* Returns true if the current HTTP request is coming from a Flash-based client
*
- * @return bool True if call is from Flash
+ * @return mixed|bool True if call is from Flash
* @deprecated 3.0.0 Use `$this->request->is('flash')` instead.
*/
- public function isFlash()
+ public function isFlash(): mixed
{
return $this->request->is('flash');
}
@@ -343,10 +347,10 @@ public function isFlash()
/**
* Returns true if the current request is over HTTPS, false otherwise.
*
- * @return bool True if call is over HTTPS
+ * @return mixed|bool True if call is over HTTPS
* @deprecated 3.0.0 Use `$this->request->is('ssl')` instead.
*/
- public function isSSL()
+ public function isSSL(): mixed
{
return $this->request->is('ssl');
}
@@ -354,9 +358,9 @@ public function isSSL()
/**
* Returns true if the current call accepts an XML response, false otherwise
*
- * @return bool True if client accepts an XML response
+ * @return mixed|bool True if client accepts an XML response
*/
- public function isXml()
+ public function isXml(): mixed
{
return $this->prefers('xml');
}
@@ -364,9 +368,9 @@ public function isXml()
/**
* Returns true if the current call accepts an RSS response, false otherwise
*
- * @return bool True if client accepts an RSS response
+ * @return mixed|bool True if client accepts an RSS response
*/
- public function isRss()
+ public function isRss(): mixed
{
return $this->prefers('rss');
}
@@ -374,9 +378,9 @@ public function isRss()
/**
* Returns true if the current call accepts an Atom response, false otherwise
*
- * @return bool True if client accepts an RSS response
+ * @return mixed|bool True if client accepts an RSS response
*/
- public function isAtom()
+ public function isAtom(): mixed
{
return $this->prefers('atom');
}
@@ -385,9 +389,9 @@ public function isAtom()
* Returns true if user agent string matches a mobile web browser, or if the
* client accepts WAP content.
*
- * @return bool True if user agent is a mobile web browser
+ * @return mixed|bool True if user agent is a mobile web browser
*/
- public function isMobile()
+ public function isMobile(): mixed
{
return $this->request->is('mobile') || $this->accepts('wap');
}
@@ -395,9 +399,9 @@ public function isMobile()
/**
* Returns true if the client accepts WAP content
*
- * @return bool
+ * @return mixed|bool
*/
- public function isWap()
+ public function isWap(): mixed
{
return $this->prefers('wap');
}
@@ -405,10 +409,10 @@ public function isWap()
/**
* Returns true if the current call a POST request
*
- * @return bool True if call is a POST
+ * @return mixed|bool True if call is a POST
* @deprecated 3.0.0 Use $this->request->is('post'); from your controller.
*/
- public function isPost()
+ public function isPost(): mixed
{
return $this->request->is('post');
}
@@ -419,7 +423,7 @@ public function isPost()
* @return bool True if call is a PUT
* @deprecated 3.0.0 Use $this->request->is('put'); from your controller.
*/
- public function isPut()
+ public function isPut(): bool
{
return $this->request->is('put');
}
@@ -427,10 +431,10 @@ public function isPut()
/**
* Returns true if the current call a GET request
*
- * @return bool True if call is a GET
+ * @return mixed|bool True if call is a GET
* @deprecated 3.0.0 Use $this->request->is('get'); from your controller.
*/
- public function isGet()
+ public function isGet(): mixed
{
return $this->request->is('get');
}
@@ -438,10 +442,10 @@ public function isGet()
/**
* Returns true if the current call a DELETE request
*
- * @return bool True if call is a DELETE
+ * @return mixed|bool True if call is a DELETE
* @deprecated 3.0.0 Use $this->request->is('delete'); from your controller.
*/
- public function isDelete()
+ public function isDelete(): mixed
{
return $this->request->is('delete');
}
@@ -450,9 +454,9 @@ public function isDelete()
* Gets Prototype version if call is Ajax, otherwise empty string.
* The Prototype library sets a special "Prototype version" HTTP header.
*
- * @return string|bool When Ajax the prototype version of component making the call otherwise false
+ * @return string|false When Ajax the prototype version of component making the call otherwise false
*/
- public function getAjaxVersion()
+ public function getAjaxVersion(): string|false
{
$httpX = env('HTTP_X_PROTOTYPE_VERSION');
@@ -466,12 +470,12 @@ public function getAjaxVersion()
* startup method.
*
* @param string $name The name of the Content-type, i.e. "html", "xml", "css"
- * @param array|string $type The Content-type or array of Content-types assigned to the name,
+ * @param array|string|null $type The Content-type or array of Content-types assigned to the name,
* i.e. "text/html", or "application/xml"
* @return void
* @deprecated 3.0.0 Use `$this->response->type()` instead.
*/
- public function setContent($name, $type = null)
+ public function setContent(string $name, array|string|null $type = null): void
{
$this->response->type([$name => $type]);
}
@@ -482,7 +486,7 @@ public function setContent($name, $type = null)
* @return string Server address
* @deprecated 3.0.0 Use $this->request->referer() from your controller instead
*/
- public function getReferer()
+ public function getReferer(): string
{
return $this->request->referer(false);
}
@@ -492,10 +496,10 @@ public function getReferer()
*
* @param bool $safe Use safe = false when you think the user might manipulate
* their HTTP_CLIENT_IP header. Setting $safe = false will also look at HTTP_X_FORWARDED_FOR
- * @return string Client IP address
+ * @return string|false Client IP address
* @deprecated 3.0.0 Use $this->request->clientIp() from your, controller instead.
*/
- public function getClientIP($safe = true)
+ public function getClientIP(bool $safe = true): string|false
{
return $this->request->clientIp($safe);
}
@@ -515,21 +519,22 @@ public function getClientIP($safe = true)
*
* Returns true if the client accepts xml.
*
- * @param array|string $type Can be null (or no parameter), a string type name, or an
+ * @param array|string|null $type Can be null (or no parameter), a string type name, or an
* array of types
- * @return mixed If null or no parameter is passed, returns an array of content
+ * @return array|string|bool If null or no parameter is passed, returns an array of content
* types the client accepts. If a string is passed, returns true
* if the client accepts it. If an array is passed, returns true
* if the client accepts one or more elements in the array.
* @see RequestHandlerComponent::setContent()
*/
- public function accepts($type = null)
+ public function accepts(array|string|null $type = null): array|string|bool
{
$accepted = $this->request->accepts();
if (!$type) {
return $this->mapType($accepted);
}
+
if (is_array($type)) {
foreach ($type as $t) {
$t = $this->mapAlias($t);
@@ -540,28 +545,25 @@ public function accepts($type = null)
return false;
}
- if (is_string($type)) {
- return in_array($this->mapAlias($type), $accepted);
- }
- return false;
+ return in_array($this->mapAlias($type), $accepted);
}
/**
* Determines the content type of the data the client has sent (i.e. in a POST request)
*
- * @param array|string $type Can be null (or no parameter), a string type name, or an array of types
+ * @param array|string|null $type Can be null (or no parameter), a string type name, or an array of types
* @return mixed If a single type is supplied a boolean will be returned. If no type is provided
* The mapped value of CONTENT_TYPE will be returned. If an array is supplied the first type
* in the request content type will be returned.
*/
- public function requestedWith($type = null)
+ public function requestedWith(array|string|null $type = null): mixed
{
if (
- !$this->request->is('patch') &&
- !$this->request->is('post') &&
- !$this->request->is('put') &&
- !$this->request->is('delete')
+ !$this->request->is('patch')
+ && !$this->request->is('post')
+ && !$this->request->is('put')
+ && !$this->request->is('delete')
) {
return null;
}
@@ -582,9 +584,8 @@ public function requestedWith($type = null)
if (!$type) {
return $this->mapType($contentType);
}
- if (is_string($type)) {
- return $type === $this->mapType($contentType);
- }
+
+ return $type === $this->mapType($contentType);
}
/**
@@ -595,7 +596,7 @@ public function requestedWith($type = null)
* if provided, and secondarily by the list of content-types provided in
* HTTP_ACCEPT.
*
- * @param array|string $type An optional array of 'friendly' content-type names, i.e.
+ * @param array|string|null $type An optional array of 'friendly' content-type names, i.e.
* 'html', 'xml', 'js', etc.
* @return mixed If $type is null or not provided, the first content-type in the
* list, based on preference, is returned. If a single type is provided
@@ -604,7 +605,7 @@ public function requestedWith($type = null)
* If no type is provided the first preferred type is returned.
* @see RequestHandlerComponent::setContent()
*/
- public function prefers($type = null)
+ public function prefers(array|string|null $type = null): mixed
{
$acceptRaw = $this->request->parseAccept();
@@ -659,8 +660,11 @@ public function prefers($type = null)
* @see RequestHandlerComponent::setContent()
* @see RequestHandlerComponent::respondAs()
*/
- public function renderAs(Controller $controller, $type, $options = [])
- {
+ public function renderAs(
+ Controller $controller,
+ string $type,
+ array $options = [],
+ ): void {
$defaults = ['charset' => 'UTF-8'];
if (Configure::read('App.encoding') !== null) {
@@ -670,8 +674,9 @@ public function renderAs(Controller $controller, $type, $options = [])
if ($type === 'ajax') {
$controller->layout = $this->ajaxLayout;
+ $this->respondAs('html', $options);
- return $this->respondAs('html', $options);
+ return;
}
$pluginDot = null;
@@ -727,7 +732,7 @@ public function renderAs(Controller $controller, $type, $options = [])
* already been set by this method.
* @see RequestHandlerComponent::setContent()
*/
- public function respondAs($type, $options = [])
+ public function respondAs(array|string $type, array $options = []): bool
{
$defaults = ['index' => null, 'charset' => null, 'attachment' => false];
$options = $options + $defaults;
@@ -767,10 +772,10 @@ public function respondAs($type, $options = [])
/**
* Returns the current response type (Content-type header), or null if not alias exists
*
- * @return mixed A string content type alias, or raw content type if no alias map exists,
+ * @return array|string A string content type alias, or raw content type if no alias map exists,
* otherwise null
*/
- public function responseType()
+ public function responseType(): array|string
{
return $this->mapType($this->response->type());
}
@@ -779,10 +784,10 @@ public function responseType()
* Maps a content-type back to an alias
*
* @param array|string $cType Either a string content type to map, or an array of types.
- * @return array|string Aliases for the types provided.
+ * @return array|string|null Aliases for the types provided.
* @deprecated 3.0.0 Use $this->response->mapType() in your controller instead.
*/
- public function mapType($cType)
+ public function mapType(array|string $cType): array|string|null
{
return $this->response->mapType($cType);
}
@@ -794,7 +799,7 @@ public function mapType($cType)
* @return array|string|null Null on an undefined alias. String value of the mapped alias type. If an
* alias maps to more than one content type, the first one will be returned.
*/
- public function mapAlias($alias)
+ public function mapAlias(array|string $alias): array|string|null
{
if (is_array($alias)) {
return array_map([$this, 'mapAlias'], $alias);
@@ -816,13 +821,13 @@ public function mapAlias($alias)
* converted by RequestHandlerComponent during the startup() callback.
*
* @param string $type The type alias being converted, ie. json
- * @param array $handler The handler array for the type. The first index should
+ * @param array|null $handler The handler array for the type. The first index should
* be the handling callback, all other arguments should be additional parameters
* for the handler.
* @return void
* @throws CakeException
*/
- public function addInputType($type, $handler)
+ public function addInputType(string $type, ?array $handler): void
{
if (!is_array($handler) || !isset($handler[0]) || !is_callable($handler[0])) {
throw new CakeException(__d('cake_dev', 'You must give a handler callback.'));
@@ -833,15 +838,18 @@ public function addInputType($type, $handler)
/**
* Getter/setter for viewClassMap
*
- * @param array|string $type The type string or array with format `array('type' => 'viewClass')` to map one or more
- * @param array $viewClass The viewClass to be used for the type without `View` appended
+ * @param array|string|null $type The type string or array with format `array('type' => 'viewClass')` to map one or more
+ * @param array|string|null $viewClass The viewClass to be used for the type without `View` appended
* @return array|string Returns viewClass when only string $type is set, else array with viewClassMap
*/
- public function viewClassMap($type = null, $viewClass = null)
- {
+ public function viewClassMap(
+ array|string|null $type = null,
+ array|string|null $viewClass = null,
+ ): array|string {
if (!$viewClass && is_string($type) && isset($this->_viewClassMap[$type])) {
return $this->_viewClassMap[$type];
}
+
if (is_string($type)) {
$this->_viewClassMap[$type] = $viewClass;
} elseif (is_array($type)) {
diff --git a/src/Controller/Component/SecurityComponent.php b/src/Controller/Component/SecurityComponent.php
index 236b0cdcc7..719729dfc0 100644
--- a/src/Controller/Component/SecurityComponent.php
+++ b/src/Controller/Component/SecurityComponent.php
@@ -56,9 +56,9 @@ class SecurityComponent extends Component
/**
* The controller method that will be called if this request is black-hole'd
*
- * @var string
+ * @var string|null
*/
- public $blackHoleCallback = null;
+ public ?string $blackHoleCallback = null;
/**
* List of controller actions for which a POST request is required
@@ -67,7 +67,7 @@ class SecurityComponent extends Component
* @deprecated 3.0.0 Use CakeRequest::allowMethod() instead.
* @see SecurityComponent::requirePost()
*/
- public $requirePost = [];
+ public array $requirePost = [];
/**
* List of controller actions for which a GET request is required
@@ -76,7 +76,7 @@ class SecurityComponent extends Component
* @deprecated 3.0.0 Use CakeRequest::allowMethod() instead.
* @see SecurityComponent::requireGet()
*/
- public $requireGet = [];
+ public array $requireGet = [];
/**
* List of controller actions for which a PUT request is required
@@ -85,7 +85,7 @@ class SecurityComponent extends Component
* @deprecated 3.0.0 Use CakeRequest::allowMethod() instead.
* @see SecurityComponent::requirePut()
*/
- public $requirePut = [];
+ public array $requirePut = [];
/**
* List of controller actions for which a DELETE request is required
@@ -94,7 +94,7 @@ class SecurityComponent extends Component
* @deprecated 3.0.0 Use CakeRequest::allowMethod() instead.
* @see SecurityComponent::requireDelete()
*/
- public $requireDelete = [];
+ public array $requireDelete = [];
/**
* List of actions that require an SSL-secured connection
@@ -102,7 +102,7 @@ class SecurityComponent extends Component
* @var array
* @see SecurityComponent::requireSecure()
*/
- public $requireSecure = [];
+ public array $requireSecure = [];
/**
* List of actions that require a valid authentication key
@@ -111,7 +111,7 @@ class SecurityComponent extends Component
* @see SecurityComponent::requireAuth()
* @deprecated 2.8.1 This feature is confusing and not useful.
*/
- public $requireAuth = [];
+ public array $requireAuth = [];
/**
* Controllers from which actions of the current controller are allowed to receive
@@ -120,7 +120,7 @@ class SecurityComponent extends Component
* @var array
* @see SecurityComponent::requireAuth()
*/
- public $allowedControllers = [];
+ public array $allowedControllers = [];
/**
* Actions from which actions of the current controller are allowed to receive
@@ -129,7 +129,7 @@ class SecurityComponent extends Component
* @var array
* @see SecurityComponent::requireAuth()
*/
- public $allowedActions = [];
+ public array $allowedActions = [];
/**
* Deprecated property, superseded by unlockedFields.
@@ -138,7 +138,7 @@ class SecurityComponent extends Component
* @deprecated 3.0.0 Superseded by unlockedFields.
* @see SecurityComponent::$unlockedFields
*/
- public $disabledFields = [];
+ public array $disabledFields = [];
/**
* Form fields to exclude from POST validation. Fields can be unlocked
@@ -148,16 +148,16 @@ class SecurityComponent extends Component
*
* @var array
*/
- public $unlockedFields = [];
+ public array $unlockedFields = [];
/**
* Actions to exclude from CSRF and POST validation checks.
* Other checks like requireAuth(), requireSecure(),
* requirePost(), requireGet() etc. will still be applied.
*
- * @var array
+ * @var array|string
*/
- public $unlockedActions = [];
+ public array|string $unlockedActions = [];
/**
* Whether to validate POST data. Set to false to disable for data coming from 3rd party
@@ -165,7 +165,7 @@ class SecurityComponent extends Component
*
* @var bool
*/
- public $validatePost = true;
+ public bool $validatePost = true;
/**
* Whether to use CSRF protected forms. Set to false to disable CSRF protection on forms.
@@ -174,7 +174,7 @@ class SecurityComponent extends Component
* @see http://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)
* @see SecurityComponent::$csrfExpires
*/
- public $csrfCheck = true;
+ public bool $csrfCheck = true;
/**
* The duration from when a CSRF token is created that it will expire on.
@@ -183,7 +183,7 @@ class SecurityComponent extends Component
*
* @var string
*/
- public $csrfExpires = '+30 minutes';
+ public string $csrfExpires = '+30 minutes';
/**
* Controls whether or not CSRF tokens are use and burn. Set to false to not generate
@@ -193,7 +193,7 @@ class SecurityComponent extends Component
*
* @var bool
*/
- public $csrfUseOnce = true;
+ public bool $csrfUseOnce = true;
/**
* Control the number of tokens a user can keep open.
@@ -206,7 +206,7 @@ class SecurityComponent extends Component
*
* @var int
*/
- public $csrfLimit = 100;
+ public int $csrfLimit = 100;
/**
* Other components used by the Security component
@@ -234,9 +234,9 @@ class SecurityComponent extends Component
*
* @param Controller $controller Instantiating controller
* @throws AuthSecurityException
- * @return void
+ * @return mixed
*/
- public function startup(Controller $controller)
+ public function startup(Controller $controller): mixed
{
$this->request = $controller->request;
$this->_action = $controller->request->params['action'];
@@ -271,6 +271,8 @@ public function startup(Controller $controller)
if ($hasData && is_array($controller->request->data)) {
unset($controller->request->data['_Token']);
}
+
+ return null;
}
/**
@@ -362,8 +364,11 @@ public function requireAuth(array|string|null ...$args): void
* @link https://book.cakephp.org/2.0/en/core-libraries/components/security-component.html#handling-blackhole-callbacks
* @throws BadRequestException
*/
- public function blackHole(Controller $controller, $error = '', ?SecurityException $exception = null)
- {
+ public function blackHole(
+ Controller $controller,
+ string $error = '',
+ ?SecurityException $exception = null,
+ ): mixed {
if (!$this->blackHoleCallback) {
$this->_throwException($exception);
}
@@ -378,15 +383,17 @@ public function blackHole(Controller $controller, $error = '', ?SecurityExceptio
* @throws BadRequestException
* @return void
*/
- protected function _throwException($exception = null)
+ protected function _throwException(?SecurityException $exception = null): void
{
if ($exception !== null) {
- if (!Configure::read('debug') && $exception instanceof SecurityException) {
+ if (!Configure::read('debug')) {
$exception->setReason($exception->getMessage());
$exception->setMessage(self::DEFAULT_EXCEPTION_MESSAGE);
}
+
throw $exception;
}
+
throw new BadRequestException(self::DEFAULT_EXCEPTION_MESSAGE);
}
@@ -412,11 +419,11 @@ protected function _requireMethod(string $method, array|string|null $actions = [
* @throws SecurityException
* @return bool True if $method is required
*/
- protected function _methodsRequired(Controller $controller)
+ protected function _methodsRequired(Controller $controller): bool
{
foreach (['Post', 'Get', 'Put', 'Delete'] as $method) {
$property = 'require' . $method;
- if (is_array($this->$property) && !empty($this->$property)) {
+ if (!empty($this->$property)) {
$require = $this->$property;
if (in_array($this->_action, $require) || $this->$property === ['*']) {
if (!$controller->request->is($method)) {
@@ -438,9 +445,9 @@ protected function _methodsRequired(Controller $controller)
* @throws SecurityException
* @return bool True if secure connection required
*/
- protected function _secureRequired(Controller $controller)
+ protected function _secureRequired(Controller $controller): bool
{
- if (is_array($this->requireSecure) && !empty($this->requireSecure)) {
+ if (!empty($this->requireSecure)) {
$requireSecure = $this->requireSecure;
if (in_array($this->_action, $requireSecure) || $this->requireSecure === ['*']) {
@@ -463,9 +470,9 @@ protected function _secureRequired(Controller $controller)
* @throws AuthSecurityException
* @deprecated 2.8.1 This feature is confusing and not useful.
*/
- protected function _authRequired(Controller $controller)
+ protected function _authRequired(Controller $controller): ?bool
{
- if (is_array($this->requireAuth) && !empty($this->requireAuth) && !empty($controller->request->data)) {
+ if (!empty($this->requireAuth) && !empty($controller->request->data)) {
$requireAuth = $this->requireAuth;
if (in_array($controller->request->params['action'], $requireAuth) || $this->requireAuth === ['*']) {
@@ -517,7 +524,7 @@ protected function _authRequired(Controller $controller)
* @throws AuthSecurityException
* @return bool true if submitted form is valid
*/
- protected function _validatePost(Controller $controller)
+ protected function _validatePost(Controller $controller): bool
{
$token = $this->_validToken($controller);
$hashParts = $this->_hashParts($controller);
@@ -543,7 +550,7 @@ protected function _validatePost(Controller $controller)
* @throws SecurityException
* @return string fields token
*/
- protected function _validToken(Controller $controller)
+ protected function _validToken(Controller $controller): string
{
$check = $controller->request->data;
@@ -578,7 +585,7 @@ protected function _validToken(Controller $controller)
* @param Controller $controller Instantiating controller
* @return array
*/
- protected function _hashParts(Controller $controller)
+ protected function _hashParts(Controller $controller): array
{
$fieldList = $this->_fieldsList($controller->request->data);
$unlocked = $this->_sortedUnlocked($controller->request->data);
@@ -597,14 +604,14 @@ protected function _hashParts(Controller $controller)
* @param array $check Data array
* @return array
*/
- protected function _fieldsList(array $check)
+ protected function _fieldsList(array $check): array
{
$locked = '';
$token = urldecode($check['_Token']['fields']);
$unlocked = $this->_unlocked($check);
if (strpos($token, ':')) {
- [$token, $locked] = explode(':', $token, 2);
+ [, $locked] = explode(':', $token, 2);
}
unset($check['_Token'], $check['_csrfToken']);
@@ -629,11 +636,11 @@ protected function _fieldsList(array $check)
}
$unlockedFields = array_unique(
- array_merge((array)$this->disabledFields, (array)$this->unlockedFields, $unlocked),
+ array_merge($this->disabledFields, $this->unlockedFields, $unlocked),
);
foreach ($fieldList as $i => $key) {
- $isLocked = (is_array($locked) && in_array($key, $locked));
+ $isLocked = in_array($key, $locked);
if (!empty($unlockedFields)) {
foreach ($unlockedFields as $off) {
@@ -666,7 +673,7 @@ protected function _fieldsList(array $check)
* @param array $data Data array
* @return string
*/
- protected function _unlocked(array $data)
+ protected function _unlocked(array $data): string
{
return urldecode($data['_Token']['unlocked']);
}
@@ -677,7 +684,7 @@ protected function _unlocked(array $data)
* @param array $data Data array
* @return string
*/
- protected function _sortedUnlocked($data)
+ protected function _sortedUnlocked(array $data): string
{
$unlocked = $this->_unlocked($data);
$unlocked = explode('|', $unlocked);
@@ -693,8 +700,10 @@ protected function _sortedUnlocked($data)
* @param array $hashParts Elements used to generate the Token hash
* @return string Message explaining why the tokens are not matching
*/
- protected function _debugPostTokenNotMatching(Controller $controller, $hashParts)
- {
+ protected function _debugPostTokenNotMatching(
+ Controller $controller,
+ array $hashParts,
+ ): string {
$messages = [];
$expectedParts = json_decode(urldecode($controller->request->data['_Token']['debug']), true);
if (!is_array($expectedParts) || count($expectedParts) !== 3) {
@@ -741,12 +750,17 @@ protected function _debugPostTokenNotMatching(Controller $controller, $hashParts
* @param array $dataFields Fields array, containing the POST data fields
* @param array $expectedFields Fields array, containing the expected fields we should have in POST
* @param string $intKeyMessage Message string if unexpected found in data fields indexed by int (not protected)
- * @param string $stringKeyMessage Message string if tampered found in data fields indexed by string (protected)
+ * @param string|null $stringKeyMessage Message string if tampered found in data fields indexed by string (protected)
* @param string $missingMessage Message string if missing field
* @return array Messages
*/
- protected function _debugCheckFields($dataFields, $expectedFields = [], $intKeyMessage = '', $stringKeyMessage = '', $missingMessage = '')
- {
+ protected function _debugCheckFields(
+ array $dataFields,
+ array $expectedFields = [],
+ string $intKeyMessage = '',
+ ?string $stringKeyMessage = '',
+ string $missingMessage = '',
+ ): array {
$messages = $this->_matchExistingFields($dataFields, $expectedFields, $intKeyMessage, $stringKeyMessage);
$expectedFieldsMessage = $this->_debugExpectedFields($expectedFields, $missingMessage);
if ($expectedFieldsMessage !== null) {
@@ -762,7 +776,7 @@ protected function _debugCheckFields($dataFields, $expectedFields = [], $intKeyM
* @param CakeRequest $request The request object to add into.
* @return bool
*/
- public function generateToken(CakeRequest $request)
+ public function generateToken(CakeRequest $request): bool
{
if (isset($request->params['requested']) && $request->params['requested'] === 1) {
if ($this->Session->check('_Token')) {
@@ -814,7 +828,7 @@ public function generateToken(CakeRequest $request)
* @throws SecurityException
* @return bool Valid csrf token.
*/
- protected function _validateCsrf(Controller $controller)
+ protected function _validateCsrf(Controller $controller): bool
{
$token = $this->Session->read('_Token');
$requestToken = $controller->request->data('_Token.key');
@@ -832,15 +846,12 @@ protected function _validateCsrf(Controller $controller)
if (!$this->_validateHmacToken($requestToken)) {
throw new SecurityException('CSRF token mismatch');
}
- // Check if token exists in session
- if (!isset($token['csrfTokens'][$requestToken])) {
- throw new SecurityException('CSRF token mismatch');
- }
- } else {
- // Legacy token format for backward compatibility
- if (!isset($token['csrfTokens'][$requestToken])) {
- throw new SecurityException('CSRF token mismatch');
- }
+ }
+
+ // Check if token exists in session
+ // or Legacy token format for backward compatibility
+ if (!isset($token['csrfTokens'][$requestToken])) {
+ throw new SecurityException('CSRF token mismatch');
}
if ($token['csrfTokens'][$requestToken] < time()) {
@@ -861,7 +872,7 @@ protected function _validateCsrf(Controller $controller)
* @param array $tokens An array of nonce => expires.
* @return array An array of nonce => expires.
*/
- protected function _expireTokens($tokens)
+ protected function _expireTokens(array $tokens): array
{
$now = time();
foreach ($tokens as $nonce => $expires) {
@@ -886,8 +897,11 @@ protected function _expireTokens($tokens)
* @return mixed Controller callback method's response
* @throws BadRequestException When the blackholeCallback is not callable.
*/
- protected function _callback(Controller $controller, $method, $params = [])
- {
+ protected function _callback(
+ Controller $controller,
+ string $method,
+ array $params = [],
+ ): mixed {
if (!is_callable([$controller, $method])) {
throw new BadRequestException(__d('cake_dev', 'The request has been black-holed'));
}
@@ -902,15 +916,19 @@ protected function _callback(Controller $controller, $method, $params = [])
* @param array $dataFields Fields array, containing the POST data fields
* @param array &$expectedFields Fields array, containing the expected fields we should have in POST
* @param string $intKeyMessage Message string if unexpected found in data fields indexed by int (not protected)
- * @param string $stringKeyMessage Message string if tampered found in data fields indexed by string (protected)
+ * @param string|null $stringKeyMessage Message string if tampered found in data fields indexed by string (protected)
* @return array Error messages
*/
- protected function _matchExistingFields($dataFields, &$expectedFields, $intKeyMessage, $stringKeyMessage)
- {
+ protected function _matchExistingFields(
+ array $dataFields,
+ array &$expectedFields,
+ string $intKeyMessage,
+ ?string $stringKeyMessage,
+ ): array {
$messages = [];
- foreach ((array)$dataFields as $key => $value) {
+ foreach ($dataFields as $key => $value) {
if (is_int($key)) {
- $foundKey = array_search($value, (array)$expectedFields);
+ $foundKey = array_search($value, $expectedFields);
if ($foundKey === false) {
$messages[] = sprintf($intKeyMessage, $value);
} else {
@@ -932,16 +950,18 @@ protected function _matchExistingFields($dataFields, &$expectedFields, $intKeyMe
*
* @param array $expectedFields Expected fields
* @param string $missingMessage Message template
- * @return string Error message about expected fields
+ * @return string|null Error message about expected fields
*/
- protected function _debugExpectedFields($expectedFields = [], $missingMessage = '')
- {
+ protected function _debugExpectedFields(
+ array $expectedFields = [],
+ string $missingMessage = '',
+ ): ?string {
if (count($expectedFields) === 0) {
return null;
}
$expectedFieldNames = [];
- foreach ((array)$expectedFields as $key => $expectedField) {
+ foreach ($expectedFields as $key => $expectedField) {
if (is_int($key)) {
$expectedFieldNames[] = $expectedField;
} else {
@@ -958,7 +978,7 @@ protected function _debugExpectedFields($expectedFields = [], $missingMessage =
*
* @return string Base64 encoded token containing random value and HMAC signature
*/
- protected function _createCsrfToken()
+ protected function _createCsrfToken(): string
{
// Generate random bytes (same as CakePHP 4.x)
$value = Security::randomBytes(static::TOKEN_VALUE_LENGTH);
@@ -978,7 +998,7 @@ protected function _createCsrfToken()
* @param string $token Base64 encoded token to validate
* @return bool True if token has valid HMAC signature
*/
- protected function _validateHmacToken($token)
+ protected function _validateHmacToken(string $token): bool
{
$decoded = base64_decode($token, true);
if ($decoded === false) {
diff --git a/src/Controller/Component/SessionComponent.php b/src/Controller/Component/SessionComponent.php
index 9337277cbd..d39c45fa5f 100644
--- a/src/Controller/Component/SessionComponent.php
+++ b/src/Controller/Component/SessionComponent.php
@@ -110,7 +110,7 @@ public function consume($name)
* @return bool true is session variable is set, false if not
* @link https://book.cakephp.org/2.0/en/core-libraries/components/sessions.html#SessionComponent::check
*/
- public function check($name)
+ public function check(string $name): bool
{
return CakeSession::check($name);
}
diff --git a/src/Controller/ComponentCollection.php b/src/Controller/ComponentCollection.php
index 8c694437f6..94f380c0a6 100644
--- a/src/Controller/ComponentCollection.php
+++ b/src/Controller/ComponentCollection.php
@@ -32,12 +32,19 @@
*/
class ComponentCollection extends ObjectCollection implements CakeEventListener
{
+ /**
+ * A hash of loaded objects, indexed by name
+ *
+ * @var array
+ */
+ protected array $_loaded = [];
+
/**
* The controller that this collection was initialized with.
*
- * @var Controller
+ * @var Controller|null
*/
- protected $_Controller = null;
+ protected ?Controller $_Controller = null;
/**
* Initializes all the Components for a controller.
@@ -46,7 +53,7 @@ class ComponentCollection extends ObjectCollection implements CakeEventListener
* @param Controller $controller Controller to initialize components for.
* @return void
*/
- public function init(Controller $controller)
+ public function init(Controller $controller): void
{
if (empty($controller->components)) {
return;
@@ -64,7 +71,7 @@ public function init(Controller $controller)
* @param Controller $controller Controller to set
* @return void
*/
- public function setController(Controller $controller)
+ public function setController(Controller $controller): void
{
$this->_Controller = $controller;
}
@@ -72,9 +79,9 @@ public function setController(Controller $controller)
/**
* Get the controller associated with the collection.
*
- * @return Controller Controller instance
+ * @return Controller|null Controller instance
*/
- public function getController()
+ public function getController(): ?Controller
{
return $this->_Controller;
}
@@ -94,36 +101,38 @@ public function getController()
* ```
* All calls to the `Email` component would use `AliasedEmail` instead.
*
- * @param string $component Component name to load
- * @param array $settings Settings for the component.
+ * @param class-string|string $name Component name to load
+ * @param array $options Settings for the component.
* @return Component A component object, Either the existing loaded component or a new one.
* @throws MissingComponentException when the component could not be found
*/
- public function load($component, $settings = [])
- {
- if (isset($settings['className'])) {
- $alias = $component;
- $component = $settings['className'];
+ public function load(
+ string $name,
+ array $options = [],
+ ): Component {
+ if (isset($options['className'])) {
+ $alias = $name;
+ $name = $options['className'];
}
- [$plugin, $name] = pluginSplit($component, true);
+ [$plugin, $_name] = pluginSplit($name, true);
if (!isset($alias)) {
- $alias = $name;
+ $alias = $_name;
}
if (isset($this->_loaded[$alias])) {
return $this->_loaded[$alias];
}
- $componentClass = App::className($component, 'Controller/Component', 'Component');
+ $componentClass = App::className($name, 'Controller/Component', 'Component');
if (!$componentClass) {
throw new MissingComponentException([
- 'class' => $name . 'Component',
+ 'class' => $_name . 'Component',
'plugin' => $plugin ? substr($plugin, 0, -1) : null,
]);
}
- $this->_loaded[$alias] = new $componentClass($this, $settings);
- $enable = $settings['enabled'] ?? true;
+ $this->_loaded[$alias] = new $componentClass($this, $options);
+ $enable = $options['enabled'] ?? true;
if ($enable) {
$this->enable($alias);
}
diff --git a/src/Controller/Controller.php b/src/Controller/Controller.php
index eeade4132d..2b1b691c04 100644
--- a/src/Controller/Controller.php
+++ b/src/Controller/Controller.php
@@ -66,22 +66,23 @@
* using Router::connect().
*
* @package Cake.Controller
- * @property AclComponent $Acl
- * @property AuthComponent $Auth
- * @property CookieComponent $Cookie
- * @property EmailComponent $Email
- * @property FlashComponent $Flash
- * @property PaginatorComponent $Paginator
- * @property RequestHandlerComponent $RequestHandler
- * @property SecurityComponent $Security
- * @property SessionComponent $Session
- * @property string $action The action handling the current request. Deprecated, use CakeRequest::$action instead.
- * @property string $base Base URL path. Deprecated, use CakeRequest::$base instead.
- * @property array $data POST data. Deprecated, use CakeRequest::$data instead.
- * @property string $here The full address to the current request. Deprecated, use CakeRequest::$here instead.
- * @property array $paginate Pagination settings.
- * @property array $params Array of parameters parsed from the URL. Deprecated, use CakeRequest::$params instead.
- * @property string $webroot Webroot path segment for the request.
+ * @property AclComponent $Acl
+ * @property AuthComponent $Auth
+ * @property CookieComponent $Cookie
+ * @property EmailComponent $Email
+ * @property FlashComponent $Flash
+ * @property PaginatorComponent $Paginator
+ * @property RequestHandlerComponent $RequestHandler
+ * @property SecurityComponent $Security
+ * @property SessionComponent $Session
+ * @property string $action The action handling the current request. Deprecated, use CakeRequest::$action instead.
+ * @property string $base Base URL path. Deprecated, use CakeRequest::$base instead.
+ * @property array $data POST data. Deprecated, use CakeRequest::$data instead.
+ * @property string $here The full address to the current request. Deprecated, use CakeRequest::$here instead.
+ * @property array $paginate Pagination settings.
+ * @property array $params Array of parameters parsed from the URL. Deprecated, use CakeRequest::$params instead.
+ * @property string $webroot Webroot path segment for the request.
+ * @property string $subDir
* @link https://book.cakephp.org/2.0/en/controllers.html
*/
class Controller extends CakeObject implements CakeEventListener
@@ -116,10 +117,10 @@ class Controller extends CakeObject implements CakeEventListener
*
* The default value is `true`.
*
- * @var array|bool
+ * @var array|bool|null
* @link https://book.cakephp.org/2.0/en/controllers.html#components-helpers-and-uses
*/
- public array|bool $uses = true;
+ public array|bool|null $uses = true;
/**
* An array containing the names of helpers this controller uses. The array elements should
@@ -155,7 +156,7 @@ class Controller extends CakeObject implements CakeEventListener
*
* @var string
*/
- protected string $_responseClass = 'CakeResponse';
+ protected string $_responseClass = CakeResponse::class;
/**
* The name of the views subfolder containing views for this controller.
@@ -274,25 +275,25 @@ class Controller extends CakeObject implements CakeEventListener
* $cacheAction can also be set to a strtotime() compatible string. This
* marks all the actions in the controller for view caching.
*
- * @var mixed
+ * @var array|string|int|false
* @link https://book.cakephp.org/2.0/en/core-libraries/helpers/cache.html#additional-configuration-options
*/
- public $cacheAction = false;
+ public array|string|int|false $cacheAction = false;
/**
* Holds all params passed and named.
*
- * @var mixed
+ * @var array
*/
- public $passedArgs = [];
+ public array $passedArgs = [];
/**
* Triggers Scaffolding
*
- * @var mixed
+ * @var string|false|null
* @link https://book.cakephp.org/2.0/en/controllers/scaffolding.html
*/
- public $scaffold = false;
+ public string|false|null $scaffold = false;
/**
* Holds current methods of the controller. This is a list of all the methods reachable
@@ -397,7 +398,7 @@ public function __construct(?CakeRequest $request = null, ?CakeResponse $respons
* @param string $name Property name to check.
* @return bool
*/
- public function __isset($name)
+ public function __isset(string $name): bool
{
switch ($name) {
case 'base':
@@ -437,7 +438,7 @@ public function __isset($name)
* @param string $name The name of the requested value
* @return mixed The requested value for valid variables/aliases else null
*/
- public function __get($name)
+ public function __get(string $name): mixed
{
switch ($name) {
case 'base':
@@ -467,7 +468,7 @@ public function __get($name)
* @param mixed $value Value to set.
* @return void
*/
- public function __set($name, $value)
+ public function __set(string $name, mixed $value): void
{
switch ($name) {
case 'base':
@@ -507,7 +508,7 @@ public function __set($name, $value)
* @param CakeRequest $request Request instance.
* @return void
*/
- public function setRequest(CakeRequest $request)
+ public function setRequest(CakeRequest $request): void
{
$this->request = $request;
$this->plugin = isset($request->params['plugin']) ? Inflector::camelize($request->params['plugin']) : null;
@@ -534,7 +535,7 @@ public function setRequest(CakeRequest $request)
* @throws MissingActionException When actions are not defined and scaffolding is
* not enabled.
*/
- public function invokeAction(CakeRequest $request)
+ public function invokeAction(CakeRequest $request): mixed
{
try {
$method = new ReflectionMethod($this, $request->params['action']);
@@ -571,8 +572,10 @@ public function invokeAction(CakeRequest $request)
* @param CakeRequest $request The request to check.
* @return bool
*/
- protected function _isPrivateAction(ReflectionMethod $method, CakeRequest $request)
- {
+ protected function _isPrivateAction(
+ ReflectionMethod $method,
+ CakeRequest $request,
+ ): bool {
$privateAction = (
$method->name[0] === '_' ||
!$method->isPublic() ||
@@ -596,7 +599,7 @@ protected function _isPrivateAction(ReflectionMethod $method, CakeRequest $reque
* @param CakeRequest $request Request instance.
* @return Scaffold
*/
- protected function _getScaffold(CakeRequest $request)
+ protected function _getScaffold(CakeRequest $request): Scaffold
{
return new Scaffold($this, $request);
}
@@ -607,7 +610,7 @@ protected function _getScaffold(CakeRequest $request)
*
* @return void
*/
- protected function _mergeControllerVars()
+ protected function _mergeControllerVars(): void
{
$pluginController = $pluginDot = null;
@@ -622,7 +625,7 @@ protected function _mergeControllerVars()
// If not found via resolved FQCN, search for AppController in parent class chain
if (!$mergeParent) {
$currentClass = get_parent_class($this);
- while ($currentClass && !$mergeParent) {
+ while ($currentClass) {
// Get short class name from current parent in chain
$shortName = $currentClass;
if (str_contains($currentClass, '\\')) {
@@ -661,7 +664,7 @@ protected function _mergeControllerVars()
if ($mergeParent || !empty($pluginController)) {
$appVars = get_class_vars($this->_mergeParent);
$merge = ['components', 'helpers'];
- $this->_mergeVars($merge, $this->_mergeParent, true);
+ $this->_mergeVars($merge, $this->_mergeParent);
}
if ($this->uses === null) {
@@ -694,7 +697,7 @@ protected function _mergeControllerVars()
* @param array $merge The data to merge in.
* @return void
*/
- protected function _mergeUses($merge)
+ protected function _mergeUses(array $merge): void
{
if (!isset($merge['uses']) || $merge['uses'] === true || !is_array($this->uses)) {
return;
@@ -726,12 +729,12 @@ public function implementedEvents(): array
* see Controller::loadModel(); for more info.
* Loads Components and prepares them for initialization.
*
- * @return mixed true if models found and instance created.
+ * @return bool true if models found and instance created.
* @see Controller::loadModel()
* @link https://book.cakephp.org/2.0/en/controllers.html#Controller::constructClasses
* @throws MissingModelException
*/
- public function constructClasses()
+ public function constructClasses(): bool
{
$this->_mergeControllerVars();
if ($this->uses) {
@@ -750,7 +753,7 @@ public function constructClasses()
*
* @return CakeEventManager
*/
- public function getEventManager()
+ public function getEventManager(): CakeEventManager
{
if (empty($this->_eventManager)) {
$this->_eventManager = new CakeEventManager();
@@ -773,7 +776,7 @@ public function getEventManager()
* @triggers Controller.initialize $this
* @triggers Controller.startup $this
*/
- public function startupProcess()
+ public function startupProcess(): void
{
$this->getEventManager()->dispatch(new CakeEvent('Controller.initialize', $this));
$this->getEventManager()->dispatch(new CakeEvent('Controller.startup', $this));
@@ -789,7 +792,7 @@ public function startupProcess()
* @return void
* @triggers Controller.shutdown $this
*/
- public function shutdownProcess()
+ public function shutdownProcess(): void
{
$this->getEventManager()->dispatch(new CakeEvent('Controller.shutdown', $this));
}
@@ -797,7 +800,7 @@ public function shutdownProcess()
/**
* Queries & sets valid HTTP response codes & messages.
*
- * @param array|int $code If $code is an integer, then the corresponding code/message is
+ * @param array|int|null $code If $code is an integer, then the corresponding code/message is
* returned if it exists, null if it does not exist. If $code is an array,
* then the 'code' and 'message' keys of each nested array are added to the default
* HTTP codes. Example:
@@ -808,11 +811,11 @@ public function shutdownProcess()
* 701 => 'Unicorn Moved',
* 800 => 'Unexpected Minotaur'
* )); // sets these new values, and returns true
- * @return array|true|null Associative array of the HTTP codes as keys, and the message
+ * @return array|bool|null Associative array of the HTTP codes as keys, and the message
* strings as values, or null of the given $code does not exist.
* @deprecated 3.0.0 Since 2.4. Will be removed in 3.0. Use CakeResponse::httpCodes().
*/
- public function httpCodes($code = null)
+ public function httpCodes(array|int|null $code = null): array|bool|null
{
return $this->response->httpCodes($code);
}
@@ -827,10 +830,11 @@ public function httpCodes($code = null)
* @param string|int|null $id Initial ID the instanced model class should have
* @return bool True if the model was found
* @throws MissingModelException if the model class cannot be found.
- * @phpstan-assert T $this->{$modelClass}
*/
- public function loadModel($modelClass = null, $id = null)
- {
+ public function loadModel(
+ ?string $modelClass = null,
+ string|int|null $id = null,
+ ): bool {
if ($modelClass === null) {
$modelClass = $this->modelClass;
}
@@ -856,20 +860,23 @@ public function loadModel($modelClass = null, $id = null)
* Redirects to given $url, after turning off $this->autoRender.
* Script execution is halted after the redirect.
*
- * @param array|string $url A string or array-based URL pointing to another location within the app,
+ * @param array|string|null $url A string or array-based URL pointing to another location within the app,
* or an absolute URL
* @param array|string|int|null $status HTTP status code (eg: 301). Defaults to 302 when null is passed.
* @param bool $exit If true, exit() will be called after the redirect
* @return CakeResponse|null
- * @triggers Controller.beforeRedirect $this, array($url, $status, $exit)
+ * @triggers Controller.beforeRedirect $this, [$url, $status, $exit]
* @link https://book.cakephp.org/2.0/en/controllers.html#Controller::redirect
*/
- public function redirect($url, $status = null, $exit = true)
- {
+ public function redirect(
+ array|string|null $url,
+ array|string|int|null $status = null,
+ bool $exit = true,
+ ): ?CakeResponse {
$this->autoRender = false;
if (is_array($status)) {
- extract($status, EXTR_OVERWRITE);
+ extract($status);
}
$event = new CakeEvent('Controller.beforeRedirect', $this, [$url, $status, $exit]);
@@ -880,13 +887,13 @@ public function redirect($url, $status = null, $exit = true)
return null;
}
$response = $event->result;
- extract($this->_parseBeforeRedirect($response, $url, $status, $exit), EXTR_OVERWRITE);
+ extract($this->_parseBeforeRedirect($response, $url, $status, $exit));
if ($url !== null) {
$this->response->header('Location', Router::url($url, true));
}
- if (is_string($status)) {
+ if (is_string($status)) { // @phpstan-ignore-line
$codes = array_flip($this->response->httpCodes());
if (isset($codes[$status])) {
$status = $codes[$status];
@@ -909,24 +916,36 @@ public function redirect($url, $status = null, $exit = true)
/**
* Parse beforeRedirect Response
*
- * @param mixed $response Response from beforeRedirect callback
- * @param array|string $url The same value of beforeRedirect
- * @param int $status The same value of beforeRedirect
+ * @param mixed|array{
+ * url: array|string|null,
+ * status: int|null,
+ * exit: bool
+ * }|array $response Response from beforeRedirect callback
+ * @param array|string|null $url The same value of beforeRedirect
+ * @param string|int|null $status The same value of beforeRedirect
* @param bool $exit The same value of beforeRedirect
- * @return array Array with keys url, status and exit
+ * @return array{
+ * url: array|string|null,
+ * status: int|null,
+ * exit: bool
+ * } Array with keys url, status and exit
*/
- protected function _parseBeforeRedirect($response, $url, $status, $exit)
+ protected function _parseBeforeRedirect(mixed $response, array|string|null $url, string|int|null $status, bool $exit): array
{
if (is_array($response) && array_key_exists(0, $response)) {
foreach ($response as $resp) {
if (is_array($resp) && isset($resp['url'])) {
- extract($resp, EXTR_OVERWRITE);
+ extract($resp);
} elseif ($resp !== null) {
$url = $resp;
}
}
} elseif (is_array($response)) {
- extract($response, EXTR_OVERWRITE);
+ extract($response);
}
return compact('url', 'status', 'exit');
@@ -935,11 +954,11 @@ protected function _parseBeforeRedirect($response, $url, $status, $exit)
/**
* Convenience and object wrapper method for CakeResponse::header().
*
- * @param string $status The header message that is being set.
+ * @param array|string|null $status The header message that is being set.
* @return void
* @deprecated 3.0.0 Will be removed in 3.0. Use CakeResponse::header().
*/
- public function header($status)
+ public function header(array|string|null $status): void
{
$this->response->header($status);
}
@@ -953,7 +972,7 @@ public function header($status)
* @return void
* @link https://book.cakephp.org/2.0/en/controllers.html#interacting-with-views
*/
- public function set($one, $two = null)
+ public function set(array|string $one, mixed $two = null): void
{
if (is_array($one)) {
if (is_array($two)) {
@@ -981,7 +1000,7 @@ public function set($one, $two = null)
* @param mixed ...$args Any other parameters passed to this method will be passed as parameters to the new action.
* @return mixed Returns the return value of the called action
*/
- public function setAction($action, ...$args)
+ public function setAction(string $action, mixed ...$args): mixed
{
$this->request->params['action'] = $action;
$this->view = $action;
@@ -996,7 +1015,7 @@ public function setAction($action, ...$args)
* @return int Number of errors
* @deprecated 3.0.0 This method will be removed in 3.0
*/
- public function validate(...$args)
+ public function validate(mixed ...$args): int
{
$errors = call_user_func_array([&$this, 'validateErrors'], $args);
@@ -1017,7 +1036,7 @@ public function validate(...$args)
* @return array|false Validation errors, or false if none
* @deprecated 3.0.0 This method will be removed in 3.0
*/
- public function validateErrors(...$objects)
+ public function validateErrors(mixed ...$objects): array|false
{
if (empty($objects)) {
return false;
@@ -1031,20 +1050,21 @@ public function validateErrors(...$objects)
$object->set($object->data);
$errors = array_merge($errors, $object->invalidFields());
}
+ $this->validationErrors = (!empty($errors) ? $errors : false);
- return $this->validationErrors = (!empty($errors) ? $errors : false);
+ return $this->validationErrors;
}
/**
* Instantiates the correct view class, hands it its data, and uses it to render the view output.
*
- * @param string|bool $view View to use for rendering
- * @param string $layout Layout to use
- * @return CakeResponse A response object containing the rendered view.
+ * @param string|bool|null $view View to use for rendering
+ * @param string|null $layout Layout to use
+ * @return CakeResponse|null A response object containing the rendered view.
* @triggers Controller.beforeRender $this
* @link https://book.cakephp.org/2.0/en/controllers.html#Controller::render
*/
- public function render($view = null, $layout = null)
+ public function render(string|bool|null $view = null, ?string $layout = null): ?CakeResponse
{
$event = new CakeEvent('Controller.beforeRender', $this);
$this->getEventManager()->dispatch($event);
@@ -1097,12 +1117,12 @@ public function render($view = null, $layout = null)
/**
* Returns the referring URL for this request.
*
- * @param string $default Default URL to use if HTTP_REFERER cannot be read from headers
+ * @param array|string|null $default Default URL to use if HTTP_REFERER cannot be read from headers
* @param bool $local If true, restrict referring URLs to local server
* @return string Referring URL
* @link https://book.cakephp.org/2.0/en/controllers.html#Controller::referer
*/
- public function referer($default = null, $local = false)
+ public function referer(array|string|null $default = null, bool $local = false): string
{
if (!$this->request) {
return '/';
@@ -1123,7 +1143,7 @@ public function referer($default = null, $local = false)
* @link https://book.cakephp.org/2.0/en/controllers.html#Controller::disableCache
* @deprecated 3.0.0 Will be removed in 3.0. Use CakeResponse::disableCache().
*/
- public function disableCache()
+ public function disableCache(): void
{
$this->response->disableCache();
}
@@ -1134,15 +1154,19 @@ public function disableCache()
* Does not work if the current debug level is higher than 0.
*
* @param string $message Message to display to the user
- * @param array|string $url Relative string or array-based URL to redirect to after the time expires
+ * @param array|string|null $url Relative string or array-based URL to redirect to after the time expires
* @param int $pause Time to show the message
* @param string $layout Layout you want to use, defaults to 'flash'
* @return void
* @link https://book.cakephp.org/2.0/en/controllers.html#Controller::flash
* @deprecated 3.0.0 Will be removed in 3.0. Use Flash::set() with version 2.7+ or Session::setFlash() prior to 2.7.
*/
- public function flash($message, $url, $pause = 1, $layout = 'flash')
- {
+ public function flash(
+ string $message,
+ array|string|null $url,
+ int $pause = 1,
+ string $layout = 'flash',
+ ): void {
$this->autoRender = false;
$this->set('url', Router::url($url));
$this->set('message', $message);
@@ -1159,8 +1183,8 @@ public function flash($message, $url, $pause = 1, $layout = 'flash')
* is vulnerable creating conditions containing SQL injection. While we
* attempt to raise exceptions.
*
- * @param array $data POST'ed data organized by model and field
- * @param array|string $op A string containing an SQL comparison operator, or an array matching operators
+ * @param array|string|null $data POST'ed data organized by model and field
+ * @param array|string|null $op A string containing an SQL comparison operator, or an array matching operators
* to fields
* @param string $bool SQL boolean operator: AND, OR, XOR, etc.
* @param bool $exclusive If true, and $op is an array, fields not included in $op will not be
@@ -1169,8 +1193,12 @@ public function flash($message, $url, $pause = 1, $layout = 'flash')
* @deprecated 3.0.0 Will be removed in 3.0.
* @throws RuntimeException when unsafe operators are found.
*/
- public function postConditions($data = [], $op = null, $bool = 'AND', $exclusive = false)
- {
+ public function postConditions(
+ array|string|null $data = [],
+ array|string|null $op = null,
+ string $bool = 'AND',
+ bool $exclusive = false,
+ ): ?array {
if (!is_array($data) || empty($data)) {
if (!empty($this->request->data)) {
$data = $this->request->data;
@@ -1228,15 +1256,21 @@ public function postConditions($data = [], $op = null, $bool = 'AND', $exclusive
/**
* Handles automatic pagination of model records.
*
- * @param Model|string $object Model to paginate (e.g: model instance, or 'Model', or 'Model.InnerModel')
+ * @param Model|string|null $object Model to paginate (e.g: model instance, or 'Model', or 'Model.InnerModel')
* @param array|string $scope Conditions to use while paginating
* @param array $whitelist List of allowed options for paging
* @return array Model query results
* @link https://book.cakephp.org/2.0/en/controllers.html#Controller::paginate
*/
- public function paginate($object = null, $scope = [], $whitelist = [])
- {
- return $this->Components->load('Paginator', $this->paginate)->paginate($object, $scope, $whitelist);
+ public function paginate(
+ Model|string|null $object = null,
+ array|string $scope = [],
+ array $whitelist = [],
+ ): array {
+ /** @var PaginatorComponent $paginator */
+ $paginator = $this->Components->load('Paginator', $this->paginate);
+
+ return $paginator->paginate($object, $scope, $whitelist);
}
/**
@@ -1246,7 +1280,7 @@ public function paginate($object = null, $scope = [], $whitelist = [])
* @return void
* @link https://book.cakephp.org/2.0/en/controllers.html#request-life-cycle-callbacks
*/
- public function beforeFilter()
+ public function beforeFilter(): void
{
}
@@ -1270,18 +1304,22 @@ public function beforeRender(): void
* return a string which will be interpreted as the URL to redirect to or return associative array with
* key 'url' and optionally 'status' and 'exit'.
*
- * @param array|string $url A string or array-based URL pointing to another location within the app,
+ * @param array|string|null $url A string or array-based URL pointing to another location within the app,
* or an absolute URL
- * @param int $status Optional HTTP status code (eg: 404)
+ * @param array|string|int|null $status Optional HTTP status code (eg: 404)
* @param bool $exit If true, exit() will be called after the redirect
- * @return mixed
+ * @return array|string|false|null
* false to stop redirection event,
* string controllers a new redirection URL or
* array with the keys url, status and exit to be used by the redirect method.
* @link https://book.cakephp.org/2.0/en/controllers.html#request-life-cycle-callbacks
*/
- public function beforeRedirect($url, $status = null, $exit = true)
- {
+ public function beforeRedirect(
+ array|string|null $url,
+ array|string|int|null $status = null,
+ bool $exit = true,
+ ): array|string|false|null {
+ return null;
}
/**
@@ -1290,7 +1328,7 @@ public function beforeRedirect($url, $status = null, $exit = true)
* @return void
* @link https://book.cakephp.org/2.0/en/controllers.html#request-life-cycle-callbacks
*/
- public function afterFilter()
+ public function afterFilter(): void
{
}
@@ -1301,7 +1339,7 @@ public function afterFilter()
* @return bool Success
* @link https://book.cakephp.org/2.0/en/controllers.html#callbacks
*/
- public function beforeScaffold($method)
+ public function beforeScaffold(string $method): bool
{
return true;
}
@@ -1314,7 +1352,7 @@ public function beforeScaffold($method)
* @see Controller::beforeScaffold()
* @deprecated 3.0.0 Will be removed in 3.0.
*/
- protected function _beforeScaffold($method)
+ protected function _beforeScaffold(string $method): bool
{
return $this->beforeScaffold($method);
}
@@ -1326,7 +1364,7 @@ protected function _beforeScaffold($method)
* @return bool Success
* @link https://book.cakephp.org/2.0/en/controllers.html#callbacks
*/
- public function afterScaffoldSave($method)
+ public function afterScaffoldSave(string $method): bool
{
return true;
}
@@ -1339,7 +1377,7 @@ public function afterScaffoldSave($method)
* @see Controller::afterScaffoldSave()
* @deprecated 3.0.0 Will be removed in 3.0.
*/
- protected function _afterScaffoldSave($method)
+ protected function _afterScaffoldSave(string $method): bool
{
return $this->afterScaffoldSave($method);
}
@@ -1351,7 +1389,7 @@ protected function _afterScaffoldSave($method)
* @return bool Success
* @link https://book.cakephp.org/2.0/en/controllers.html#callbacks
*/
- public function afterScaffoldSaveError($method)
+ public function afterScaffoldSaveError(string $method): bool
{
return true;
}
@@ -1364,7 +1402,7 @@ public function afterScaffoldSaveError($method)
* @see Controller::afterScaffoldSaveError()
* @deprecated 3.0.0 Will be removed in 3.0.
*/
- protected function _afterScaffoldSaveError($method)
+ protected function _afterScaffoldSaveError(string $method): bool
{
return $this->afterScaffoldSaveError($method);
}
@@ -1378,7 +1416,7 @@ protected function _afterScaffoldSaveError($method)
* @return bool Success
* @link https://book.cakephp.org/2.0/en/controllers.html#callbacks
*/
- public function scaffoldError($method)
+ public function scaffoldError(string $method): bool
{
return false;
}
@@ -1391,7 +1429,7 @@ public function scaffoldError($method)
* @see Controller::scaffoldError()
* @deprecated 3.0.0 Will be removed in 3.0.
*/
- protected function _scaffoldError($method)
+ protected function _scaffoldError(string $method): bool
{
return $this->scaffoldError($method);
}
@@ -1401,7 +1439,7 @@ protected function _scaffoldError($method)
*
* @return View
*/
- protected function _getViewObject()
+ protected function _getViewObject(): View
{
$viewClass = $this->viewClass;
if ($this->viewClass !== 'View') {
diff --git a/src/Controller/Scaffold.php b/src/Controller/Scaffold.php
index 73a6eed543..febad3b8e5 100644
--- a/src/Controller/Scaffold.php
+++ b/src/Controller/Scaffold.php
@@ -23,12 +23,12 @@
use Cake\Core\Configure;
use Cake\Error\MethodNotAllowedException;
use Cake\Error\MissingActionException;
-use Cake\Error\MissingDatabaseException;
use Cake\Error\MissingModelException;
use Cake\Error\NotFoundException;
use Cake\Model\ConnectionManager;
use Cake\Model\Model;
use Cake\Network\CakeRequest;
+use Cake\Network\CakeResponse;
use Cake\Utility\Inflector;
/**
@@ -70,7 +70,7 @@ class Scaffold
*
* @var string
*/
- public string $viewPath;
+ public string $viewPath = '';
/**
* Name of layout to use with this View.
@@ -132,7 +132,7 @@ class Scaffold
/**
* @var mixed
*/
- public $scaffoldActions;
+ public mixed $scaffoldActions;
/**
* Construct and set up given controller with given parameters.
@@ -200,10 +200,10 @@ public function __construct(Controller $controller, CakeRequest $request)
* Renders a view action of scaffolded model.
*
* @param CakeRequest $request Request Object for scaffolding
- * @return mixed A rendered view of a row from Models database table
+ * @return CakeResponse|null A rendered view of a row from Models database table
* @throws NotFoundException
*/
- protected function _scaffoldView(CakeRequest $request)
+ protected function _scaffoldView(CakeRequest $request): ?CakeResponse
{
if ($this->controller->beforeScaffold('view')) {
if (isset($request->params['pass'][0])) {
@@ -212,25 +212,29 @@ protected function _scaffoldView(CakeRequest $request)
if (!$this->ScaffoldModel->exists()) {
throw new NotFoundException(__d('cake', 'Invalid %s', Inflector::humanize($this->modelKey)));
}
+
$this->ScaffoldModel->recursive = 1;
$this->controller->request->data = $this->ScaffoldModel->read();
$this->controller->set(
Inflector::variable($this->controller->modelClass),
$this->request->data,
);
- $this->controller->render($this->request['action'], $this->layout);
+
+ return $this->controller->render($this->request['action'], $this->layout);
} elseif ($this->controller->scaffoldError('view') === false) {
return $this->_scaffoldError();
}
+
+ return null;
}
/**
* Renders index action of scaffolded model.
*
* @param CakeRequest $params Parameters for scaffolding
- * @return mixed A rendered view listing rows from Models database table
+ * @return CakeResponse|null A rendered view listing rows from Models database table
*/
- protected function _scaffoldIndex(CakeRequest $params)
+ protected function _scaffoldIndex(CakeRequest $params): ?CakeResponse
{
if ($this->controller->beforeScaffold('index')) {
$this->ScaffoldModel->recursive = 0;
@@ -242,6 +246,8 @@ protected function _scaffoldIndex(CakeRequest $params)
} elseif ($this->controller->scaffoldError('index') === false) {
return $this->_scaffoldError();
}
+
+ return null;
}
/**
@@ -264,10 +270,10 @@ protected function _scaffoldForm(string $action = 'edit'): void
*
* @param CakeRequest $request Request Object for scaffolding
* @param string $action add or edit
- * @return mixed|void Success on save/update, add/edit form if data is empty or error if save or update fails
+ * @return CakeResponse|bool|null Success on save/update, add/edit form if data is empty or error if save or update fails
* @throws NotFoundException
*/
- protected function _scaffoldSave(CakeRequest $request, $action = 'edit')
+ protected function _scaffoldSave(CakeRequest $request, string $action = 'edit'): CakeResponse|bool|null
{
$formAction = 'edit';
$success = __d('cake', 'updated');
@@ -320,7 +326,7 @@ protected function _scaffoldSave(CakeRequest $request, $action = 'edit')
foreach ($this->ScaffoldModel->belongsTo as $assocName => $assocData) {
$varName = Inflector::variable(Inflector::pluralize(
- preg_replace('/(?:_id)$/', '', $assocData['foreignKey']),
+ preg_replace('/_id$/', '', $assocData['foreignKey']),
));
$this->controller->set($varName, $this->ScaffoldModel->{$assocName}->find('list'));
}
@@ -333,22 +339,25 @@ protected function _scaffoldSave(CakeRequest $request, $action = 'edit')
} elseif ($this->controller->scaffoldError($action) === false) {
return $this->_scaffoldError();
}
+
+ return null;
}
/**
* Performs a delete on given scaffolded Model.
*
* @param CakeRequest $request Request for scaffolding
- * @return mixed Success on delete, error if delete fails
+ * @return CakeResponse|null Success on delete, error if delete fails
* @throws MethodNotAllowedException When HTTP method is not a DELETE
* @throws NotFoundException When id being deleted does not exist.
*/
- protected function _scaffoldDelete(CakeRequest $request)
+ protected function _scaffoldDelete(CakeRequest $request): ?CakeResponse
{
if ($this->controller->beforeScaffold('delete')) {
if (!$request->is('post')) {
throw new MethodNotAllowedException();
}
+
$id = false;
if (isset($request->params['pass'][0])) {
$id = $request->params['pass'][0];
@@ -357,11 +366,13 @@ protected function _scaffoldDelete(CakeRequest $request)
if (!$this->ScaffoldModel->exists()) {
throw new NotFoundException(__d('cake', 'Invalid %s', Inflector::humanize($this->modelClass)));
}
+
if ($this->ScaffoldModel->delete()) {
$message = __d('cake', 'The %1$s with id: %2$s has been deleted.', Inflector::humanize($this->modelClass), $id);
return $this->_sendMessage($message, 'success');
}
+
$message = __d(
'cake',
'There was an error deleting the %1$s with id: %2$s',
@@ -373,6 +384,8 @@ protected function _scaffoldDelete(CakeRequest $request)
} elseif ($this->controller->scaffoldError('delete') === false) {
return $this->_scaffoldError();
}
+
+ return null;
}
/**
@@ -383,22 +396,25 @@ protected function _scaffoldDelete(CakeRequest $request)
* @param string $element Flash template to use
* @return CakeResponse|null
*/
- protected function _sendMessage($message, $element = 'default')
+ protected function _sendMessage(string $message, string $element = 'default'): ?CakeResponse
{
if ($this->_validSession) {
$this->controller->Flash->set($message, compact('element'));
return $this->controller->redirect($this->redirect);
}
+
$this->controller->flash($message, $this->redirect);
+
+ return null;
}
/**
* Show a scaffold error
*
- * @return mixed A rendered view showing the error
+ * @return CakeResponse|null A rendered view showing the error
*/
- protected function _scaffoldError()
+ protected function _scaffoldError(): ?CakeResponse
{
return $this->controller->render('error', $this->layout);
}
@@ -411,64 +427,60 @@ protected function _scaffoldError()
* @param CakeRequest $request Request object for scaffolding
* @return void
* @throws MissingActionException When methods are not scaffolded.
- * @throws MissingDatabaseException When the database connection is undefined.
*/
protected function _scaffold(CakeRequest $request): void
{
- $db = ConnectionManager::getDataSource($this->ScaffoldModel->useDbConfig);
+ ConnectionManager::getDataSource($this->ScaffoldModel->useDbConfig);
+
$prefixes = Configure::read('Routing.prefixes');
$scaffoldPrefix = $this->scaffoldActions;
- if (isset($db)) {
- if (empty($this->scaffoldActions)) {
- $this->scaffoldActions = [
- 'index', 'list', 'view', 'add', 'create', 'edit', 'update', 'delete',
- ];
- } elseif (!empty($prefixes) && in_array($scaffoldPrefix, $prefixes)) {
- $this->scaffoldActions = [
- $scaffoldPrefix . '_index',
- $scaffoldPrefix . '_list',
- $scaffoldPrefix . '_view',
- $scaffoldPrefix . '_add',
- $scaffoldPrefix . '_create',
- $scaffoldPrefix . '_edit',
- $scaffoldPrefix . '_update',
- $scaffoldPrefix . '_delete',
- ];
- }
+ if (empty($this->scaffoldActions)) {
+ $this->scaffoldActions = [
+ 'index', 'list', 'view', 'add', 'create', 'edit', 'update', 'delete',
+ ];
+ } elseif (!empty($prefixes) && in_array($scaffoldPrefix, $prefixes)) {
+ $this->scaffoldActions = [
+ $scaffoldPrefix . '_index',
+ $scaffoldPrefix . '_list',
+ $scaffoldPrefix . '_view',
+ $scaffoldPrefix . '_add',
+ $scaffoldPrefix . '_create',
+ $scaffoldPrefix . '_edit',
+ $scaffoldPrefix . '_update',
+ $scaffoldPrefix . '_delete',
+ ];
+ }
- if (in_array($request->params['action'], $this->scaffoldActions)) {
- if (!empty($prefixes)) {
- $request->params['action'] = str_replace($scaffoldPrefix . '_', '', $request->params['action']);
- }
- switch ($request->params['action']) {
- case 'index':
- case 'list':
- $this->_scaffoldIndex($request);
- break;
- case 'view':
- $this->_scaffoldView($request);
- break;
- case 'add':
- case 'create':
- $this->_scaffoldSave($request, 'add');
- break;
- case 'edit':
- case 'update':
- $this->_scaffoldSave($request, 'edit');
- break;
- case 'delete':
- $this->_scaffoldDelete($request);
- break;
- }
- } else {
- throw new MissingActionException([
- 'controller' => $this->controller::class,
- 'action' => $request->action,
- ]);
+ if (in_array($request->params['action'], $this->scaffoldActions)) {
+ if (!empty($prefixes)) {
+ $request->params['action'] = str_replace($scaffoldPrefix . '_', '', $request->params['action']);
+ }
+ switch ($request->params['action']) {
+ case 'index':
+ case 'list':
+ $this->_scaffoldIndex($request);
+ break;
+ case 'view':
+ $this->_scaffoldView($request);
+ break;
+ case 'add':
+ case 'create':
+ $this->_scaffoldSave($request, 'add');
+ break;
+ case 'edit':
+ case 'update':
+ $this->_scaffoldSave($request, 'edit');
+ break;
+ case 'delete':
+ $this->_scaffoldDelete($request);
+ break;
}
} else {
- throw new MissingDatabaseException(['connection' => $this->ScaffoldModel->useDbConfig]);
+ throw new MissingActionException([
+ 'controller' => $this->controller::class,
+ 'action' => $request->action,
+ ]);
}
}
@@ -477,7 +489,7 @@ protected function _scaffold(CakeRequest $request): void
*
* @return array Associations for model
*/
- protected function _associations()
+ protected function _associations(): array
{
$keys = ['belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'];
$associations = [];
diff --git a/src/Core/App.php b/src/Core/App.php
index 3ccde99827..d965909cc4 100644
--- a/src/Core/App.php
+++ b/src/Core/App.php
@@ -103,7 +103,7 @@ class App
*
* @var array
*/
- public static $types = [
+ public static array $types = [
'class' => ['extends' => null, 'core' => true],
'file' => ['extends' => null, 'core' => true],
'model' => ['extends' => 'AppModel', 'core' => false],
@@ -123,56 +123,56 @@ class App
*
* @var array
*/
- public static $search = [];
+ public static array $search = [];
/**
* Whether or not to return the file that is loaded.
*
* @var bool
*/
- public static $return = false;
+ public static bool $return = false;
/**
* Holds key/value pairs of $type => file path.
*
* @var array
*/
- protected static $_map = [];
+ protected static array $_map = [];
/**
* Holds and key => value array of object types.
*
* @var array
*/
- protected static $_objects = [];
+ protected static array $_objects = [];
/**
* Holds the location of each class
*
* @var array
*/
- protected static $_classMap = [];
+ protected static array $_classMap = [];
/**
* Holds the possible paths for each package name
*
* @var array
*/
- protected static $_packages = [];
+ protected static array $_packages = [];
/**
* Holds the templates for each customizable package path in the application
*
- * @var array
+ * @var array|null
*/
- protected static $_packageFormat = [];
+ protected static ?array $_packageFormat = [];
/**
* Maps an old style CakePHP class type to the corresponding package
*
* @var array
*/
- public static $legacy = [
+ public static array $legacy = [
'models' => 'Model',
'behaviors' => 'Model/Behavior',
'datasources' => 'Model/Datasource',
@@ -192,14 +192,14 @@ class App
*
* @var bool
*/
- protected static $_cacheChange = false;
+ protected static bool $_cacheChange = false;
/**
* Indicates whether the object cache should be stored again because of an addition to it
*
* @var bool
*/
- protected static $_objectCacheChange = false;
+ protected static bool $_objectCacheChange = false;
/**
* Indicates the the Application is in the bootstrapping process. Used to better cache
@@ -207,7 +207,7 @@ class App
*
* @var bool
*/
- public static $bootstrapping = false;
+ public static bool $bootstrapping = false;
/**
* Used to read information stored path
@@ -219,11 +219,11 @@ class App
* `App::path('Model/Datasource', 'MyPlugin'); will return the path for datasources under the 'MyPlugin' plugin`
*
* @param string $type type of path
- * @param string $plugin name of plugin
+ * @param string|null $plugin name of plugin
* @return array
* @link https://book.cakephp.org/2.0/en/core-utility-libraries/app.html#App::path
*/
- public static function path($type, $plugin = null)
+ public static function path(string $type, ?string $plugin = null): array
{
if (!empty(static::$legacy[$type])) {
$type = static::$legacy[$type];
@@ -274,7 +274,7 @@ public static function path($type, $plugin = null)
* @return array An array of packages and their associated paths.
* @link https://book.cakephp.org/2.0/en/core-utility-libraries/app.html#App::paths
*/
- public static function paths()
+ public static function paths(): array
{
return static::$_packages;
}
@@ -302,7 +302,7 @@ public static function paths()
* @return void
* @link https://book.cakephp.org/2.0/en/core-utility-libraries/app.html#App::build
*/
- public static function build($paths = [], $mode = App::PREPEND)
+ public static function build(array $paths = [], string|bool $mode = App::PREPEND): void
{
// Provides Backwards compatibility for old-style package names
$legacyPaths = [];
@@ -393,7 +393,7 @@ public static function build($paths = [], $mode = App::PREPEND)
* @link https://book.cakephp.org/2.0/en/core-utility-libraries/app.html#App::pluginPath
* @deprecated 3.0.0 Use `CakePlugin::path()` instead.
*/
- public static function pluginPath($plugin)
+ public static function pluginPath(string $plugin): string
{
return CakePlugin::path($plugin);
}
@@ -409,7 +409,7 @@ public static function pluginPath($plugin)
* @return string full path to the theme.
* @link https://book.cakephp.org/2.0/en/core-utility-libraries/app.html#App::themePath
*/
- public static function themePath($theme)
+ public static function themePath(string $theme): string
{
$themeDir = 'Themed' . DS . Inflector::camelize($theme);
foreach (static::$_packages['View'] as $path) {
@@ -432,7 +432,7 @@ public static function themePath($theme)
* @return array full path to package
* @link https://book.cakephp.org/2.0/en/core-utility-libraries/app.html#App::core
*/
- public static function core($type)
+ public static function core(string $type): array
{
return [CAKE . str_replace('/', DS, $type) . DS];
}
@@ -455,13 +455,16 @@ public static function core($type)
* are commonly used by version control systems.
*
* @param string $type Type of object, i.e. 'Model', 'Controller', 'View/Helper', 'file', 'class' or 'plugin'
- * @param array|string $path Optional Scan only the path given. If null, paths for the chosen type will be used.
+ * @param array|string|null $path Optional Scan only the path given. If null, paths for the chosen type will be used.
* @param bool $cache Set to false to rescan objects of the chosen type. Defaults to true.
* @return mixed Either false on incorrect / miss. Or an array of found objects.
* @link https://book.cakephp.org/2.0/en/core-utility-libraries/app.html#App::objects
*/
- public static function objects($type, $path = null, $cache = true)
- {
+ public static function objects(
+ string $type,
+ array|string|null $path = null,
+ bool $cache = true,
+ ): mixed {
if (empty(static::$_objects) && $cache === true) {
static::$_objects = (array)Cache::read('object_map', '_cake_core_');
}
@@ -553,7 +556,7 @@ public static function objects($type, $path = null, $cache = true)
* @return void
* @link https://book.cakephp.org/2.0/en/core-utility-libraries/app.html#App::uses
*/
- public static function uses($className, $location)
+ public static function uses(string $className, string $location): void
{
static::$_classMap[$className] = $location;
}
@@ -565,21 +568,21 @@ public static function uses($className, $location)
* if a class is name `MyCustomClass` the file name should be `MyCustomClass.php`
*
* @param string $className the name of the class to load
- * @return bool
+ * @return void
*/
- public static function load($className)
+ public static function load(string $className): void
{
if (str_contains($className, '\\')) {
- return false;
+ return;
}
if (class_exists($className, false)) {
- return true;
+ return;
}
if (!isset(static::$_classMap[$className])) {
- return false;
+ return;
}
if (str_contains($className, '..')) {
- return false;
+ return;
}
// Check if the namespaced version already exists via legacy class map
@@ -587,7 +590,7 @@ public static function load($className)
$legacyClassMap = LegacyClassLoader::getClassMap();
if (isset($legacyClassMap[$className]) && class_exists($legacyClassMap[$className], false)) {
// Namespaced class exists, let legacy autoloader create the alias
- return false;
+ return;
}
}
@@ -596,7 +599,9 @@ public static function load($className)
$file = static::_mapped($className, $plugin);
if ($file) {
- return include_once $file;
+ include_once $file;
+
+ return;
}
$paths = static::path($package, $plugin);
@@ -617,11 +622,11 @@ public static function load($className)
if (file_exists($file)) {
static::_map($file, $className, $plugin);
- return include_once $file;
+ include_once $file;
+
+ return;
}
}
-
- return false;
}
/**
@@ -631,7 +636,7 @@ public static function load($className)
* @return string|null Package name, or null if not declared
* @link https://book.cakephp.org/2.0/en/core-utility-libraries/app.html#App::location
*/
- public static function location($className)
+ public static function location(string $className): ?string
{
if (!empty(static::$_classMap[$className])) {
return static::$_classMap[$className];
@@ -652,8 +657,11 @@ public static function location($className)
* @param string $suffix Class name suffix
* @return class-string|null Namespaced class name, null if the class is not found.
*/
- public static function className(string $class, string $type = '', string $suffix = ''): ?string
- {
+ public static function className(
+ string $class,
+ string $type = '',
+ string $suffix = '',
+ ): ?string {
if (str_contains($class, '\\')) {
return class_exists($class) ? $class : null;
}
@@ -822,7 +830,7 @@ protected static function _loadParentForSuffix(string $suffix, string $type, ?st
* @param string $namespace Namespace.
* @return bool
*/
- protected static function _classExistsInBase($name, $namespace)
+ protected static function _classExistsInBase(string $name, string $namespace): bool
{
return class_exists($namespace . $name);
}
@@ -831,22 +839,28 @@ protected static function _classExistsInBase($name, $namespace)
* Finds classes based on $name or specific file(s) to search. Calling App::import() will
* not construct any classes contained in the files. It will only find and require() the file.
*
- * @param array|string $type The type of Class if passed as a string, or all params can be passed as
+ * @param array|string|null $type The type of Class if passed as a string, or all params can be passed as
* a single array to $type.
- * @param array|string $name Name of the Class or a unique name for the file
+ * @param array|string|null $name Name of the Class or a unique name for the file
* @param array|bool $parent boolean true if Class Parent should be searched, accepts key => value
* array('parent' => $parent, 'file' => $file, 'search' => $search, 'ext' => '$ext');
* $ext allows setting the extension of the file name
* based on Inflector::underscore($name) . ".$ext";
* @param array $search paths to search for files, array('path 1', 'path 2', 'path 3');
- * @param string $file full name of the file to search for including extension
+ * @param string|null $file full name of the file to search for including extension
* @param bool $return Return the loaded file, the file must have a return
* statement in it to work: return $variable;
- * @return bool true if Class is already in memory or if file is found and loaded, false if not
+ * @return array|bool true if Class is already in memory or if file is found and loaded, false if not
* @link https://book.cakephp.org/2.0/en/core-utility-libraries/app.html#including-files-with-app-import
*/
- public static function import($type = null, $name = null, $parent = true, $search = [], $file = null, $return = false)
- {
+ public static function import(
+ array|string|null $type = null,
+ array|string|null $name = null,
+ array|bool $parent = true,
+ array $search = [],
+ ?string $file = null,
+ bool $return = false,
+ ): array|bool {
$ext = null;
if (is_array($type)) {
@@ -903,14 +917,19 @@ public static function import($type = null, $name = null, $parent = true, $searc
* This is a compatibility wrapper around using App::uses() and automatic class loading
*
* @param string $name unique name of the file for identifying it inside the application
- * @param string $plugin camel cased plugin name if any
+ * @param string|null $plugin camel cased plugin name if any
* @param string $type name of the packed where the class is located
* @param string $originalType type name as supplied initially by the user
* @param bool $parent whether to load the class parent or not
* @return bool true indicating the successful load and existence of the class
*/
- protected static function _loadClass($name, $plugin, $type, $originalType, $parent)
- {
+ protected static function _loadClass(
+ string $name,
+ ?string $plugin,
+ string $type,
+ string $originalType,
+ bool $parent,
+ ): bool {
if ($type === 'Console/Command' && $name === 'Shell') {
$type = 'Console';
} elseif (isset(static::$types[$originalType]['suffix'])) {
@@ -950,28 +969,34 @@ protected static function _loadClass($name, $plugin, $type, $originalType, $pare
* Helper function to include single files
*
* @param string $name unique name of the file for identifying it inside the application
- * @param string $plugin camel cased plugin name if any
+ * @param string|null $plugin camel cased plugin name if any
* @param array $search list of paths to search the file into
* @param string $file filename if known, the $name param will be used otherwise
* @param bool $return whether this function should return the contents of the file after being parsed by php or just a success notice
* @return mixed if $return contents of the file after php parses it, boolean indicating success otherwise
*/
- protected static function _loadFile($name, $plugin, $search, $file, $return)
- {
+ protected static function _loadFile(
+ string $name,
+ ?string $plugin,
+ array $search,
+ string $file,
+ bool $return,
+ ): mixed {
$mapped = static::_mapped($name, $plugin);
if ($mapped) {
$file = $mapped;
} elseif (!empty($search)) {
+ $found = false;
foreach ($search as $path) {
- $found = false;
if (file_exists($path . $file)) {
$file = $path . $file;
$found = true;
break;
}
- if (empty($found)) {
- $file = false;
- }
+ }
+
+ if ($found === false) {
+ $file = false;
}
}
if (!empty($file) && file_exists($file)) {
@@ -991,13 +1016,17 @@ protected static function _loadFile($name, $plugin, $search, $file, $return)
* Helper function to load files from vendors folders
*
* @param string $name unique name of the file for identifying it inside the application
- * @param string $plugin camel cased plugin name if any
- * @param string $file file name if known
- * @param string $ext file extension if known
+ * @param string|null $plugin camel cased plugin name if any
+ * @param string|null $file file name if known
+ * @param string|null $ext file extension if known
* @return bool true if the file was loaded successfully, false otherwise
*/
- protected static function _loadVendor($name, $plugin, $file, $ext)
- {
+ protected static function _loadVendor(
+ string $name,
+ ?string $plugin,
+ ?string $file,
+ ?string $ext,
+ ): bool {
if ($mapped = static::_mapped($name, $plugin)) {
return (bool)include_once $mapped;
}
@@ -1031,7 +1060,7 @@ protected static function _loadVendor($name, $plugin, $file, $ext)
*
* @return void
*/
- public static function init()
+ public static function init(): void
{
static::$_map += (array)Cache::read('file_map', '_cake_core_');
register_shutdown_function(['App', 'shutdown']);
@@ -1042,11 +1071,14 @@ public static function init()
*
* @param string $file full path to file
* @param string $name unique name for this map
- * @param string $plugin camelized if object is from a plugin, the name of the plugin
+ * @param string|null $plugin camelized if object is from a plugin, the name of the plugin
* @return void
*/
- protected static function _map($file, $name, $plugin = null)
- {
+ protected static function _map(
+ string $file,
+ string $name,
+ ?string $plugin = null,
+ ): void {
$key = $name;
if ($plugin) {
$key = 'plugin.' . $name;
@@ -1066,11 +1098,13 @@ protected static function _map($file, $name, $plugin = null)
* Returns a file's complete path.
*
* @param string $name unique name
- * @param string $plugin camelized if object is from a plugin, the name of the plugin
+ * @param string|null $plugin camelized if object is from a plugin, the name of the plugin
* @return mixed file path if found, false otherwise
*/
- protected static function _mapped($name, $plugin = null)
- {
+ protected static function _mapped(
+ string $name,
+ ?string $plugin = null,
+ ): mixed {
$key = $name;
if ($plugin) {
$key = 'plugin.' . $name;
@@ -1084,7 +1118,7 @@ protected static function _mapped($name, $plugin = null)
*
* @return array templates for each customizable package path
*/
- protected static function _packageFormat()
+ protected static function _packageFormat(): array
{
if (empty(static::$_packageFormat)) {
static::$_packageFormat = [
@@ -1161,10 +1195,10 @@ protected static function _packageFormat()
* Increases the PHP "memory_limit" ini setting by the specified amount
* in kilobytes
*
- * @param string $additionalKb Number in kilobytes
+ * @param int $additionalKb Number in kilobytes
* @return void
*/
- public static function increaseMemoryLimit($additionalKb)
+ public static function increaseMemoryLimit(int $additionalKb): void
{
$limit = ini_get('memory_limit');
if (!is_string($limit) || !strlen($limit)) {
@@ -1172,7 +1206,7 @@ public static function increaseMemoryLimit($additionalKb)
}
$limit = trim($limit);
$units = strtoupper(substr($limit, -1));
- $current = substr($limit, 0, strlen($limit) - 1);
+ $current = (int)substr($limit, 0, strlen($limit) - 1);
if ($units === 'M') {
$current = $current * 1024;
$units = 'K';
@@ -1195,7 +1229,7 @@ public static function increaseMemoryLimit($additionalKb)
*
* @return void
*/
- public static function shutdown()
+ public static function shutdown(): void
{
$megabytes = Configure::read('Error.extraFatalErrorMemory');
if ($megabytes === null) {
@@ -1219,7 +1253,7 @@ public static function shutdown()
*
* @return void
*/
- protected static function _checkFatalError()
+ protected static function _checkFatalError(): void
{
$lastError = error_get_last();
if (!is_array($lastError)) {
diff --git a/src/Core/CakeObject.php b/src/Core/CakeObject.php
index f58d8b4003..a53e173ce3 100644
--- a/src/Core/CakeObject.php
+++ b/src/Core/CakeObject.php
@@ -48,11 +48,9 @@ public function __construct()
*
* @return string The name of this class
*/
- public function toString()
+ public function toString(): string
{
- $class = static::class;
-
- return $class;
+ return static::class;
}
/**
@@ -68,18 +66,21 @@ public function toString()
* POST and GET data can be simulated in requestAction. Use `$extra['url']` for
* GET data. The `$extra['data']` parameter allows POST data simulation.
*
- * @param array|string $url String or array-based URL. Unlike other URL arrays in CakePHP, this
+ * @param array|string|null $url String or array-based URL. Unlike other URL arrays in CakePHP, this
* URL will not automatically handle passed and named arguments in the $url parameter.
* @param array $extra if array includes the key "return" it sets the AutoRender to true. Can
* also be used to submit GET/POST data, and named/passed arguments.
* @return mixed Boolean true or false on success/failure, or contents
* of rendered action if 'return' is set in $extra.
*/
- public function requestAction($url, $extra = [])
- {
+ public function requestAction(
+ array|string|null $url,
+ array $extra = [],
+ ): mixed {
if (empty($url)) {
return false;
}
+
if (($index = array_search('return', $extra)) !== false) {
$extra['return'] = 0;
$extra['autoRender'] = 1;
@@ -101,7 +102,7 @@ public function requestAction($url, $extra = [])
}
if (is_string($url)) {
$request = new CakeRequest($url);
- } elseif (is_array($url)) {
+ } else {
$params = $url + ['pass' => [], 'named' => [], 'base' => false];
$params = $extra + $params;
$request = new CakeRequest(Router::reverse($params));
@@ -135,9 +136,9 @@ public function dispatchMethod(string $method, array $params = []): mixed
* testing easier.
*
* @param string|int $status see http://php.net/exit for values
- * @return never|int
+ * @return void
*/
- protected function _stop($status = 0)
+ protected function _stop(string|int $status = 0): void
{
exit($status);
}
@@ -147,12 +148,12 @@ protected function _stop($status = 0)
* for more information on writing to logs.
*
* @param mixed $msg Log message
- * @param int $type Error type constant. Defined in app/Config/core.php.
+ * @param string|int $type Error type constant. Defined in app/Config/core.php.
* @param array|string|null $scope The scope(s) a log message is being created in.
* See CakeLog::config() for more information on logging scopes.
* @return bool Success of log write
*/
- public function log($msg, $type = LOG_ERR, $scope = null)
+ public function log(mixed $msg, string|int $type = LOG_ERR, array|string|null $scope = null): bool
{
if (!is_string($msg)) {
$msg = print_r($msg, true);
diff --git a/src/Core/Configure.php b/src/Core/Configure.php
index 871f5d9823..32cbdd3679 100644
--- a/src/Core/Configure.php
+++ b/src/Core/Configure.php
@@ -42,7 +42,7 @@ class Configure
*
* @var array
*/
- protected static $_values = [
+ protected static array $_values = [
'debug' => 0,
];
@@ -52,7 +52,7 @@ class Configure
* @var array
* @see Configure::load()
*/
- protected static $_readers = [];
+ protected static array $_readers = [];
/**
* Initializes configure and runs the bootstrap process.
@@ -68,7 +68,7 @@ class Configure
* @param bool $boot Whether to do bootstrapping.
* @return void
*/
- public static function bootstrap($boot = true)
+ public static function bootstrap(bool $boot = true): void
{
if ($boot) {
static::_appDefaults();
@@ -131,7 +131,7 @@ class_exists(CakeText::class);
*
* @return void
*/
- protected static function _appDefaults()
+ protected static function _appDefaults(): void
{
static::write('App', (array)static::read('App') + [
'base' => false,
@@ -160,14 +160,16 @@ protected static function _appDefaults()
* ));
* ```
*
- * @param array|string $config The key to write, can be a dot notation value.
+ * @param array|string|bool $config The key to write, can be a dot notation value.
* Alternatively can be an array containing key(s) and value(s).
* @param mixed $value Value to set for var
* @return bool True if write was successful
* @link https://book.cakephp.org/2.0/en/development/configuration.html#Configure::write
*/
- public static function write($config, $value = null)
- {
+ public static function write(
+ array|string|bool $config,
+ mixed $value = null,
+ ): bool {
if (!is_array($config)) {
$config = [$config => $value];
}
@@ -178,9 +180,9 @@ public static function write($config, $value = null)
if (isset($config['debug']) && function_exists('ini_set')) {
if (static::$_values['debug']) {
- ini_set('display_errors', 1);
+ ini_set('display_errors', 1); // @phpstan-ignore-line
} else {
- ini_set('display_errors', 0);
+ ini_set('display_errors', 0); // @phpstan-ignore-line
}
}
@@ -201,7 +203,7 @@ public static function write($config, $value = null)
* @return mixed value stored in configure, or null.
* @link https://book.cakephp.org/2.0/en/development/configuration.html#Configure::read
*/
- public static function read($var = null)
+ public static function read(?string $var = null): mixed
{
if ($var === null) {
return static::$_values;
@@ -216,10 +218,10 @@ public static function read($var = null)
* This is primarily used during bootstrapping to move configuration data
* out of configure into the various other classes in CakePHP.
*
- * @param string $var The key to read and remove.
- * @return array|null
+ * @param string|null $var The key to read and remove.
+ * @return array|string|null
*/
- public static function consume($var)
+ public static function consume(?string $var): array|string|null
{
$simple = !str_contains($var, '.');
if ($simple && !isset(static::$_values[$var])) {
@@ -240,10 +242,10 @@ public static function consume($var)
/**
* Returns true if given variable is set in Configure.
*
- * @param string $var Variable name to check for
+ * @param string|null $var Variable name to check for
* @return bool True if variable is there
*/
- public static function check($var)
+ public static function check(?string $var): bool
{
if (empty($var)) {
return false;
@@ -265,7 +267,7 @@ public static function check($var)
* @return void
* @link https://book.cakephp.org/2.0/en/development/configuration.html#Configure::delete
*/
- public static function delete($var)
+ public static function delete(string $var): void
{
static::$_values = Hash::remove(static::$_values, $var);
}
@@ -284,7 +286,7 @@ public static function delete($var)
* @param ConfigReaderInterface $reader The reader to append.
* @return void
*/
- public static function config($name, ConfigReaderInterface $reader)
+ public static function config(string $name, ConfigReaderInterface $reader): void
{
static::$_readers[$name] = $reader;
}
@@ -293,9 +295,9 @@ public static function config($name, ConfigReaderInterface $reader)
* Gets the names of the configured reader objects.
*
* @param string|null $name Name to check. If null returns all configured reader names.
- * @return array Array of the configured reader objects.
+ * @return array|bool Array of the configured reader objects.
*/
- public static function configured($name = null)
+ public static function configured(?string $name = null): array|bool
{
if ($name) {
return isset(static::$_readers[$name]);
@@ -311,7 +313,7 @@ public static function configured($name = null)
* @param string $name Name of the reader to drop.
* @return bool Success
*/
- public static function drop($name)
+ public static function drop(string $name): bool
{
if (!isset(static::$_readers[$name])) {
return false;
@@ -346,8 +348,11 @@ public static function drop($name)
* @throws ConfigureException Will throw any exceptions the reader raises.
* @link https://book.cakephp.org/2.0/en/development/configuration.html#Configure::load
*/
- public static function load($key, $config = 'default', $merge = true)
- {
+ public static function load(
+ string $key,
+ string $config = 'default',
+ bool $merge = true,
+ ): bool {
$reader = static::_getReader($config);
if (!$reader) {
return false;
@@ -391,8 +396,11 @@ public static function load($key, $config = 'default', $merge = true)
* @return bool success
* @throws ConfigureException if the adapter does not implement a `dump` method.
*/
- public static function dump($key, $config = 'default', $keys = [])
- {
+ public static function dump(
+ string $key,
+ string $config = 'default',
+ array $keys = [],
+ ): bool {
$reader = static::_getReader($config);
if (!$reader) {
throw new ConfigureException(__d('cake_dev', 'There is no "%s" adapter.', $config));
@@ -401,7 +409,7 @@ public static function dump($key, $config = 'default', $keys = [])
throw new ConfigureException(__d('cake_dev', 'The "%s" adapter, does not have a %s method.', $config, 'dump()'));
}
$values = static::$_values;
- if (!empty($keys) && is_array($keys)) {
+ if (!empty($keys)) {
$values = array_intersect_key($values, array_flip($keys));
}
@@ -415,7 +423,7 @@ public static function dump($key, $config = 'default', $keys = [])
* @param string $config The name of the configured adapter
* @return mixed Reader instance or false
*/
- protected static function _getReader($config)
+ protected static function _getReader(string $config): mixed
{
if (!isset(static::$_readers[$config])) {
if ($config !== 'default') {
@@ -434,7 +442,7 @@ protected static function _getReader($config)
*
* @return string Current version of CakePHP
*/
- public static function version()
+ public static function version(): string
{
if (!isset(static::$_values['Cake']['version'])) {
$config = require CORE_ROOT . DS . 'config' . DS . 'config.php';
@@ -451,11 +459,14 @@ public static function version()
*
* @param string $name The storage name for the saved configuration.
* @param string $cacheConfig The cache configuration to save into. Defaults to 'default'
- * @param array $data Either an array of data to store, or leave empty to store all values.
+ * @param array|null $data Either an array of data to store, or leave empty to store all values.
* @return bool Success
*/
- public static function store($name, $cacheConfig = 'default', $data = null)
- {
+ public static function store(
+ string $name,
+ string $cacheConfig = 'default',
+ ?array $data = null,
+ ): bool {
if ($data === null) {
$data = static::$_values;
}
@@ -471,8 +482,10 @@ public static function store($name, $cacheConfig = 'default', $data = null)
* @param string $cacheConfig Name of the Cache configuration to read from.
* @return bool Success.
*/
- public static function restore($name, $cacheConfig = 'default')
- {
+ public static function restore(
+ string $name,
+ string $cacheConfig = 'default',
+ ): bool {
$values = Cache::read($name, $cacheConfig);
if ($values) {
return static::write($values);
@@ -486,7 +499,7 @@ public static function restore($name, $cacheConfig = 'default')
*
* @return bool Success.
*/
- public static function clear()
+ public static function clear(): bool
{
static::$_values = [];
@@ -500,8 +513,10 @@ public static function clear()
* @param array $exception The exception handling configuration.
* @return void
*/
- protected static function _setErrorHandlers($error, $exception)
- {
+ protected static function _setErrorHandlers(
+ array $error,
+ array $exception,
+ ): void {
$level = -1;
if (isset($error['level'])) {
error_reporting($error['level']);
diff --git a/src/Error/AuthSecurityException.php b/src/Error/AuthSecurityException.php
index 42ab349c27..c529d58b89 100644
--- a/src/Error/AuthSecurityException.php
+++ b/src/Error/AuthSecurityException.php
@@ -28,5 +28,5 @@ class AuthSecurityException extends SecurityException
*
* @var string
*/
- protected $_type = 'auth';
+ protected string $_type = 'auth';
}
diff --git a/src/Error/BadRequestException.php b/src/Error/BadRequestException.php
index 5eb291de49..3a37a2151f 100644
--- a/src/Error/BadRequestException.php
+++ b/src/Error/BadRequestException.php
@@ -26,14 +26,15 @@ class BadRequestException extends HttpException
/**
* Constructor
*
- * @param string $message If no message is given 'Bad Request' will be the message
+ * @param string|null $message If no message is given 'Bad Request' will be the message
* @param int $code Status code, defaults to 400
*/
- public function __construct($message = null, $code = 400)
+ public function __construct(?string $message = null, int $code = 400)
{
if (empty($message)) {
$message = 'Bad Request';
}
+
parent::__construct($message, $code);
}
}
diff --git a/src/Error/CakeException.php b/src/Error/CakeException.php
index 9795697e61..be2a0934df 100644
--- a/src/Error/CakeException.php
+++ b/src/Error/CakeException.php
@@ -16,6 +16,8 @@
namespace Cake\Error;
+use Throwable;
+
/**
* CakeException is used a base class for CakePHP's internal exceptions.
* In general framework errors are interpreted as 500 code errors.
@@ -37,7 +39,7 @@ class CakeException extends CakeBaseException
*
* @var string
*/
- protected $_messageTemplate = '';
+ protected string $_messageTemplate = '';
/**
* Constructor.
@@ -48,14 +50,16 @@ class CakeException extends CakeBaseException
* @param array|string $message Either the string of the error message, or an array of attributes
* that are made available in the view, and sprintf()'d into CakeException::$_messageTemplate
* @param int $code The code of the error, is also the HTTP status code for the error.
+ * @param Throwable|null $previous
*/
- public function __construct($message, $code = 500)
+ public function __construct(array|string $message, int $code = 500, ?Throwable $previous = null)
{
if (is_array($message)) {
$this->_attributes = $message;
$message = __d('cake_dev', $this->_messageTemplate, $message);
}
- parent::__construct($message, $code);
+
+ parent::__construct($message, $code, $previous);
}
/**
diff --git a/src/Error/ErrorHandler.php b/src/Error/ErrorHandler.php
index 673fa220ae..8d686cd895 100644
--- a/src/Error/ErrorHandler.php
+++ b/src/Error/ErrorHandler.php
@@ -107,7 +107,7 @@ class ErrorHandler
*
* @var bool
*/
- protected static $_bailExceptionRendering = false;
+ protected static bool $_bailExceptionRendering = false;
/**
* Set as the default exception handler by the CakePHP bootstrap process.
@@ -153,7 +153,7 @@ public static function handleException(Exception|ParseError $exception): void
* @param Exception $exception Exception instance
* @return string Formatted message
*/
- protected static function _getMessage($exception)
+ protected static function _getMessage(Exception $exception): string
{
$message = sprintf(
'[%s] %s',
@@ -292,7 +292,7 @@ public static function handleFatalError(int $code, string $description, string $
* Map an error code into an Error word, and log location.
*
* @param int $code Error code to map
- * @return list{string|null, int|null} Array of error word, and log location.
+ * @return array{string|null, int|null} Array of error word, and log location.
*/
public static function mapErrorCode(int $code): array
{
@@ -334,12 +334,17 @@ public static function mapErrorCode(int $code): array
* @param string $error The error type (e.g. "Warning")
* @param int $code Code of error
* @param string $description Error description
- * @param string|null $file File on which error occurred
- * @param int|null $line Line that triggered the error
+ * @param string $file File on which error occurred
+ * @param int $line Line that triggered the error
* @return string
*/
- protected static function _getErrorMessage(string $error, int $code, string $description, string $file, int $line): string
- {
+ protected static function _getErrorMessage(
+ string $error,
+ int $code,
+ string $description,
+ string $file,
+ int $line,
+ ): string {
$errorConfig = Configure::read('Error');
$message = $error . ' (' . $code . '): ' . $description . ' in [' . $file . ', line ' . $line . ']';
if (!empty($errorConfig['trace'])) {
diff --git a/src/Error/ExceptionRenderer.php b/src/Error/ExceptionRenderer.php
index 8d836935ed..db79fa00f3 100644
--- a/src/Error/ExceptionRenderer.php
+++ b/src/Error/ExceptionRenderer.php
@@ -34,6 +34,7 @@
use Cake\Utility\Inflector;
use Cake\View\View;
use Exception;
+use ParseError;
use PDOException;
/**
@@ -68,30 +69,30 @@ class ExceptionRenderer
/**
* Controller instance.
*
- * @var Controller
+ * @var Controller|null
*/
- public $controller = null;
+ public ?Controller $controller = null;
/**
* template to render for CakeException
*
* @var string
*/
- public $template = '';
+ public string $template = '';
/**
* The method corresponding to the Exception this object is for.
*
* @var string
*/
- public $method = '';
+ public string $method = '';
/**
* The exception being handled.
*
- * @var Exception
+ * @var Exception|null
*/
- public $error = null;
+ public ?Exception $error = null;
/**
* Creates the controller to perform rendering on the error response.
@@ -100,7 +101,7 @@ class ExceptionRenderer
*
* @param Exception|ParseError $exception Exception
*/
- public function __construct($exception)
+ public function __construct(Exception|ParseError $exception)
{
$this->controller = $this->_getController($exception);
@@ -156,7 +157,7 @@ public function __construct($exception)
* @param Exception $exception The exception to get a controller for.
* @return Controller
*/
- protected function _getController($exception)
+ protected function _getController(Exception $exception): Controller
{
App::uses('AppController', 'Controller');
if (!$request = Router::getRequest(true)) {
@@ -203,7 +204,7 @@ protected function _getController($exception)
*
* @return void
*/
- public function render()
+ public function render(): void
{
if ($this->method) {
call_user_func_array([$this, $this->method], [$this->error]);
@@ -216,7 +217,7 @@ public function render()
* @param CakeException $error The exception to render.
* @return void
*/
- protected function _cakeError(CakeException $error)
+ protected function _cakeError(CakeException $error): void
{
$url = $this->controller->request->here();
$code = $error->getCode() >= 400 && $error->getCode() < 506 ? $error->getCode() : 500;
@@ -239,7 +240,7 @@ protected function _cakeError(CakeException $error)
* @param Exception $error The exception to render.
* @return void
*/
- public function error400($error)
+ public function error400(Exception $error): void
{
$message = $error->getMessage();
if (!Configure::read('debug') && $error instanceof CakeException) {
@@ -263,7 +264,7 @@ public function error400($error)
* @param Exception $error The exception to render.
* @return void
*/
- public function error500($error)
+ public function error500(Exception $error): void
{
$message = $error->getMessage();
if (!Configure::read('debug')) {
@@ -288,7 +289,7 @@ public function error500($error)
* @param PDOException $error The exception to render.
* @return void
*/
- public function pdoError(PDOException $error)
+ public function pdoError(PDOException $error): void
{
$url = $this->controller->request->here();
$code = 500;
@@ -310,7 +311,7 @@ public function pdoError(PDOException $error)
* @param string $template The template to render.
* @return void
*/
- protected function _outputMessage($template)
+ protected function _outputMessage(string $template): void
{
try {
$this->controller->render($template);
@@ -341,7 +342,7 @@ protected function _outputMessage($template)
* @param string $template The template to render
* @return void
*/
- protected function _outputMessageSafe($template)
+ protected function _outputMessageSafe(string $template): void
{
$this->controller->layoutPath = null;
$this->controller->subDir = null;
@@ -362,7 +363,7 @@ protected function _outputMessageSafe($template)
*
* @return void
*/
- protected function _shutdown()
+ protected function _shutdown(): void
{
$afterFilterEvent = new CakeEvent('Controller.shutdown', $this->controller);
$this->controller->getEventManager()->dispatch($afterFilterEvent);
diff --git a/src/Error/FatalErrorException.php b/src/Error/FatalErrorException.php
index 15cc99ed43..a5d622206c 100644
--- a/src/Error/FatalErrorException.php
+++ b/src/Error/FatalErrorException.php
@@ -28,12 +28,17 @@ class FatalErrorException extends CakeException
*
* @param string $message The error message.
* @param int $code The error code.
- * @param string $file The file the error occurred in.
- * @param int $line The line the error occurred on.
+ * @param string|null $file The file the error occurred in.
+ * @param int|null $line The line the error occurred on.
*/
- public function __construct($message, $code = 500, $file = null, $line = null)
- {
+ public function __construct(
+ array|string $message,
+ int $code = 500,
+ ?string $file = null,
+ ?int $line = null,
+ ) {
parent::__construct($message, $code);
+
if ($file) {
$this->file = $file;
}
diff --git a/src/Error/ForbiddenException.php b/src/Error/ForbiddenException.php
index 7903973f6b..0328ee1b11 100644
--- a/src/Error/ForbiddenException.php
+++ b/src/Error/ForbiddenException.php
@@ -26,14 +26,15 @@ class ForbiddenException extends HttpException
/**
* Constructor
*
- * @param string $message If no message is given 'Forbidden' will be the message
+ * @param string|null $message If no message is given 'Forbidden' will be the message
* @param int $code Status code, defaults to 403
*/
- public function __construct($message = null, $code = 403)
+ public function __construct(?string $message = null, int $code = 403)
{
if (empty($message)) {
$message = 'Forbidden';
}
+
parent::__construct($message, $code);
}
}
diff --git a/src/Error/InternalErrorException.php b/src/Error/InternalErrorException.php
index ab803fd6bd..a2553b1bd2 100644
--- a/src/Error/InternalErrorException.php
+++ b/src/Error/InternalErrorException.php
@@ -26,14 +26,15 @@ class InternalErrorException extends HttpException
/**
* Constructor
*
- * @param string $message If no message is given 'Internal Server Error' will be the message
+ * @param string|null $message If no message is given 'Internal Server Error' will be the message
* @param int $code Status code, defaults to 500
*/
- public function __construct($message = null, $code = 500)
+ public function __construct(?string $message = null, int $code = 500)
{
if (empty($message)) {
$message = 'Internal Server Error';
}
+
parent::__construct($message, $code);
}
}
diff --git a/src/Error/MethodNotAllowedException.php b/src/Error/MethodNotAllowedException.php
index 918233a3e6..dcc1bdc269 100644
--- a/src/Error/MethodNotAllowedException.php
+++ b/src/Error/MethodNotAllowedException.php
@@ -26,14 +26,15 @@ class MethodNotAllowedException extends HttpException
/**
* Constructor
*
- * @param string $message If no message is given 'Method Not Allowed' will be the message
+ * @param string|null $message If no message is given 'Method Not Allowed' will be the message
* @param int $code Status code, defaults to 405
*/
- public function __construct($message = null, $code = 405)
+ public function __construct(?string $message = null, int $code = 405)
{
if (empty($message)) {
$message = 'Method Not Allowed';
}
+
parent::__construct($message, $code);
}
}
diff --git a/src/Error/MissingActionException.php b/src/Error/MissingActionException.php
index ff3ca0cc45..03089fd1e6 100644
--- a/src/Error/MissingActionException.php
+++ b/src/Error/MissingActionException.php
@@ -24,9 +24,7 @@
*/
class MissingActionException extends CakeException
{
- protected $_messageTemplate = 'Action %s::%s() could not be found.';
-
-//@codingStandardsIgnoreStart
+ protected string $_messageTemplate = 'Action %s::%s() could not be found.';
/**
* Constructor
@@ -34,10 +32,8 @@ class MissingActionException extends CakeException
* @param array|string $message Error message
* @param int $code Error code
*/
- public function __construct($message, $code = 404)
+ public function __construct(array|string $message, int $code = 404)
{
parent::__construct($message, $code);
}
-
-//@codingStandardsIgnoreEnd
}
diff --git a/src/Error/MissingBehaviorException.php b/src/Error/MissingBehaviorException.php
index 525ac0a470..ddf324ffbc 100644
--- a/src/Error/MissingBehaviorException.php
+++ b/src/Error/MissingBehaviorException.php
@@ -23,5 +23,5 @@
*/
class MissingBehaviorException extends CakeException
{
- protected $_messageTemplate = 'Behavior class %s could not be found.';
+ protected string $_messageTemplate = 'Behavior class %s could not be found.';
}
diff --git a/src/Error/MissingComponentException.php b/src/Error/MissingComponentException.php
index 1a016e7534..94c12581dd 100644
--- a/src/Error/MissingComponentException.php
+++ b/src/Error/MissingComponentException.php
@@ -23,5 +23,5 @@
*/
class MissingComponentException extends CakeException
{
- protected $_messageTemplate = 'Component class %s could not be found.';
+ protected string $_messageTemplate = 'Component class %s could not be found.';
}
diff --git a/src/Error/MissingConnectionException.php b/src/Error/MissingConnectionException.php
index 74e8811aa0..c6bc468aaf 100644
--- a/src/Error/MissingConnectionException.php
+++ b/src/Error/MissingConnectionException.php
@@ -23,7 +23,7 @@
*/
class MissingConnectionException extends CakeException
{
- protected $_messageTemplate = 'Database connection "%s" is missing, or could not be created.';
+ protected string $_messageTemplate = 'Database connection "%s" is missing, or could not be created.';
/**
* Constructor
@@ -31,7 +31,7 @@ class MissingConnectionException extends CakeException
* @param array|string $message The error message.
* @param int $code The error code.
*/
- public function __construct($message, $code = 500)
+ public function __construct(array|string $message, int $code = 500)
{
if (is_array($message)) {
$message += ['enabled' => true];
@@ -39,6 +39,7 @@ public function __construct($message, $code = 500)
if (isset($message['message'])) {
$this->_messageTemplate .= ' %s';
}
+
parent::__construct($message, $code);
}
}
diff --git a/src/Error/MissingControllerException.php b/src/Error/MissingControllerException.php
index f04d8c9584..dffbd6ca57 100644
--- a/src/Error/MissingControllerException.php
+++ b/src/Error/MissingControllerException.php
@@ -24,9 +24,7 @@
*/
class MissingControllerException extends CakeException
{
- protected $_messageTemplate = 'Controller class %s could not be found.';
-
-//@codingStandardsIgnoreStart
+ protected string $_messageTemplate = 'Controller class %s could not be found.';
/**
* Constructor
@@ -34,10 +32,8 @@ class MissingControllerException extends CakeException
* @param array|string $message Error message
* @param int $code Error code
*/
- public function __construct($message, $code = 404)
+ public function __construct(array|string $message, int $code = 404)
{
parent::__construct($message, $code);
}
-
-//@codingStandardsIgnoreEnd
}
diff --git a/src/Error/MissingDatabaseException.php b/src/Error/MissingDatabaseException.php
index 4520e590ba..3c637f5163 100644
--- a/src/Error/MissingDatabaseException.php
+++ b/src/Error/MissingDatabaseException.php
@@ -23,5 +23,5 @@
*/
class MissingDatabaseException extends CakeException
{
- protected $_messageTemplate = 'Database connection "%s" could not be found.';
+ protected string $_messageTemplate = 'Database connection "%s" could not be found.';
}
diff --git a/src/Error/MissingDatasourceConfigException.php b/src/Error/MissingDatasourceConfigException.php
index 011d053472..0094e257f2 100644
--- a/src/Error/MissingDatasourceConfigException.php
+++ b/src/Error/MissingDatasourceConfigException.php
@@ -23,5 +23,5 @@
*/
class MissingDatasourceConfigException extends CakeException
{
- protected $_messageTemplate = 'The datasource configuration "%s" was not found in database.php';
+ protected string $_messageTemplate = 'The datasource configuration "%s" was not found in database.php';
}
diff --git a/src/Error/MissingDatasourceException.php b/src/Error/MissingDatasourceException.php
index e2fcefc182..3b28b4f3c9 100644
--- a/src/Error/MissingDatasourceException.php
+++ b/src/Error/MissingDatasourceException.php
@@ -23,5 +23,5 @@
*/
class MissingDatasourceException extends CakeException
{
- protected $_messageTemplate = 'Datasource class %s could not be found. %s';
+ protected string $_messageTemplate = 'Datasource class %s could not be found. %s';
}
diff --git a/src/Error/MissingDispatcherFilterException.php b/src/Error/MissingDispatcherFilterException.php
index 1e74d4b44a..451b0eeed2 100644
--- a/src/Error/MissingDispatcherFilterException.php
+++ b/src/Error/MissingDispatcherFilterException.php
@@ -23,5 +23,5 @@
*/
class MissingDispatcherFilterException extends CakeException
{
- protected $_messageTemplate = 'Dispatcher filter %s could not be found.';
+ protected string $_messageTemplate = 'Dispatcher filter %s could not be found.';
}
diff --git a/src/Error/MissingHelperException.php b/src/Error/MissingHelperException.php
index ed2f40cc95..358360e3a0 100644
--- a/src/Error/MissingHelperException.php
+++ b/src/Error/MissingHelperException.php
@@ -23,5 +23,5 @@
*/
class MissingHelperException extends CakeException
{
- protected $_messageTemplate = 'Helper class %s could not be found.';
+ protected string $_messageTemplate = 'Helper class %s could not be found.';
}
diff --git a/src/Error/MissingLayoutException.php b/src/Error/MissingLayoutException.php
index cdce044fc1..9a2a88f6b7 100644
--- a/src/Error/MissingLayoutException.php
+++ b/src/Error/MissingLayoutException.php
@@ -23,5 +23,5 @@
*/
class MissingLayoutException extends CakeException
{
- protected $_messageTemplate = 'Layout file "%s" is missing.';
+ protected string $_messageTemplate = 'Layout file "%s" is missing.';
}
diff --git a/src/Error/MissingModelException.php b/src/Error/MissingModelException.php
index 4cd16286f2..42142c690e 100644
--- a/src/Error/MissingModelException.php
+++ b/src/Error/MissingModelException.php
@@ -23,5 +23,5 @@
*/
class MissingModelException extends CakeException
{
- protected $_messageTemplate = 'Model %s could not be found.';
+ protected string $_messageTemplate = 'Model %s could not be found.';
}
diff --git a/src/Error/MissingPluginException.php b/src/Error/MissingPluginException.php
index a4d6bd901c..74a5c121a2 100644
--- a/src/Error/MissingPluginException.php
+++ b/src/Error/MissingPluginException.php
@@ -23,5 +23,5 @@
*/
class MissingPluginException extends CakeException
{
- protected $_messageTemplate = 'Plugin %s could not be found.';
+ protected string $_messageTemplate = 'Plugin %s could not be found.';
}
diff --git a/src/Error/MissingShellException.php b/src/Error/MissingShellException.php
index 24c563a19f..fc535b8df1 100644
--- a/src/Error/MissingShellException.php
+++ b/src/Error/MissingShellException.php
@@ -23,5 +23,5 @@
*/
class MissingShellException extends CakeException
{
- protected $_messageTemplate = 'Shell class %s could not be found.';
+ protected string $_messageTemplate = 'Shell class %s could not be found.';
}
diff --git a/src/Error/MissingShellMethodException.php b/src/Error/MissingShellMethodException.php
index f74019302e..ce7be6de58 100644
--- a/src/Error/MissingShellMethodException.php
+++ b/src/Error/MissingShellMethodException.php
@@ -23,5 +23,5 @@
*/
class MissingShellMethodException extends CakeException
{
- protected $_messageTemplate = "Unknown command %1\$s %2\$s.\nFor usage try `cake %1\$s --help`";
+ protected string $_messageTemplate = "Unknown command %1\$s %2\$s.\nFor usage try `cake %1\$s --help`";
}
diff --git a/src/Error/MissingTableException.php b/src/Error/MissingTableException.php
index c2b282ea3e..3fb413706a 100644
--- a/src/Error/MissingTableException.php
+++ b/src/Error/MissingTableException.php
@@ -23,5 +23,5 @@
*/
class MissingTableException extends CakeException
{
- protected $_messageTemplate = 'Table %s for model %s was not found in datasource %s.';
+ protected string $_messageTemplate = 'Table %s for model %s was not found in datasource %s.';
}
diff --git a/src/Error/MissingTaskException.php b/src/Error/MissingTaskException.php
index 221cf6e120..6ea68aeddc 100644
--- a/src/Error/MissingTaskException.php
+++ b/src/Error/MissingTaskException.php
@@ -23,5 +23,5 @@
*/
class MissingTaskException extends CakeException
{
- protected $_messageTemplate = 'Task class %s could not be found.';
+ protected string $_messageTemplate = 'Task class %s could not be found.';
}
diff --git a/src/Error/MissingTestLoaderException.php b/src/Error/MissingTestLoaderException.php
index 9e80fdb0c5..2d56a237fc 100644
--- a/src/Error/MissingTestLoaderException.php
+++ b/src/Error/MissingTestLoaderException.php
@@ -23,5 +23,5 @@
*/
class MissingTestLoaderException extends CakeException
{
- protected $_messageTemplate = 'Test loader %s could not be found.';
+ protected string $_messageTemplate = 'Test loader %s could not be found.';
}
diff --git a/src/Error/MissingViewException.php b/src/Error/MissingViewException.php
index 9815acc866..517e5e7ff5 100644
--- a/src/Error/MissingViewException.php
+++ b/src/Error/MissingViewException.php
@@ -23,5 +23,5 @@
*/
class MissingViewException extends CakeException
{
- protected $_messageTemplate = 'View file "%s" is missing.';
+ protected string $_messageTemplate = 'View file "%s" is missing.';
}
diff --git a/src/Error/NotFoundException.php b/src/Error/NotFoundException.php
index 216856fec9..b7051ee945 100644
--- a/src/Error/NotFoundException.php
+++ b/src/Error/NotFoundException.php
@@ -26,14 +26,15 @@ class NotFoundException extends HttpException
/**
* Constructor
*
- * @param string $message If no message is given 'Not Found' will be the message
+ * @param string|null $message If no message is given 'Not Found' will be the message
* @param int $code Status code, defaults to 404
*/
- public function __construct($message = null, $code = 404)
+ public function __construct(?string $message = null, int $code = 404)
{
if (empty($message)) {
$message = 'Not Found';
}
+
parent::__construct($message, $code);
}
}
diff --git a/src/Error/NotImplementedException.php b/src/Error/NotImplementedException.php
index b3d624530d..97b83b78d0 100644
--- a/src/Error/NotImplementedException.php
+++ b/src/Error/NotImplementedException.php
@@ -23,9 +23,7 @@
*/
class NotImplementedException extends CakeException
{
- protected $_messageTemplate = '%s is not implemented.';
-
-//@codingStandardsIgnoreStart
+ protected string $_messageTemplate = '%s is not implemented.';
/**
* Constructor
@@ -33,10 +31,8 @@ class NotImplementedException extends CakeException
* @param array|string $message Error message
* @param int $code Error code
*/
- public function __construct($message, $code = 501)
+ public function __construct(array|string $message, int $code = 501)
{
parent::__construct($message, $code);
}
-
-//@codingStandardsIgnoreEnd
}
diff --git a/src/Error/PrivateActionException.php b/src/Error/PrivateActionException.php
index 05656197db..1ce5f76a4c 100644
--- a/src/Error/PrivateActionException.php
+++ b/src/Error/PrivateActionException.php
@@ -26,21 +26,17 @@
*/
class PrivateActionException extends CakeException
{
- protected $_messageTemplate = 'Private Action %s::%s() is not directly accessible.';
-
-//@codingStandardsIgnoreStart
+ protected string $_messageTemplate = 'Private Action %s::%s() is not directly accessible.';
/**
* Constructor
*
* @param array|string $message Error message
* @param int $code Error code
- * @param \Exception|null $previous Previous exception
+ * @param Exception|null $previous Previous exception
*/
- public function __construct($message, $code = 404, ?Exception $previous = null)
+ public function __construct(array|string $message, int $code = 404, ?Exception $previous = null)
{
parent::__construct($message, $code, $previous);
}
-
-//@codingStandardsIgnoreEnd
}
diff --git a/src/Error/SecurityException.php b/src/Error/SecurityException.php
index 7a672aa6de..36e79f4761 100644
--- a/src/Error/SecurityException.php
+++ b/src/Error/SecurityException.php
@@ -28,21 +28,21 @@ class SecurityException extends BadRequestException
*
* @var string
*/
- protected $_type = 'secure';
+ protected string $_type = 'secure';
/**
* Reason for request blackhole
*
- * @var string
+ * @var string|null
*/
- protected $_reason = null;
+ protected ?string $_reason = null;
/**
* Getter for type
*
* @return string
*/
- public function getType()
+ public function getType(): string
{
return $this->_type;
}
@@ -53,7 +53,7 @@ public function getType()
* @param string $message Exception message
* @return void
*/
- public function setMessage($message)
+ public function setMessage(string $message): void
{
$this->message = $message;
}
@@ -64,7 +64,7 @@ public function setMessage($message)
* @param string|null $reason Reason details
* @return void
*/
- public function setReason($reason = null)
+ public function setReason(?string $reason = null): void
{
$this->_reason = $reason;
}
@@ -72,9 +72,9 @@ public function setReason($reason = null)
/**
* Get Reason
*
- * @return string
+ * @return string|null
*/
- public function getReason()
+ public function getReason(): ?string
{
return $this->_reason;
}
diff --git a/src/Error/UnauthorizedException.php b/src/Error/UnauthorizedException.php
index 67a5b05797..9f9c03fc74 100644
--- a/src/Error/UnauthorizedException.php
+++ b/src/Error/UnauthorizedException.php
@@ -26,14 +26,15 @@ class UnauthorizedException extends HttpException
/**
* Constructor
*
- * @param string $message If no message is given 'Unauthorized' will be the message
+ * @param string|null $message If no message is given 'Unauthorized' will be the message
* @param int $code Status code, defaults to 401
*/
- public function __construct($message = null, $code = 401)
+ public function __construct(?string $message = null, int $code = 401)
{
if (empty($message)) {
$message = 'Unauthorized';
}
+
parent::__construct($message, $code);
}
}
diff --git a/src/Event/CakeEvent.php b/src/Event/CakeEvent.php
index b6f9264b83..eb5dda60f3 100644
--- a/src/Event/CakeEvent.php
+++ b/src/Event/CakeEvent.php
@@ -79,27 +79,27 @@ class CakeEvent
*
* @var mixed
*/
- public $data = null;
+ public mixed $data = null;
/**
* Property used to retain the result value of the event listeners
*
* @var mixed
*/
- public $result = null;
+ public mixed $result = null;
/**
* Flags an event as stopped or not, default is false
*
* @var bool
*/
- protected $_stopped = false;
+ protected bool $_stopped = false;
/**
* Constructor
*
* @param string $name Name of the event
- * @param object $subject the object that this event applies to (usually the object that is generating the event)
+ * @param object|null $subject the object that this event applies to (usually the object that is generating the event)
* @param mixed $data any value you wish to be transported with this event to it can be read by listeners
*
* ## Examples of usage:
@@ -109,7 +109,7 @@ class CakeEvent
* $event = new CakeEvent('User.afterRegister', $UserModel);
* ```
*/
- public function __construct($name, $subject = null, $data = null)
+ public function __construct(string $name, ?object $subject = null, mixed $data = null)
{
$this->_name = $name;
$this->data = $data;
@@ -122,19 +122,21 @@ public function __construct($name, $subject = null, $data = null)
* @param string $attribute Attribute name.
* @return mixed
*/
- public function __get($attribute)
+ public function __get(string $attribute): mixed
{
if ($attribute === 'name' || $attribute === 'subject') {
return $this->{$attribute}();
}
+
+ return null;
}
/**
* Returns the name of this event. This is usually used as the event identifier
*
- * @return string
+ * @return string|null
*/
- public function name()
+ public function name(): ?string
{
return $this->_name;
}
@@ -142,9 +144,9 @@ public function name()
/**
* Returns the subject of this event
*
- * @return object
+ * @return object|null
*/
- public function subject()
+ public function subject(): ?object
{
return $this->_subject;
}
diff --git a/src/Event/CakeEventManager.php b/src/Event/CakeEventManager.php
index 94f9ea602e..cec594554e 100644
--- a/src/Event/CakeEventManager.php
+++ b/src/Event/CakeEventManager.php
@@ -33,28 +33,28 @@ class CakeEventManager
*
* @var int
*/
- public static $defaultPriority = 10;
+ public static int $defaultPriority = 10;
/**
* The globally available instance, used for dispatching events attached from any scope
*
- * @var CakeEventManager
+ * @var CakeEventManager|null
*/
- protected static $_generalManager = null;
+ protected static ?CakeEventManager $_generalManager = null;
/**
* List of listener callbacks associated to
*
- * @var object
+ * @var array
*/
- protected $_listeners = [];
+ protected array $_listeners = [];
/**
* Internal flag to distinguish a common manager from the singleton
*
* @var bool
*/
- protected $_isGlobal = false;
+ protected bool $_isGlobal = false;
/**
* Returns the globally available instance of a CakeEventManager
@@ -64,10 +64,10 @@ class CakeEventManager
*
* If called with the first parameter, it will be set as the globally available instance
*
- * @param CakeEventManager $manager Optional event manager instance.
+ * @param CakeEventManager|null $manager Optional event manager instance.
* @return CakeEventManager the global event manager
*/
- public static function instance($manager = null)
+ public static function instance(?CakeEventManager $manager = null): CakeEventManager
{
if ($manager instanceof CakeEventManager) {
static::$_generalManager = $manager;
@@ -84,12 +84,12 @@ public static function instance($manager = null)
/**
* Adds a new listener to an event. Listeners
*
- * @param CakeEventListener|callable $callable PHP valid callback type or instance of CakeEventListener to be called
+ * @param CakeEventListener|callable|array|string $callable PHP valid callback type or instance of CakeEventListener to be called
* when the event named with $eventKey is triggered. If a CakeEventListener instance is passed, then the `implementedEvents`
* method will be called on the object to register the declared events individually as methods to be managed by this class.
* It is possible to define multiple event handlers per event name.
*
- * @param string $eventKey The event unique identifier name with which the callback will be associated. If $callable
+ * @param string|null $eventKey The event unique identifier name with which the callback will be associated. If $callable
* is an instance of CakeEventListener this argument will be ignored
*
* @param array $options used to set the `priority` and `passParams` flags to the listener.
@@ -100,8 +100,11 @@ public static function instance($manager = null)
* @throws InvalidArgumentException When event key is missing or callable is not an
* instance of CakeEventListener.
*/
- public function attach($callable, $eventKey = null, $options = [])
- {
+ public function attach(
+ CakeEventListener|callable|array|string $callable,
+ ?string $eventKey = null,
+ array $options = [],
+ ): void {
if (!$eventKey && !($callable instanceof CakeEventListener)) {
throw new InvalidArgumentException(__d('cake_dev', 'The eventKey variable is required'));
}
@@ -124,9 +127,9 @@ public function attach($callable, $eventKey = null, $options = [])
* @param CakeEventListener $subscriber Event listener.
* @return void
*/
- protected function _attachSubscriber(CakeEventListener $subscriber)
+ protected function _attachSubscriber(CakeEventListener $subscriber): void
{
- foreach ((array)$subscriber->implementedEvents() as $eventKey => $function) {
+ foreach ($subscriber->implementedEvents() as $eventKey => $function) {
$options = [];
$method = $function;
if (is_array($function) && isset($function['callable'])) {
@@ -151,9 +154,9 @@ protected function _attachSubscriber(CakeEventListener $subscriber)
*
* @param array $function the array taken from a handler definition for an event
* @param CakeEventListener $object The handler object
- * @return callable
+ * @return array
*/
- protected function _extractCallable($function, $object)
+ protected function _extractCallable(array $function, CakeEventListener $object): array
{
$method = $function['callable'];
$options = $function;
@@ -168,12 +171,14 @@ protected function _extractCallable($function, $object)
/**
* Removes a listener from the active listeners.
*
- * @param CakeEventListener|callable $callable any valid PHP callback type or an instance of CakeEventListener
- * @param string $eventKey The event unique identifier name with which the callback has been associated
+ * @param CakeEventListener|array|string $callable any valid PHP callback type or an instance of CakeEventListener
+ * @param string|null $eventKey The event unique identifier name with which the callback has been associated
* @return void
*/
- public function detach($callable, $eventKey = null): void
- {
+ public function detach(
+ CakeEventListener|array|string $callable,
+ ?string $eventKey = null,
+ ): void {
if ($callable instanceof CakeEventListener) {
$this->_detachSubscriber($callable, $eventKey);
@@ -203,12 +208,14 @@ public function detach($callable, $eventKey = null): void
* Auxiliary function to help detach all listeners provided by an object implementing CakeEventListener
*
* @param CakeEventListener $subscriber the subscriber to be detached
- * @param string $eventKey optional event key name to unsubscribe the listener from
+ * @param string|null $eventKey optional event key name to unsubscribe the listener from
* @return void
*/
- protected function _detachSubscriber(CakeEventListener $subscriber, $eventKey = null): void
- {
- $events = (array)$subscriber->implementedEvents();
+ protected function _detachSubscriber(
+ CakeEventListener $subscriber,
+ ?string $eventKey = null,
+ ): void {
+ $events = $subscriber->implementedEvents();
if (!empty($eventKey) && empty($events[$eventKey])) {
return;
} elseif (!empty($eventKey)) {
@@ -236,7 +243,7 @@ protected function _detachSubscriber(CakeEventListener $subscriber, $eventKey =
* @return CakeEvent
* @triggers $event
*/
- public function dispatch($event)
+ public function dispatch(CakeEvent|string $event): CakeEvent
{
if (is_string($event)) {
$event = new CakeEvent($event);
@@ -273,7 +280,7 @@ public function dispatch($event)
* @param string $eventKey Event key.
* @return array
*/
- public function listeners($eventKey)
+ public function listeners(string $eventKey): array
{
$localListeners = [];
if (!$this->_isGlobal) {
@@ -306,7 +313,7 @@ public function listeners($eventKey)
* @param string $eventKey Event key.
* @return array
*/
- public function prioritisedListeners($eventKey)
+ public function prioritisedListeners(string $eventKey): array
{
if (empty($this->_listeners[$eventKey])) {
return [];
diff --git a/src/I18n/I18n.php b/src/I18n/I18n.php
index ffc9ee25e3..e8d96f125f 100644
--- a/src/I18n/I18n.php
+++ b/src/I18n/I18n.php
@@ -37,44 +37,44 @@ class I18n
/**
* Instance of the L10n class for localization
*
- * @var L10n
+ * @var L10n|null
*/
- public $l10n = null;
+ public ?L10n $l10n = null;
/**
* Default domain of translation
*
* @var string
*/
- public static $defaultDomain = 'default';
+ public static string $defaultDomain = 'default';
/**
* Current domain of translation
*
- * @var string
+ * @var string|null
*/
- public $domain = null;
+ public ?string $domain = null;
/**
* Current category of translation
*
* @var string
*/
- public $category = 'LC_MESSAGES';
+ public string $category = 'LC_MESSAGES';
/**
* Current language used for translations
*
- * @var string
+ * @var string|null
*/
- protected $_lang = null;
+ protected ?string $_lang = null;
/**
* Translation strings for a specific domain read from the .mo or .po files
*
* @var array
*/
- protected $_domains = [];
+ protected array $_domains = [];
/**
* Set to true when I18N::_bindTextDomain() is called for the first time.
@@ -82,15 +82,21 @@ class I18n
*
* @var bool
*/
- protected $_noLocale = false;
+ protected bool $_noLocale = false;
/**
* Translation categories
*
* @var array
*/
- protected $_categories = [
- 'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', 'LC_NUMERIC', 'LC_TIME', 'LC_MESSAGES',
+ protected array $_categories = [
+ 'LC_ALL',
+ 'LC_COLLATE',
+ 'LC_CTYPE',
+ 'LC_MONETARY',
+ 'LC_NUMERIC',
+ 'LC_TIME',
+ 'LC_MESSAGES',
];
/**
@@ -158,9 +164,9 @@ class I18n
/**
* Escape string
*
- * @var string
+ * @var string|null
*/
- protected $_escape = null;
+ protected ?string $_escape = null;
/**
* Constructor, use I18n::getInstance() to get the i18n translation object.
@@ -175,7 +181,7 @@ public function __construct()
*
* @return I18n
*/
- public static function getInstance()
+ public static function getInstance(): I18n
{
static $instance = [];
if (!$instance) {
@@ -190,26 +196,26 @@ public static function getInstance()
* Returns a translated string based on current language and translation files stored in locale folder
*
* @param string $singular String to translate
- * @param string $plural Plural string (if any)
- * @param string $domain Domain The domain of the translation. Domains are often used by plugin translations.
+ * @param string|null $plural Plural string (if any)
+ * @param string|null $domain Domain The domain of the translation. Domains are often used by plugin translations.
* If null, the default domain will be used.
- * @param string|int $category Category The integer value of the category to use.
- * @param int $count Count Count is used with $plural to choose the correct plural form.
- * @param string $language Language to translate string to.
+ * @param string|int|null $category Category The integer value of the category to use.
+ * @param int|null $count Count Count is used with $plural to choose the correct plural form.
+ * @param string|null $language Language to translate string to.
* If null it checks for language in session followed by Config.language configuration variable.
- * @param string $context Context The context of the translation, e.g a verb or a noun.
- * @return string translated string.
+ * @param string|null $context Context The context of the translation, e.g a verb or a noun.
+ * @return array|string translated string.
* @throws CakeException When '' is provided as a domain.
*/
public static function translate(
- $singular,
- $plural = null,
- $domain = null,
- $category = self::LC_MESSAGES,
- $count = null,
- $language = null,
- $context = null,
- ) {
+ string $singular,
+ ?string $plural = null,
+ ?string $domain = null,
+ string|int|null $category = self::LC_MESSAGES,
+ ?int $count = null,
+ ?string $language = null,
+ ?string $context = null,
+ ): array|string {
$_this = I18n::getInstance();
if (str_contains($singular, "\r\n")) {
@@ -273,32 +279,28 @@ public static function translate(
}
if (!empty($_this->_domains[$domain][$_this->_lang][$_this->category][$singular][$context])) {
- if (
- ($trans = $_this->_domains[$domain][$_this->_lang][$_this->category][$singular][$context]) ||
- ($plurals) && ($trans = $_this->_domains[$domain][$_this->_lang][$_this->category][$plural][$context])
- ) {
- if (is_array($trans)) {
- if (isset($trans[$plurals])) {
- $trans = $trans[$plurals];
- } else {
- trigger_error(
- __d(
- 'cake_dev',
- 'Missing plural form translation for "%s" in "%s" domain, "%s" locale. ' .
- ' Check your po file for correct plurals and valid Plural-Forms header.',
- $singular,
- $domain,
- $_this->_lang,
- ),
- E_USER_WARNING,
- );
- $trans = $trans[0];
- }
- }
- if (strlen($trans)) {
- return $trans;
+ $trans = $_this->_domains[$domain][$_this->_lang][$_this->category][$singular][$context];
+ if (is_array($trans)) {
+ if (isset($trans[$plurals])) {
+ $trans = $trans[$plurals];
+ } else {
+ trigger_error(
+ __d(
+ 'cake_dev',
+ 'Missing plural form translation for "%s" in "%s" domain, "%s" locale. ' .
+ ' Check your po file for correct plurals and valid Plural-Forms header.',
+ $singular,
+ $domain,
+ $_this->_lang,
+ ),
+ E_USER_WARNING,
+ );
+ $trans = $trans[0];
}
}
+ if (strlen($trans)) {
+ return $trans;
+ }
}
if (!empty($plurals)) {
@@ -313,7 +315,7 @@ public static function translate(
*
* @return void
*/
- public static function clear()
+ public static function clear(): void
{
$self = I18n::getInstance();
$self->_domains = [];
@@ -324,7 +326,7 @@ public static function clear()
*
* @return array
*/
- public static function domains()
+ public static function domains(): array
{
$self = I18n::getInstance();
@@ -334,13 +336,13 @@ public static function domains()
/**
* Attempts to find the plural form of a string.
*
- * @param string $header Type
+ * @param string|null $header Type
* @param int $n Number
* @return int plural match
* @link http://localization-guide.readthedocs.org/en/latest/l10n/pluralforms.html
* @link https://developer.mozilla.org/en-US/docs/Mozilla/Localization/Localization_and_Plurals#List_of_Plural_Rules
*/
- protected function _pluralGuess($header, $n)
+ protected function _pluralGuess(?string $header, int $n): int
{
if (!is_string($header) || $header === 'nplurals=1;plural=0;' || !isset($header[0])) {
return 0;
@@ -399,7 +401,7 @@ protected function _pluralGuess($header, $n)
* @param string $domain Domain to bind
* @return string Domain binded
*/
- protected function _bindTextDomain($domain)
+ protected function _bindTextDomain(string $domain): string
{
$this->_noLocale = true;
$core = true;
@@ -502,7 +504,7 @@ protected function _bindTextDomain($domain)
}
if (isset($this->_domains[$domain][$this->_lang][$this->category]['%po-header']['plural-forms'])) {
- $switch = preg_replace('/(?:[() {}\\[\\]^\\s*\\]]+)/', '', $this->_domains[$domain][$this->_lang][$this->category]['%po-header']['plural-forms']);
+ $switch = preg_replace('/[() {}\[\]^*]+/', '', $this->_domains[$domain][$this->_lang][$this->category]['%po-header']['plural-forms']);
$this->_domains[$domain][$this->_lang][$this->category]['%plural-c'] = $switch;
unset($this->_domains[$domain][$this->_lang][$this->category]['%po-header']);
}
@@ -520,20 +522,24 @@ protected function _bindTextDomain($domain)
* Loads the binary .mo file and returns array of translations
*
* @param string $filename Binary .mo file to load
- * @return mixed Array of translations on success or false on failure
+ * @return array|bool Array of translations on success or false on failure
* @link https://www.gnu.org/software/gettext/manual/html_node/MO-Files.html
*/
- public static function loadMo($filename)
+ public static function loadMo(string $filename): array|bool
{
$translations = false;
- // @codingStandardsIgnoreStart
// Binary files extracted makes non-standard local variables
if ($data = file_get_contents($filename)) {
$translations = [];
$header = substr($data, 0, 20);
$header = unpack('L1magic/L1version/L1count/L1o_msg/L1o_trn', $header);
- extract($header);
+
+ $magic = $header['magic'] ?? null;
+ $version = $header['version'] ?? null;
+ $count = $header['count'] ?? null;
+ $o_msg = $header['o_msg'] ?? null;
+ $o_trn = $header['o_trn'] ?? null;
if ((dechex($magic) === '950412de' || dechex($magic) === 'ffffffff950412de') && !$version) {
for ($n = 0; $n < $count; $n++) {
@@ -567,7 +573,6 @@ public static function loadMo($filename)
}
}
}
- // @codingStandardsIgnoreEnd
return $translations;
}
@@ -576,9 +581,9 @@ public static function loadMo($filename)
* Loads the text .po file and returns array of translations
*
* @param string $filename Text .po file to load
- * @return mixed Array of translations on success or false on failure
+ * @return array|bool Array of translations on success or false on failure
*/
- public static function loadPo($filename)
+ public static function loadPo(string $filename): array|bool
{
if (!$file = fopen($filename, 'r')) {
return false;
@@ -658,9 +663,9 @@ public static function loadPo($filename)
* Parses a locale definition file following the POSIX standard
*
* @param string $filename Locale definition filename
- * @return mixed Array of definitions on success or false on failure
+ * @return array|bool Array of definitions on success or false on failure
*/
- public static function loadLocaleDefinition($filename)
+ public static function loadLocaleDefinition(string $filename): array|bool
{
if (!$file = fopen($filename, 'r')) {
return false;
@@ -703,13 +708,14 @@ public static function loadLocaleDefinition($filename)
}
$mustEscape = [$escape . ',', $escape . ';', $escape . '<', $escape . '>', $escape . $escape];
+ /** @var array $replacements */
$replacements = array_map('crc32', $mustEscape);
$value = str_replace($mustEscape, $replacements, $value);
$value = explode(';', $value);
$_this->_escape = $escape;
foreach ($value as $i => $val) {
$val = trim($val, '"');
- $val = preg_replace_callback('/(?:<)?(.[^>]*)(?:>)?/', [&$_this, '_parseLiteralValue'], $val);
+ $val = preg_replace_callback('/(.[^>]*)>?/', [&$_this, '_parseLiteralValue'], $val);
$val = str_replace($replacements, $mustEscape, $val);
$value[$i] = $val;
}
@@ -726,12 +732,14 @@ public static function loadLocaleDefinition($filename)
/**
* Puts the parameters in raw translated strings
*
- * @param string $translated The raw translated string
+ * @param array|string $translated The raw translated string
* @param array $args The arguments to put in the translation
- * @return mixed Translated string with arguments
+ * @return array|string Translated string with arguments
*/
- public static function insertArgs($translated, array $args): mixed
- {
+ public static function insertArgs(
+ array|string $translated,
+ array $args,
+ ): array|string {
$len = count($args);
if ($len === 0 || ($len === 1 && $args[0] === null)) {
return $translated;
@@ -749,10 +757,10 @@ public static function insertArgs($translated, array $args): mixed
/**
* Auxiliary function to parse a symbol from a locale definition file
*
- * @param string $string Symbol to be parsed
+ * @param array|string $string Symbol to be parsed
* @return string parsed symbol
*/
- protected function _parseLiteralValue($string)
+ protected function _parseLiteralValue(array|string $string): string
{
$string = $string[1];
if (substr($string, 0, 2) === $this->_escape . 'x') {
@@ -763,17 +771,23 @@ protected function _parseLiteralValue($string)
if (substr($string, 0, 2) === $this->_escape . 'd') {
$delimiter = $this->_escape . 'd';
- return implode('', array_map('chr', array_filter(explode($delimiter, $string))));
+ return implode('', array_map(function ($value) {
+ return chr((int)$value);
+ }, array_filter(explode($delimiter, $string))));
}
if ($string[0] === $this->_escape && isset($string[1]) && is_numeric($string[1])) {
$delimiter = $this->_escape;
- return implode('', array_map('chr', array_filter(explode($delimiter, $string))));
+ return implode('', array_map(function ($value) {
+ return chr((int)$value);
+ }, array_filter(explode($delimiter, $string))));
}
if (str_starts_with($string, 'U00')) {
$delimiter = 'U00';
- return implode('', array_map('chr', array_map('hexdec', array_filter(explode($delimiter, $string)))));
+ return implode('', array_map(function ($value) {
+ return chr((int)$value);
+ }, array_map('hexdec', array_filter(explode($delimiter, $string)))));
}
if (preg_match('/U([0-9a-fA-F]{4})/', $string, $match)) {
return Multibyte::ascii([hexdec($match[1])]);
@@ -789,7 +803,7 @@ protected function _parseLiteralValue($string)
* @param string $domain Domain where format is stored
* @return mixed translated format string if only value or array of translated strings for corresponding format.
*/
- protected function _translateTime($format, $domain)
+ protected function _translateTime(string $format, string $domain): mixed
{
if (!empty($this->_domains[$domain][$this->_lang]['LC_TIME'][$format])) {
if (($trans = $this->_domains[$domain][$this->_lang][$this->category][$format])) {
diff --git a/src/I18n/L10n.php b/src/I18n/L10n.php
index ffef5e4be9..e7efcbf686 100644
--- a/src/I18n/L10n.php
+++ b/src/I18n/L10n.php
@@ -33,28 +33,28 @@ class L10n
*
* @var string
*/
- public $language = 'English (United States)';
+ public string $language = 'English (United States)';
/**
* Locale search paths
*
* @var array
*/
- public $languagePath = ['en_us', 'eng'];
+ public array $languagePath = ['en_us', 'eng'];
/**
* ISO 639-3 for current locale
*
* @var string
*/
- public $lang = 'eng';
+ public string $lang = 'eng';
/**
* Locale
*
* @var string
*/
- public $locale = 'en_us';
+ public string $locale = 'en_us';
/**
* Default language.
@@ -63,23 +63,23 @@ class L10n
* as a fall back else if DEFAULT_LANGUAGE it defined it will be used.
* Constant DEFAULT_LANGUAGE has been deprecated in 2.4
*
- * @var string
+ * @var string|null
*/
- public $default = null;
+ public ?string $default = null;
/**
* Encoding used for current locale
*
* @var string
*/
- public $charset = 'utf-8';
+ public string $charset = 'utf-8';
/**
* Text direction for current locale
*
* @var string
*/
- public $direction = 'ltr';
+ public string $direction = 'ltr';
/**
* Maps ISO 639-3 to I10n::_l10nCatalog
@@ -89,7 +89,7 @@ class L10n
*
* @var array
*/
- protected $_l10nMap = [
+ protected array $_l10nMap = [
/* Afrikaans */ 'afr' => 'af',
/* Albanian */ 'sqi' => 'sq',
/* Albanian - bibliographic */ 'alb' => 'sq',
@@ -185,7 +185,7 @@ class L10n
*
* @var array
*/
- protected $_l10nCatalog = [
+ protected array $_l10nCatalog = [
'af' => ['language' => 'Afrikaans', 'locale' => 'afr', 'localeFallback' => 'afr', 'charset' => 'utf-8', 'direction' => 'ltr'],
'ar' => ['language' => 'Arabic', 'locale' => 'ara', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'],
'ar-ae' => ['language' => 'Arabic (U.A.E.)', 'locale' => 'ar_ae', 'localeFallback' => 'ara', 'charset' => 'utf-8', 'direction' => 'rtl'],
@@ -364,10 +364,10 @@ public function __construct()
* If $language is null it attempt to get settings from L10n::_autoLanguage(); if this fails
* the method will get the settings from L10n::_setLanguage();
*
- * @param string $language Language (if null will use DEFAULT_LANGUAGE if defined)
+ * @param string|null $language Language (if null will use DEFAULT_LANGUAGE if defined)
* @return mixed
*/
- public function get($language = null)
+ public function get(?string $language = null): mixed
{
if ($language !== null) {
return $this->_setLanguage($language);
@@ -384,10 +384,10 @@ public function get($language = null)
* Sets the class vars to correct values for $language.
* If $language is null it will use the L10n::$default if defined
*
- * @param string $language Language (if null will use L10n::$default if defined)
+ * @param string|null $language Language (if null will use L10n::$default if defined)
* @return mixed
*/
- protected function _setLanguage($language = null)
+ protected function _setLanguage(?string $language = null): mixed
{
$catalog = false;
if ($language !== null) {
@@ -431,6 +431,8 @@ protected function _setLanguage($language = null)
if ($language) {
return $language;
}
+
+ return null;
}
/**
@@ -438,7 +440,7 @@ protected function _setLanguage($language = null)
*
* @return bool Success
*/
- protected function _autoLanguage()
+ protected function _autoLanguage(): bool
{
$_detectableLanguages = CakeRequest::acceptLanguage();
foreach ($_detectableLanguages as $langKey) {
@@ -463,11 +465,11 @@ protected function _autoLanguage()
/**
* Attempts to find locale for language, or language for locale
*
- * @param array|string $mixed 2/3 char string (language/locale), array of those strings, or null
- * @return array|string|bool string language/locale, array of those values, whole map as an array,
+ * @param array|string|null $mixed 2/3 char string (language/locale), array of those strings, or null
+ * @return array|string|false string language/locale, array of those values, whole map as an array,
* or false when language/locale doesn't exist
*/
- public function map($mixed = null)
+ public function map(array|string|null $mixed = null): array|string|false
{
if (is_array($mixed)) {
$result = [];
@@ -493,11 +495,11 @@ public function map($mixed = null)
/**
* Attempts to find catalog record for requested language
*
- * @param array|string $language string requested language, array of requested languages, or null for whole catalog
+ * @param array|string|null $language string requested language, array of requested languages, or null for whole catalog
* @return array|false array catalog record for requested language, array of catalog records, whole catalog,
* or false when language doesn't exist
*/
- public function catalog($language = null)
+ public function catalog(array|string|null $language = null): array|false
{
if (is_array($language)) {
$result = [];
diff --git a/src/I18n/Multibyte.php b/src/I18n/Multibyte.php
index a70daec523..edb8169c92 100644
--- a/src/I18n/Multibyte.php
+++ b/src/I18n/Multibyte.php
@@ -33,21 +33,21 @@ class Multibyte
*
* @var array
*/
- protected static $_caseFold = [];
+ protected static array $_caseFold = [];
/**
* Holds an array of Unicode code point ranges
*
* @var array
*/
- protected static $_codeRange = [];
+ protected static array $_codeRange = [];
/**
* Holds the current code point range
*
- * @var string
+ * @var string|null
*/
- protected static $_table = null;
+ protected static ?string $_table = null;
/**
* Converts a multibyte character string
@@ -56,7 +56,7 @@ class Multibyte
* @param string $string String to convert.
* @return array
*/
- public static function utf8($string)
+ public static function utf8(string $string): array
{
$map = [];
@@ -97,7 +97,7 @@ public static function utf8($string)
* @param array $array Values array.
* @return string
*/
- public static function ascii($array)
+ public static function ascii(array $array): string
{
$ascii = '';
@@ -123,11 +123,14 @@ public static function ascii($array)
* @param string $haystack The string from which to get the position of the first occurrence of $needle.
* @param string $needle The string to find in $haystack.
* @param int $offset The position in $haystack to start searching.
- * @return int|bool The numeric position of the first occurrence of $needle in the $haystack string,
+ * @return int|false The numeric position of the first occurrence of $needle in the $haystack string,
* or false if $needle is not found.
*/
- public static function stripos($haystack, $needle, $offset = 0)
- {
+ public static function stripos(
+ string $haystack,
+ string $needle,
+ int $offset = 0,
+ ): int|false {
return mb_stripos($haystack, $needle, $offset);
}
@@ -140,10 +143,13 @@ public static function stripos($haystack, $needle, $offset = 0)
* If set to true, it returns all of $haystack from the beginning to the first occurrence of $needle.
* If set to false, it returns all of $haystack from the first occurrence of $needle to the end,
* Default value is false.
- * @return int|bool The portion of $haystack, or false if $needle is not found.
+ * @return string|false The portion of $haystack, or false if $needle is not found.
*/
- public static function stristr($haystack, $needle, $part = false)
- {
+ public static function stristr(
+ string $haystack,
+ string $needle,
+ bool $part = false,
+ ): string|false {
return mb_stristr($haystack, $needle, $part);
}
@@ -153,7 +159,7 @@ public static function stristr($haystack, $needle, $part = false)
* @param string $string The string being checked for length.
* @return int The number of characters in string $string
*/
- public static function strlen($string)
+ public static function strlen(string $string): int
{
return mb_strlen($string);
}
@@ -164,11 +170,14 @@ public static function strlen($string)
* @param string $haystack The string being checked.
* @param string $needle The position counted from the beginning of haystack.
* @param int $offset The search offset. If it is not specified, 0 is used.
- * @return int|bool The numeric position of the first occurrence of $needle in the $haystack string.
+ * @return int|false The numeric position of the first occurrence of $needle in the $haystack string.
* If $needle is not found, it returns false.
*/
- public static function strpos($haystack, $needle, $offset = 0)
- {
+ public static function strpos(
+ string $haystack,
+ string $needle,
+ int $offset = 0,
+ ): int|false {
return mb_strpos($haystack, $needle, $offset);
}
@@ -181,10 +190,13 @@ public static function strpos($haystack, $needle, $offset = 0)
* If set to true, it returns all of $haystack from the beginning to the last occurrence of $needle.
* If set to false, it returns all of $haystack from the last occurrence of $needle to the end,
* Default value is false.
- * @return string|bool The portion of $haystack. or false if $needle is not found.
+ * @return string|false The portion of $haystack. or false if $needle is not found.
*/
- public static function strrchr($haystack, $needle, $part = false)
- {
+ public static function strrchr(
+ string $haystack,
+ string $needle,
+ bool $part = false,
+ ): string|false {
return mb_strrchr($haystack, $needle, $part);
}
@@ -197,10 +209,13 @@ public static function strrchr($haystack, $needle, $part = false)
* If set to true, it returns all of $haystack from the beginning to the last occurrence of $needle.
* If set to false, it returns all of $haystack from the last occurrence of $needle to the end,
* Default value is false.
- * @return string|bool The portion of $haystack. or false if $needle is not found.
+ * @return string|false The portion of $haystack. or false if $needle is not found.
*/
- public static function strrichr($haystack, $needle, $part = false)
- {
+ public static function strrichr(
+ string $haystack,
+ string $needle,
+ bool $part = false,
+ ): string|false {
return mb_strrichr($haystack, $needle, $part);
}
@@ -210,11 +225,14 @@ public static function strrichr($haystack, $needle, $part = false)
* @param string $haystack The string from which to get the position of the last occurrence of $needle.
* @param string $needle The string to find in $haystack.
* @param int $offset The position in $haystack to start searching.
- * @return int|bool The numeric position of the last occurrence of $needle in the $haystack string,
+ * @return int|false The numeric position of the last occurrence of $needle in the $haystack string,
* or false if $needle is not found.
*/
- public static function strripos($haystack, $needle, $offset = 0)
- {
+ public static function strripos(
+ string $haystack,
+ string $needle,
+ int $offset = 0,
+ ): int|false {
return mb_strripos($haystack, $needle, $offset);
}
@@ -225,11 +243,14 @@ public static function strripos($haystack, $needle, $offset = 0)
* @param string $needle The string to find in $haystack.
* @param int $offset May be specified to begin searching an arbitrary number of characters into the string.
* Negative values will stop searching at an arbitrary point prior to the end of the string.
- * @return int|bool The numeric position of the last occurrence of $needle in the $haystack string.
+ * @return int|false The numeric position of the last occurrence of $needle in the $haystack string.
* If $needle is not found, it returns false.
*/
- public static function strrpos($haystack, $needle, $offset = 0)
- {
+ public static function strrpos(
+ string $haystack,
+ string $needle,
+ int $offset = 0,
+ ): int|false {
return mb_strrpos($haystack, $needle, $offset);
}
@@ -242,10 +263,13 @@ public static function strrpos($haystack, $needle, $offset = 0)
* If set to true, it returns all of $haystack from the beginning to the first occurrence of $needle.
* If set to false, it returns all of $haystack from the first occurrence of $needle to the end,
* Default value is FALSE.
- * @return string|bool The portion of $haystack, or true if $needle is not found.
+ * @return string|false The portion of $haystack, or true if $needle is not found.
*/
- public static function strstr($haystack, $needle, $part = false)
- {
+ public static function strstr(
+ string $haystack,
+ string $needle,
+ bool $part = false,
+ ): string|false {
return mb_strstr($haystack, $needle, $part);
}
@@ -255,22 +279,18 @@ public static function strstr($haystack, $needle, $part = false)
* @param string $string The string being lowercased.
* @return string with all alphabetic characters converted to lowercase.
*/
- public static function strtolower($string)
+ public static function strtolower(string $string): string
{
$utf8Map = Multibyte::utf8($string);
$length = count($utf8Map);
- $lowerCase = [];
+ $lowerCase = [];
for ($i = 0; $i < $length; $i++) {
$char = $utf8Map[$i];
if ($char < 128) {
- $str = strtolower(chr($char));
- $strlen = strlen($str);
- for ($ii = 0; $ii < $strlen; $ii++) {
- $lower = ord(substr($str, $ii, 1));
- }
+ $lower = ord(strtolower(chr($char)));
$lowerCase[] = $lower;
$matched = true;
} else {
@@ -278,11 +298,11 @@ public static function strtolower($string)
$keys = static::_find($char, 'upper');
if (!empty($keys)) {
- foreach ($keys as $key => $value) {
- if ($keys[$key]['upper'] == $char && count($keys[$key]['lower']) > 0) {
- $lowerCase[] = $keys[$key]['lower'][0];
+ foreach ($keys as $value) {
+ if ($value['upper'] == $char && count($value['lower']) > 0) {
+ $lowerCase[] = $value['lower'][0];
$matched = true;
- break 1;
+ break;
}
}
}
@@ -301,7 +321,7 @@ public static function strtolower($string)
* @param string $string The string being uppercased.
* @return string with all alphabetic characters converted to uppercase.
*/
- public static function strtoupper($string)
+ public static function strtoupper(string $string): string
{
$utf8Map = Multibyte::utf8($string);
@@ -313,11 +333,7 @@ public static function strtoupper($string)
$char = $utf8Map[$i];
if ($char < 128) {
- $str = strtoupper(chr($char));
- $strlen = strlen($str);
- for ($ii = 0; $ii < $strlen; $ii++) {
- $upper = ord(substr($str, $ii, 1));
- }
+ $upper = ord(strtoupper(chr($char)));
$upperCase[] = $upper;
$matched = true;
} else {
@@ -326,24 +342,24 @@ public static function strtoupper($string)
$keyCount = count($keys);
if (!empty($keys)) {
- foreach ($keys as $key => $value) {
+ foreach ($keys as $value) {
$matched = false;
$replace = 0;
- if ($length > 1 && count($keys[$key]['lower']) > 1) {
+ if ($length > 1 && count($value['lower']) > 1) {
$j = 0;
- for ($ii = 0, $count = count($keys[$key]['lower']); $ii < $count; $ii++) {
+ for ($ii = 0, $count = count($value['lower']); $ii < $count; $ii++) {
$nextChar = $utf8Map[$i + $ii];
- if (isset($nextChar) && ($nextChar == $keys[$key]['lower'][$j + $ii])) {
+ if (isset($nextChar) && ($nextChar == $value['lower'][$j + $ii])) {
$replace++;
}
}
if ($replace == $count) {
- $upperCase[] = $keys[$key]['upper'];
- $replaced = array_merge($replaced, array_values($keys[$key]['lower']));
+ $upperCase[] = $value['upper'];
+ $replaced = array_merge($replaced, array_values($value['lower']));
$matched = true;
- break 1;
+ break;
}
} elseif ($length > 1 && $keyCount > 1) {
$j = 0;
@@ -367,10 +383,10 @@ public static function strtoupper($string)
}
}
}
- if ($keys[$key]['lower'][0] == $char) {
- $upperCase[] = $keys[$key]['upper'];
+ if ($value['lower'][0] == $char) {
+ $upperCase[] = $value['upper'];
$matched = true;
- break 1;
+ break;
}
}
}
@@ -390,7 +406,7 @@ public static function strtoupper($string)
* @param string $needle The string being found.
* @return int The number of times the $needle substring occurs in the $haystack string.
*/
- public static function substrCount($haystack, $needle)
+ public static function substrCount(string $haystack, string $needle): int
{
return mb_substr_count($haystack, $needle);
}
@@ -400,11 +416,14 @@ public static function substrCount($haystack, $needle)
*
* @param string $string The string being checked.
* @param int $start The first position used in $string.
- * @param int $length The maximum length of the returned string.
+ * @param int|null $length The maximum length of the returned string.
* @return string The portion of $string specified by the $string and $length parameters.
*/
- public static function substr($string, $start, $length = null)
- {
+ public static function substr(
+ string $string,
+ int $start,
+ ?int $length = null,
+ ): string {
return mb_substr($string, $start, $length);
}
@@ -412,12 +431,15 @@ public static function substr($string, $start, $length = null)
* Prepare a string for mail transport, using the provided encoding
*
* @param string $string value to encode
- * @param string $charset charset to use for encoding. defaults to UTF-8
+ * @param string|null $charset charset to use for encoding. defaults to UTF-8
* @param string $newline Newline string.
* @return string
*/
- public static function mimeEncode($string, $charset = null, $newline = "\r\n")
- {
+ public static function mimeEncode(
+ string $string,
+ ?string $charset = null,
+ string $newline = "\r\n",
+ ): string {
if (!Multibyte::checkMultibyte($string) && strlen($string) < 75) {
return $string;
}
@@ -452,7 +474,7 @@ public static function mimeEncode($string, $charset = null, $newline = "\r\n")
$string = implode($spacer, $parts);
} else {
$string = chunk_split(base64_encode($string), $length, $spacer);
- $string = preg_replace('/' . preg_quote($spacer) . '$/', '', $string);
+ $string = preg_replace('/' . preg_quote($spacer, '/') . '$/', '', $string);
}
return $start . $string . $end;
@@ -462,9 +484,9 @@ public static function mimeEncode($string, $charset = null, $newline = "\r\n")
* Return the Code points range for Unicode characters
*
* @param int $decimal Decimal value.
- * @return string
+ * @return string|false
*/
- protected static function _codepoint($decimal)
+ protected static function _codepoint(int $decimal): string|false
{
if ($decimal > 128 && $decimal < 256) {
$return = '0080_00ff'; // Latin-1 Supplement
@@ -515,7 +537,7 @@ protected static function _codepoint($decimal)
* @param string $type Type 'lower' or 'upper'. Defaults to 'lower'.
* @return array
*/
- protected static function _find($char, $type = 'lower')
+ protected static function _find(int $char, string $type = 'lower'): array
{
$found = [];
if (!isset(static::$_codeRange[$char])) {
@@ -554,7 +576,7 @@ protected static function _find($char, $type = 'lower')
* @param string $string Value to test.
* @return bool
*/
- public static function checkMultibyte($string)
+ public static function checkMultibyte(string $string): bool
{
$length = strlen($string);
diff --git a/src/LegacyClassLoader.php b/src/LegacyClassLoader.php
index b0ab3360ad..9eee48fc32 100644
--- a/src/LegacyClassLoader.php
+++ b/src/LegacyClassLoader.php
@@ -23,7 +23,7 @@ class LegacyClassLoader
*
* @var array
*/
- private static $classMap = [
+ private static array $classMap = [
'AbstractPasswordHasher' => 'Cake\Controller\Component\Auth\AbstractPasswordHasher',
'AbstractTransport' => 'Cake\Network\Email\AbstractTransport',
'AclBehavior' => 'Cake\Model\Behavior\AclBehavior',
@@ -236,13 +236,13 @@ public static function getClassMap(): array
* Autoload function for legacy class names
*
* @param string $class The class name to autoload
- * @return bool True if the class was loaded, false otherwise
+ * @return void
*/
- public static function autoload(string $class): bool
+ public static function autoload(string $class): void
{
// Skip if class is already namespaced
if (str_contains($class, '\\')) {
- return false;
+ return;
}
// Handle App* base classes (AppController, AppModel, AppHelper, AppShell)
@@ -252,18 +252,14 @@ public static function autoload(string $class): bool
if ($namespacedClass && class_exists($namespacedClass)) {
class_alias($namespacedClass, $class);
- return true;
+ return;
}
}
// Check if we have a mapping for this legacy class name
if (isset(self::$classMap[$class])) {
class_alias(self::$classMap[$class], $class);
-
- return true;
}
-
- return false;
}
/**
@@ -355,7 +351,7 @@ class_alias('Cake\Error\XmlException', 'XmlException');
// IDE helper - class aliases for code completion (never executed)
// phpcs:disable
-if (false) {
+if (false) { // @phpstan-ignore-line
class_alias('Cake\Controller\Component\Auth\AbstractPasswordHasher', 'AbstractPasswordHasher');
class_alias('Cake\Network\Email\AbstractTransport', 'AbstractTransport');
class_alias('Cake\Model\Behavior\AclBehavior', 'AclBehavior');
diff --git a/src/Log/CakeLog.php b/src/Log/CakeLog.php
index 3a28b15242..d2959fac04 100644
--- a/src/Log/CakeLog.php
+++ b/src/Log/CakeLog.php
@@ -79,9 +79,9 @@ class CakeLog
/**
* LogEngineCollection class
*
- * @var LogEngineCollection
+ * @var LogEngineCollection|null
*/
- protected static $_Collection;
+ protected static ?LogEngineCollection $_Collection = null;
/**
* Default log levels as detailed in RFC 5424
@@ -92,7 +92,7 @@ class CakeLog
*
* @var array
*/
- protected static $_defaultLevels = [
+ protected static array $_defaultLevels = [
'emergency' => LOG_EMERG,
'alert' => LOG_ALERT,
'critical' => LOG_CRIT,
@@ -115,14 +115,14 @@ class CakeLog
*
* @var array
*/
- protected static $_levelMap;
+ protected static array $_levelMap = [];
/**
* initialize ObjectCollection
*
* @return void
*/
- protected static function _init()
+ protected static function _init(): void
{
static::$_levels = static::defaultLevels();
static::$_Collection = new LogEngineCollection();
@@ -192,7 +192,7 @@ protected static function _init()
* @throws CakeLogException
* @link https://book.cakephp.org/2.0/en/core-libraries/logging.html#creating-and-configuring-log-streams
*/
- public static function config($key, $config)
+ public static function config(string $key, array $config): bool
{
if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $key)) {
throw new CakeLogException(__d('cake_dev', 'Invalid key name'));
@@ -423,7 +423,7 @@ public static function stream($streamName)
* @return bool Success
* @link https://book.cakephp.org/2.0/en/core-libraries/logging.html#writing-to-logs
*/
- public static function write($type, $message, $scope = [])
+ public static function write(string|int $type, $message, $scope = []): bool
{
if (empty(static::$_Collection)) {
static::_init();
diff --git a/src/Log/CakeLogInterface.php b/src/Log/CakeLogInterface.php
index 9ba683024e..6fb3d0c597 100644
--- a/src/Log/CakeLogInterface.php
+++ b/src/Log/CakeLogInterface.php
@@ -31,7 +31,7 @@ interface CakeLogInterface
*
* @param string $type Message type.
* @param string $message Message to write.
- * @return void
+ * @return bool|null
*/
- public function write($type, $message);
+ public function write(string $type, string $message): ?bool;
}
diff --git a/src/Log/Engine/BaseLog.php b/src/Log/Engine/BaseLog.php
index 47a23f9228..a95806e597 100644
--- a/src/Log/Engine/BaseLog.php
+++ b/src/Log/Engine/BaseLog.php
@@ -32,14 +32,14 @@ abstract class BaseLog implements CakeLogInterface
*
* @var array
*/
- protected $_config = [];
+ protected array $_config = [];
/**
* Constructor
*
* @param array $config Configuration array
*/
- public function __construct($config = [])
+ public function __construct(array $config = [])
{
$this->config($config);
}
@@ -55,7 +55,7 @@ public function __construct($config = [])
* @param array $config engine configuration
* @return array
*/
- public function config($config = [])
+ public function config(array $config = []): array
{
if (!empty($config)) {
foreach (['types', 'scopes'] as $option) {
diff --git a/src/Log/Engine/ConsoleLog.php b/src/Log/Engine/ConsoleLog.php
index 2eb9b3cbb7..13ca8acaf1 100644
--- a/src/Log/Engine/ConsoleLog.php
+++ b/src/Log/Engine/ConsoleLog.php
@@ -32,9 +32,9 @@ class ConsoleLog extends BaseLog
/**
* Output stream
*
- * @var ConsoleOutput
+ * @var ConsoleOutput|null
*/
- protected $_output = null;
+ protected ?ConsoleOutput $_output = null;
/**
* Constructs a new Console Logger.
@@ -49,22 +49,14 @@ class ConsoleLog extends BaseLog
* @param array $config Options for the FileLog, see above.
* @throws CakeLogException
*/
- public function __construct($config = [])
+ public function __construct(array $config = [])
{
parent::__construct($config);
- if (
- (DS === '\\' && !(bool)env('ANSICON') && env('ConEmuANSI') !== 'ON') ||
- (function_exists('posix_isatty') && !posix_isatty($this->_output))
- ) {
- $outputAs = ConsoleOutput::PLAIN;
- } else {
- $outputAs = ConsoleOutput::COLOR;
- }
+
$config = Hash::merge([
'stream' => 'php://stderr',
'types' => null,
'scopes' => [],
- 'outputAs' => $outputAs,
], $this->_config);
$config = $this->config($config);
if ($config['stream'] instanceof ConsoleOutput) {
@@ -74,7 +66,7 @@ public function __construct($config = [])
} else {
throw new CakeLogException('`stream` not a ConsoleOutput nor string');
}
- $this->_output->outputAs($config['outputAs']);
+ $this->_config['outputAs'] = $this->_output->outputAs();
}
/**
@@ -82,12 +74,12 @@ public function __construct($config = [])
*
* @param string $type The type of log you are making.
* @param string $message The message you want to log.
- * @return bool success of write.
+ * @return bool|null success of write.
*/
- public function write($type, $message)
+ public function write(string $type, string $message): ?bool
{
$output = date('Y-m-d H:i:s') . ' ' . ucfirst($type) . ': ' . $message . "\n";
- return $this->_output->write(sprintf('<%s>%s%s>', $type, $output, $type), false);
+ return $this->_output->write(sprintf('<%s>%s%s>', $type, $output, $type), 0);
}
}
diff --git a/src/Log/Engine/FileLog.php b/src/Log/Engine/FileLog.php
index 4ba2d29c3c..6b8c9190e7 100644
--- a/src/Log/Engine/FileLog.php
+++ b/src/Log/Engine/FileLog.php
@@ -36,7 +36,7 @@ class FileLog extends BaseLog
* @var array
* @see FileLog::__construct()
*/
- protected $_defaults = [
+ protected array $_defaults = [
'path' => LOGS,
'file' => null,
'types' => null,
@@ -49,23 +49,23 @@ class FileLog extends BaseLog
/**
* Path to save log files on.
*
- * @var string
+ * @var string|null
*/
- protected $_path = null;
+ protected ?string $_path = null;
/**
* Log file name
*
- * @var string
+ * @var string|null
*/
- protected $_file = null;
+ protected ?string $_file = null;
/**
* Max file size, used for log file rotation.
*
- * @var int
+ * @var int|null
*/
- protected $_size = null;
+ protected ?int $_size = null;
/**
* Constructs a new File Logger.
@@ -87,7 +87,7 @@ class FileLog extends BaseLog
*
* @param array $config Options for the FileLog, see above.
*/
- public function __construct($config = [])
+ public function __construct(array $config = [])
{
$config = Hash::merge($this->_defaults, $config);
parent::__construct($config);
@@ -99,7 +99,7 @@ public function __construct($config = [])
* @param array $config Engine configuration
* @return array
*/
- public function config($config = [])
+ public function config(array $config = []): array
{
parent::config($config);
@@ -132,9 +132,9 @@ public function config($config = [])
*
* @param string $type The type of log you are making.
* @param string $message The message you want to log.
- * @return bool success of write.
+ * @return bool|null success of write.
*/
- public function write($type, $message)
+ public function write(string $type, string $message): ?bool
{
$output = date('Y-m-d H:i:s') . ' ' . ucfirst($type) . ': ' . $message . "\n";
$filename = $this->_getFilename($type);
@@ -169,7 +169,7 @@ public function write($type, $message)
* @param string $type The type of log.
* @return string File name
*/
- protected function _getFilename($type)
+ protected function _getFilename(string $type): string
{
$debugTypes = ['notice', 'info', 'debug'];
@@ -191,10 +191,10 @@ protected function _getFilename($type)
* Also if `rotate` count is reached oldest file is removed.
*
* @param string $filename Log file name
- * @return mixed True if rotated successfully or false in case of error, otherwise null.
+ * @return bool|null True if rotated successfully or false in case of error, otherwise null.
* Void if file doesn't need to be rotated.
*/
- protected function _rotateFile($filename)
+ protected function _rotateFile(string $filename): bool|null
{
$filepath = $this->_path . $filename;
clearstatcache(true, $filepath);
diff --git a/src/Log/Engine/SyslogLog.php b/src/Log/Engine/SyslogLog.php
index 63f836d542..d6e8f65ec1 100644
--- a/src/Log/Engine/SyslogLog.php
+++ b/src/Log/Engine/SyslogLog.php
@@ -51,7 +51,7 @@ class SyslogLog extends BaseLog
*
* @var array
*/
- protected $_defaults = [
+ protected array $_defaults = [
'format' => '%s: %s',
'flag' => LOG_ODELAY,
'prefix' => '',
@@ -63,7 +63,7 @@ class SyslogLog extends BaseLog
*
* @var array
*/
- protected $_priorityMap = [
+ protected array $_priorityMap = [
'emergency' => LOG_EMERG,
'alert' => LOG_ALERT,
'critical' => LOG_CRIT,
@@ -79,7 +79,7 @@ class SyslogLog extends BaseLog
*
* @var bool
*/
- protected $_open = false;
+ protected bool $_open = false;
/**
* Make sure the configuration contains the format parameter, by default it uses
@@ -87,7 +87,7 @@ class SyslogLog extends BaseLog
*
* @param array $config Options list.
*/
- public function __construct($config = [])
+ public function __construct(array $config = [])
{
$config += $this->_defaults;
parent::__construct($config);
@@ -101,9 +101,9 @@ public function __construct($config = [])
*
* @param string $type The type of log you are making.
* @param string $message The message you want to log.
- * @return bool success of write.
+ * @return bool|null success of write.
*/
- public function write($type, $message)
+ public function write(string $type, string $message): ?bool
{
if (!$this->_open) {
$config = $this->_config;
@@ -134,7 +134,7 @@ public function write($type, $message)
* @param int $facility the stream or facility to log to
* @return void
*/
- protected function _open($ident, $options, $facility)
+ protected function _open(string $ident, int $options, int $facility): void
{
openlog($ident, $options, $facility);
}
@@ -147,7 +147,7 @@ protected function _open($ident, $options, $facility)
* @param string $message Message to log.
* @return bool
*/
- protected function _write($priority, $message)
+ protected function _write(int $priority, string $message): bool
{
return syslog($priority, $message);
}
diff --git a/src/Log/LogEngineCollection.php b/src/Log/LogEngineCollection.php
index 6775504cb3..fbc2fbfc5d 100644
--- a/src/Log/LogEngineCollection.php
+++ b/src/Log/LogEngineCollection.php
@@ -29,6 +29,11 @@
*/
class LogEngineCollection extends ObjectCollection
{
+ /**
+ * @var array
+ */
+ protected array $_loaded = [];
+
/**
* Loads/constructs a Log engine.
*
@@ -37,7 +42,7 @@ class LogEngineCollection extends ObjectCollection
* @return CakeLogInterface BaseLog engine instance
* @throws CakeLogException when logger class does not implement a write method
*/
- public function load($name, $options = [])
+ public function load(string $name, array $options = []): CakeLogInterface
{
$enable = $options['enabled'] ?? true;
$loggerName = $options['engine'];
@@ -62,10 +67,10 @@ public function load($name, $options = [])
* Checks that the logger class implements a write method as well.
*
* @param string $loggerName the plugin.className of the logger class you want to build.
- * @return mixed boolean false on any failures, string of classname to use if search was successful.
+ * @return string|null null on any failures, string of classname to use if search was successful.
* @throws CakeLogException
*/
- protected static function _getLogger($loggerName)
+ protected static function _getLogger(string $loggerName): ?string
{
[$plugin, $name] = pluginSplit($loggerName, true);
$originalLoggerName = $loggerName;
diff --git a/src/Model/AclNode.php b/src/Model/AclNode.php
index 74095e76d8..d63e751fa4 100644
--- a/src/Model/AclNode.php
+++ b/src/Model/AclNode.php
@@ -18,6 +18,7 @@
use Cake\Core\Configure;
use Cake\Error\CakeException;
+use Cake\Model\Datasource\DboSource;
use Cake\Utility\ClassRegistry;
use Cake\Utility\Inflector;
@@ -47,11 +48,14 @@ class AclNode extends Model
*
* @param array|string|int|bool $id Set this ID for this model on startup,
* can also be an array of options, see above.
- * @param string $table Name of database table to use.
- * @param string $ds DataSource connection name.
+ * @param string|null $table Name of database table to use.
+ * @param string|null $ds DataSource connection name.
*/
- public function __construct($id = false, $table = null, $ds = null)
- {
+ public function __construct(
+ array|string|int|bool $id = false,
+ ?string $table = null,
+ ?string $ds = null,
+ ) {
$config = Configure::read('Acl.database');
if (isset($config)) {
$this->useDbConfig = $config;
@@ -62,12 +66,14 @@ public function __construct($id = false, $table = null, $ds = null)
/**
* Retrieves the Aro/Aco node for this model
*
- * @param Model|array|string $ref Array with 'model' and 'foreign_key', model object, or string value
- * @return array Node found in database
+ * @param Model|array|string|null $ref Array with 'model' and 'foreign_key', model object, or string value
+ * @return array|false|null Node found in database
* @throws CakeException when binding to a model that doesn't exist.
*/
- public function node($ref = null)
- {
+ public function node(
+ Model|array|string|null $ref = null,
+ ): array|false|null {
+ /** @var DboSource $db */
$db = $this->getDataSource();
$type = $this->alias;
$result = null;
@@ -134,16 +140,17 @@ public function node($ref = null)
) {
return false;
}
- } elseif (is_object($ref) && $ref instanceof Model) {
+ } elseif ($ref instanceof Model) {
$ref = ['model' => $ref->name, 'foreign_key' => $ref->id];
- } elseif (is_array($ref) && !(isset($ref['model']) && isset($ref['foreign_key']))) {
+ } elseif (!(isset($ref['model']) && isset($ref['foreign_key']))) {
$name = key($ref);
[, $alias] = pluginSplit($name);
+ /** @var Model $model */
$model = ClassRegistry::init(['class' => $name, 'alias' => $alias]);
if (empty($model)) {
- throw new CakeException('cake_dev', "Model class '%s' not found in AclNode::node() when trying to bind %s object", $type, $this->alias);
+ throw new CakeException(__d('cake_dev', "Model class '%s' not found in AclNode::node() when trying to bind %s object", $type, $this->alias));
}
$tmpRef = null;
diff --git a/src/Model/Aco.php b/src/Model/Aco.php
index 18aa854988..431d0ec431 100644
--- a/src/Model/Aco.php
+++ b/src/Model/Aco.php
@@ -26,7 +26,7 @@ class Aco extends AclNode
/**
* Model name
*
- * @var string
+ * @var string|null
*/
public ?string $name = 'Aco';
diff --git a/src/Model/AcoAction.php b/src/Model/AcoAction.php
index b1b23e7f2e..60c7903bdb 100644
--- a/src/Model/AcoAction.php
+++ b/src/Model/AcoAction.php
@@ -31,7 +31,7 @@ class AcoAction extends AppModel
/**
* Model name
*
- * @var string
+ * @var string|null
*/
public ?string $name = 'AcoAction';
diff --git a/src/Model/Aro.php b/src/Model/Aro.php
index fc8de28f06..da9a7030e6 100644
--- a/src/Model/Aro.php
+++ b/src/Model/Aro.php
@@ -26,7 +26,7 @@ class Aro extends AclNode
/**
* Model name
*
- * @var string
+ * @var string|null
*/
public ?string $name = 'Aro';
diff --git a/src/Model/Behavior/AclBehavior.php b/src/Model/Behavior/AclBehavior.php
index 563f892345..d2f535e1c2 100644
--- a/src/Model/Behavior/AclBehavior.php
+++ b/src/Model/Behavior/AclBehavior.php
@@ -40,7 +40,7 @@ class AclBehavior extends ModelBehavior
*
* @var array
*/
- protected $_typeMaps = ['requester' => 'Aro', 'controlled' => 'Aco', 'both' => ['Aro', 'Aco']];
+ protected array $_typeMaps = ['requester' => 'Aro', 'controlled' => 'Aco', 'both' => ['Aro', 'Aco']];
/**
* Sets up the configuration for the model, and loads ACL models if they haven't been already
@@ -49,7 +49,7 @@ class AclBehavior extends ModelBehavior
* @param array $config Configuration options.
* @return void
*/
- public function setup(Model $model, $config = [])
+ public function setup(Model $model, array $config = []): void
{
if (isset($config[0])) {
$config['type'] = $config[0];
@@ -76,12 +76,15 @@ public function setup(Model $model, $config = [])
*
* @param Model $model Model using this behavior.
* @param Model|array|string $ref Array with 'model' and 'foreign_key', model object, or string value
- * @param string $type Only needed when Acl is set up as 'both', specify 'Aro' or 'Aco' to get the correct node
+ * @param string|null $type Only needed when Acl is set up as 'both', specify 'Aro' or 'Aco' to get the correct node
* @return array
* @link https://book.cakephp.org/2.0/en/core-libraries/behaviors/acl.html#node
*/
- public function node(Model $model, $ref = null, $type = null)
- {
+ public function node(
+ Model $model,
+ Model|array|string|null $ref = null,
+ string|null $type = null,
+ ): array {
if (empty($type)) {
$type = $this->_typeMaps[$this->settings[$model->name]['type']];
if (is_array($type)) {
@@ -112,7 +115,9 @@ public function afterSave(Model $model, bool $created, array $options = []): ?bo
$types = [$types];
}
foreach ($types as $type) {
- $parent = $model->parentNode($type);
+ $parent = method_exists($model, 'parentNode')
+ ? $model->parentNode($type)
+ : null;
if (!empty($parent)) {
$parent = $this->node($model, $parent, $type);
}
@@ -145,7 +150,7 @@ public function afterDelete(Model $model): ?bool
$types = [$types];
}
foreach ($types as $type) {
- $node = Hash::extract($this->node($model, null, $type), "0.{$type}.id");
+ $node = Hash::get($this->node($model, null, $type), "0.{$type}.id");
if (!empty($node)) {
$model->{$type}->delete($node);
}
diff --git a/src/Model/Behavior/ContainableBehavior.php b/src/Model/Behavior/ContainableBehavior.php
index 1cf23531f2..985743dce2 100644
--- a/src/Model/Behavior/ContainableBehavior.php
+++ b/src/Model/Behavior/ContainableBehavior.php
@@ -39,14 +39,14 @@ class ContainableBehavior extends ModelBehavior
*
* @var array
*/
- public $types = ['belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'];
+ public array $types = ['belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'];
/**
* Runtime configuration for this behavior
*
* @var array
*/
- public $runtime = [];
+ public array $runtime = [];
/**
* Initiate behavior for the model using specified settings.
@@ -63,15 +63,15 @@ class ContainableBehavior extends ModelBehavior
* bindings. DEFAULTS TO: true
*
* @param Model $model Model using the behavior
- * @param array $settings Settings to override for model.
+ * @param array $config Settings to override for model.
* @return void
*/
- public function setup(Model $model, $settings = [])
+ public function setup(Model $model, array $config = []): void
{
if (!isset($this->settings[$model->alias])) {
$this->settings[$model->alias] = ['recursive' => true, 'notices' => true, 'autoFields' => true];
}
- $this->settings[$model->alias] = array_merge($this->settings[$model->alias], $settings);
+ $this->settings[$model->alias] = array_merge($this->settings[$model->alias], $config);
}
/**
@@ -93,9 +93,9 @@ public function setup(Model $model, $settings = [])
*
* @param Model $model Model using the behavior
* @param array $query Query parameters as set by cake
- * @return array
+ * @return array|bool|null
*/
- public function beforeFind(Model $model, $query)
+ public function beforeFind(Model $model, array $query): array|bool|null
{
$reset = ($query['reset'] ?? true);
$noContain = false;
@@ -160,7 +160,7 @@ public function beforeFind(Model $model, $query)
$instance->unbindModel([$type => $unbind], $reset);
}
foreach ($instance->{$type} as $assoc => $options) {
- if (isset($_model['keep'][$assoc]) && !empty($_model['keep'][$assoc])) {
+ if (!empty($_model['keep'][$assoc])) {
if (isset($_model['keep'][$assoc]['fields'])) {
$_model['keep'][$assoc]['fields'] = $this->fieldDependencies($containments['models'][$assoc]['instance'], $map, $_model['keep'][$assoc]['fields']);
}
@@ -251,7 +251,7 @@ public function contain(Model $model, ...$args): void
* @param Model $model Model on which to reset bindings
* @return void
*/
- public function resetBindings(Model $model)
+ public function resetBindings(Model $model): void
{
if (!empty($model->__backOriginalAssociation)) {
$model->__backAssociation = $model->__backOriginalAssociation;
@@ -273,17 +273,21 @@ public function resetBindings(Model $model)
* @param Model $model Model on which binding restriction is being applied
* @param array $contain Parameters to use for restricting this model
* @param array $containments Current set of containments
- * @param bool $throwErrors Whether non-existent bindings show throw errors
+ * @param bool|null $throwErrors Whether non-existent bindings show throw errors
* @return array Containments
*/
- public function containments(Model $model, $contain, $containments = [], $throwErrors = null)
- {
+ public function containments(
+ Model $model,
+ array $contain,
+ array $containments = [],
+ ?bool $throwErrors = null,
+ ): array {
$options = ['className', 'joinTable', 'with', 'foreignKey', 'associationForeignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'unique', 'finderQuery'];
$keep = [];
if ($throwErrors === null) {
$throwErrors = (empty($this->settings[$model->alias]) ? true : $this->settings[$model->alias]['notices']);
}
- foreach ((array)$contain as $name => $children) {
+ foreach ($contain as $name => $children) {
if (is_numeric($name)) {
$name = $children;
$children = [];
@@ -340,7 +344,7 @@ public function containments(Model $model, $contain, $containments = [], $throwE
}
if ($optionKey && isset($children[$key])) {
if (!empty($keep[$name][$key]) && is_array($keep[$name][$key])) {
- $keep[$name][$key] = array_merge(($keep[$name][$key] ?? []), (array)$children[$key]);
+ $keep[$name][$key] = array_merge($keep[$name][$key], (array)$children[$key]);
} else {
$keep[$name][$key] = $children[$key];
}
@@ -377,11 +381,14 @@ public function containments(Model $model, $contain, $containments = [], $throwE
*
* @param Model $model Model
* @param array $map Map of relations for given model
- * @param array|bool $fields If array, fields to initially load, if false use $Model as primary model
- * @return array Fields
+ * @param array|string|false $fields If array, fields to initially load, if false use $Model as primary model
+ * @return array|string Fields
*/
- public function fieldDependencies(Model $model, $map, $fields = [])
- {
+ public function fieldDependencies(
+ Model $model,
+ array $map,
+ array|string|false $fields = [],
+ ): array|string {
if ($fields === false) {
$fields = [];
foreach ($map as $parent => $children) {
@@ -401,6 +408,7 @@ public function fieldDependencies(Model $model, $map, $fields = [])
if (empty($map[$model->alias])) {
return $fields;
}
+
foreach ($map[$model->alias] as $type => $bindings) {
foreach ($bindings as $dependency) {
$innerFields = [];
@@ -429,7 +437,7 @@ public function fieldDependencies(Model $model, $map, $fields = [])
* @param array $containments Containments
* @return array Built containments
*/
- public function containmentsMap($containments)
+ public function containmentsMap(array $containments): array
{
$map = [];
foreach ($containments['models'] as $name => $model) {
diff --git a/src/Model/Behavior/TranslateBehavior.php b/src/Model/Behavior/TranslateBehavior.php
index 222be6f4d5..0a4bac5137 100644
--- a/src/Model/Behavior/TranslateBehavior.php
+++ b/src/Model/Behavior/TranslateBehavior.php
@@ -20,6 +20,7 @@
use Cake\Error\CakeException;
use Cake\I18n\I18n;
use Cake\Model\ConnectionManager;
+use Cake\Model\Datasource\DboSource;
use Cake\Model\Model;
use Cake\Model\ModelBehavior;
use Cake\Utility\CakeText;
@@ -39,21 +40,21 @@ class TranslateBehavior extends ModelBehavior
*
* @var array
*/
- public $runtime = [];
+ public array $runtime = [];
/**
* Stores the joinTable object for generating joins.
*
* @var object
*/
- protected $_joinTable;
+ protected object $_joinTable;
/**
* Stores the runtime model for generating joins.
*
* @var Model
*/
- protected $_runtimeModel;
+ protected Model $_runtimeModel;
/**
* Callback
@@ -89,9 +90,9 @@ class TranslateBehavior extends ModelBehavior
*
* @param Model $model Model the behavior is being attached to.
* @param array $config Array of configuration information.
- * @return mixed
+ * @return void
*/
- public function setup(Model $model, $config = [])
+ public function setup(Model $model, array $config = []): void
{
$db = ConnectionManager::getDataSource($model->useDbConfig);
if (!$db->connected) {
@@ -99,8 +100,6 @@ public function setup(Model $model, $config = [])
__d('cake_dev', 'Datasource %s for TranslateBehavior of model %s is not connected', $model->useDbConfig, $model->alias),
E_USER_ERROR,
);
-
- return false;
}
$this->settings[$model->alias] = [];
@@ -113,8 +112,7 @@ public function setup(Model $model, $config = [])
unset($config['joinType']);
}
$this->translateModel($model);
-
- return $this->bindTranslation($model, $config, false);
+ $this->bindTranslation($model, $config, false);
}
/**
@@ -123,7 +121,7 @@ public function setup(Model $model, $config = [])
* @param Model $model Model being detached.
* @return void
*/
- public function cleanup(Model $model)
+ public function cleanup(Model $model): void
{
$this->unbindTranslation($model);
unset($this->settings[$model->alias]);
@@ -135,15 +133,17 @@ public function cleanup(Model $model)
*
* @param Model $model Model find is being run on.
* @param array $query Array of Query parameters.
- * @return array Modified query
+ * @return array|bool|null Modified query
*/
- public function beforeFind(Model $model, $query)
+ public function beforeFind(Model $model, array $query): array|bool|null
{
$this->runtime[$model->alias]['virtualFields'] = $model->virtualFields;
$locale = $this->_getLocale($model);
if (empty($locale)) {
return $query;
}
+
+ /** @var DboSource $db */
$db = $model->getDataSource();
$RuntimeModel = $this->translateModel($model);
@@ -200,7 +200,7 @@ public function beforeFind(Model $model, $query)
* @param array $query The query array to take fields from.
* @return array The fields.
*/
- protected function _getFields(Model $model, $query)
+ protected function _getFields(Model $model, array $query): array
{
$fields = array_merge(
$this->settings[$model->alias],
@@ -237,7 +237,7 @@ protected function _getFields(Model $model, $query)
* @param array $addFields The fields being joined.
* @return array The modified query
*/
- protected function _addAllJoins(Model $model, $query, $addFields)
+ protected function _addAllJoins(Model $model, array $query, array $addFields): array
{
$locale = $this->_getLocale($model);
if ($addFields) {
@@ -264,9 +264,9 @@ protected function _addAllJoins(Model $model, $query, $addFields)
* @param array $query The query array.
* @return array The list of translated fields that are in the conditions.
*/
- protected function _checkConditions(Model $model, $query)
+ protected function _checkConditions(Model $model, array $query): array
{
- if (empty($query['conditions']) || (!empty($query['conditions']) && !is_array($query['conditions']))) {
+ if (empty($query['conditions']) || !is_array($query['conditions'])) {
return [];
}
@@ -280,7 +280,7 @@ protected function _checkConditions(Model $model, $query)
* @param array $conditions The conditions array.
* @return array The list of condition fields.
*/
- protected function _getConditionFields(Model $model, $conditions)
+ protected function _getConditionFields(Model $model, array $conditions): array
{
$conditionFields = [];
foreach ($conditions as $col => $val) {
@@ -311,8 +311,14 @@ protected function _getConditionFields(Model $model, $conditions)
* @param array|string $locale The locale(s) having joins added.
* @return array The modified query
*/
- protected function _addJoin(Model $model, $query, $field, $aliasField, $locale)
- {
+ protected function _addJoin(
+ Model $model,
+ array $query,
+ string $field,
+ string $aliasField,
+ array|string $locale,
+ ): array {
+ /** @var DboSource $db */
$db = ConnectionManager::getDataSource($model->useDbConfig);
$RuntimeModel = $this->_runtimeModel;
$joinTable = $this->_joinTable;
@@ -367,7 +373,7 @@ protected function _addJoin(Model $model, $query, $field, $aliasField, $locale)
* @param bool $primary Did the find originate on $model.
* @return array Modified results
*/
- public function afterFind(Model $model, $results, $primary = false)
+ public function afterFind(Model $model, mixed $results, bool $primary = false): mixed
{
$model->virtualFields = $this->runtime[$model->alias]['virtualFields'];
@@ -421,7 +427,7 @@ public function afterFind(Model $model, $results, $primary = false)
*
* @param Model $model Model invalidFields was called on.
* @param array $options Options passed from Model::save().
- * @return bool
+ * @return bool|null
* @see Model::save()
*/
public function beforeValidate(Model $model, array $options = []): ?bool
@@ -524,11 +530,7 @@ public function afterSave(Model $model, bool $created, array $options = []): ?bo
if (!isset($this->runtime[$model->alias]['beforeValidate']) && !isset($this->runtime[$model->alias]['beforeSave'])) {
return null;
}
- if (isset($this->runtime[$model->alias]['beforeValidate'])) {
- $tempData = $this->runtime[$model->alias]['beforeValidate'];
- } else {
- $tempData = $this->runtime[$model->alias]['beforeSave'];
- }
+ $tempData = $this->runtime[$model->alias]['beforeValidate'] ?? $this->runtime[$model->alias]['beforeSave'];
unset($this->runtime[$model->alias]['beforeValidate'], $this->runtime[$model->alias]['beforeSave']);
$conditions = ['model' => $model->name, 'foreign_key' => $model->id];
@@ -592,11 +594,11 @@ public function afterSave(Model $model, bool $created, array $options = []): ?bo
* @param array $data The sparse data that was provided.
* @return array The fully populated data to save.
*/
- protected function _prepareTranslations(Model $model, $data)
+ protected function _prepareTranslations(Model $model, array $data): array
{
$fields = array_merge($this->settings[$model->alias], $this->runtime[$model->alias]['fields']);
$locales = [];
- foreach ($data as $key => $value) {
+ foreach ($data as $value) {
if (is_array($value)) {
$locales = array_merge($locales, array_keys($value));
}
@@ -637,11 +639,11 @@ public function afterDelete(Model $model): ?bool
* Get selected locale for model
*
* @param Model $model Model the locale needs to be set/get on.
- * @return mixed string or false
+ * @return array|string|null string or false
*/
- protected function _getLocale(Model $model)
+ protected function _getLocale(Model $model): array|string|null
{
- if (!isset($model->locale) || $model->locale === null) {
+ if (!isset($model->locale)) {
$I18n = I18n::getInstance();
$I18n->l10n->get(Configure::read('Config.language'));
$model->locale = $I18n->l10n->locale;
@@ -659,10 +661,10 @@ protected function _getLocale(Model $model)
* @param Model $model Model to get a translatemodel for.
* @return Model
*/
- public function translateModel(Model $model)
+ public function translateModel(Model $model): Model
{
if (!isset($this->runtime[$model->alias]['model'])) {
- if (!isset($model->translateModel) || empty($model->translateModel)) {
+ if (empty($model->translateModel)) {
$className = 'I18nModel';
} else {
$className = $model->translateModel;
@@ -742,8 +744,6 @@ public function bindTranslation(Model $model, array|string $fields, bool $reset
__d('cake_dev', 'Association %s is already bound to model %s', $association, $model->alias),
E_USER_ERROR,
);
-
- return false;
}
}
$associations[$association] = array_merge($default, ['conditions' => [
@@ -767,7 +767,7 @@ public function bindTranslation(Model $model, array|string $fields, bool $reset
* @param string $field The field to update.
* @return void
*/
- protected function _removeField(Model $model, $field)
+ protected function _removeField(Model $model, string $field): void
{
if (array_key_exists($field, $this->settings[$model->alias])) {
unset($this->settings[$model->alias][$field]);
@@ -787,11 +787,11 @@ protected function _removeField(Model $model, $field)
* fake field
*
* @param Model $model using this behavior of model
- * @param array|string $fields string with field, or array(field1, field2=>AssocName, field3), or null for
+ * @param array|string|null $fields string with field, or array(field1, field2=>AssocName, field3), or null for
* unbind all original translations
* @return bool
*/
- public function unbindTranslation(Model $model, $fields = null)
+ public function unbindTranslation(Model $model, array|string|null $fields = null): bool
{
if (empty($fields) && empty($this->settings[$model->alias])) {
return false;
diff --git a/src/Model/Behavior/TreeBehavior.php b/src/Model/Behavior/TreeBehavior.php
index 450f205eb8..342761e502 100644
--- a/src/Model/Behavior/TreeBehavior.php
+++ b/src/Model/Behavior/TreeBehavior.php
@@ -21,6 +21,7 @@
namespace Cake\Model\Behavior;
use Cake\Model\ConnectionManager;
+use Cake\Model\Datasource\DboSource;
use Cake\Model\Model;
use Cake\Model\ModelBehavior;
use Cake\Utility\Hash;
@@ -41,14 +42,14 @@ class TreeBehavior extends ModelBehavior
*
* @var array
*/
- public $errors = [];
+ public array $errors = [];
/**
* Defaults
*
* @var array
*/
- protected $_defaults = [
+ protected array $_defaults = [
'parent' => 'parent_id', 'left' => 'lft', 'right' => 'rght', 'level' => null,
'scope' => '1 = 1', 'type' => 'nested', '__parentChange' => false, 'recursive' => -1,
];
@@ -58,7 +59,7 @@ class TreeBehavior extends ModelBehavior
*
* @var array
*/
- protected $_deletedRow = [];
+ protected array $_deletedRow = [];
/**
* Initiate Tree behavior
@@ -67,7 +68,7 @@ class TreeBehavior extends ModelBehavior
* @param array $config array of configuration settings.
* @return void
*/
- public function setup(Model $model, $config = [])
+ public function setup(Model $model, array $config = []): void
{
if (isset($config[0])) {
$config['type'] = $config[0];
@@ -95,9 +96,14 @@ public function setup(Model $model, $config = [])
* @param array $options Options passed from Model::save().
* @return bool|null true on success, false on failure
*/
- public function afterSave(Model $model, bool $created, array $options = []): ?bool
- {
- extract($this->settings[$model->alias]);
+ public function afterSave(
+ Model $model,
+ bool $created,
+ array $options = [],
+ ): ?bool {
+ $parent = $this->settings[$model->alias]['parent'] ?? null;
+ $level = $this->settings[$model->alias]['level'] ?? null;
+
if ($created) {
if (isset($model->data[$model->alias][$parent]) && $model->data[$model->alias][$parent]) {
$this->_setParent($model, $model->data[$model->alias][$parent], $created);
@@ -120,7 +126,7 @@ public function afterSave(Model $model, bool $created, array $options = []): ?bo
* @param string|int $id Record ID
* @return void
*/
- protected function _setChildrenLevel(Model $model, $id)
+ protected function _setChildrenLevel(Model $model, string|int $id): void
{
$settings = $this->settings[$model->alias];
$primaryKey = $model->primaryKey;
@@ -154,9 +160,9 @@ protected function _setChildrenLevel(Model $model, $id)
*
* @param Model $model Model using the behavior
* @param array $query Query parameters as set by cake
- * @return array
+ * @return array|bool|null
*/
- public function beforeFind(Model $model, $query)
+ public function beforeFind(Model $model, array $query): array|bool|null
{
if ($model->findQueryType === 'threaded' && !isset($query['parent'])) {
$query['parent'] = $this->settings[$model->alias]['parent'];
@@ -176,12 +182,15 @@ public function beforeFind(Model $model, $query)
*/
public function beforeDelete(Model $model, $cascade = true): ?bool
{
- extract($this->settings[$model->alias]);
+ $left = $this->settings[$model->alias]['left'] ?? null;
+ $right = $this->settings[$model->alias]['right'] ?? null;
+
$data = $model->find('first', [
'conditions' => [$model->escapeField($model->primaryKey) => $model->id],
'fields' => [$model->escapeField($left), $model->escapeField($right)],
'order' => false,
- 'recursive' => -1]);
+ 'recursive' => -1,
+ ]);
if ($data) {
$this->_deletedRow[$model->alias] = current($data);
}
@@ -199,7 +208,10 @@ public function beforeDelete(Model $model, $cascade = true): ?bool
*/
public function afterDelete(Model $model): ?bool
{
- extract($this->settings[$model->alias]);
+ $left = $this->settings[$model->alias]['left'] ?? null;
+ $right = $this->settings[$model->alias]['right'] ?? null;
+ $scope = $this->settings[$model->alias]['scope'] ?? null;
+
$data = $this->_deletedRow[$model->alias];
$this->_deletedRow[$model->alias] = null;
@@ -234,7 +246,12 @@ public function afterDelete(Model $model): ?bool
*/
public function beforeSave(Model $model, array $options = []): ?bool
{
- extract($this->settings[$model->alias]);
+ $left = $this->settings[$model->alias]['left'] ?? null;
+ $right = $this->settings[$model->alias]['right'] ?? null;
+ $level = $this->settings[$model->alias]['level'] ?? null;
+ $parent = $this->settings[$model->alias]['parent'] ?? null;
+ $recursive = $this->settings[$model->alias]['recursive'] ?? null;
+ $scope = $this->settings[$model->alias]['scope'] ?? null;
$this->_addToWhitelist($model, [$left, $right]);
if ($level) {
@@ -313,9 +330,9 @@ public function beforeSave(Model $model, array $options = []): ?bool
*
* @param Model $model Model using this behavior
* @param string|int $id The ID of the record to read
- * @return array|bool The record read or false
+ * @return array|false The record read or false
*/
- protected function _getNode(Model $model, $id)
+ protected function _getNode(Model $model, string|int $id): array|false
{
$settings = $this->settings[$model->alias];
$fields = [$model->primaryKey, $settings['parent'], $settings['left'], $settings['right']];
@@ -338,22 +355,30 @@ protected function _getNode(Model $model, $id)
* If false is passed for the id parameter, all top level nodes are counted, or all nodes are counted.
*
* @param Model $model Model using this behavior
- * @param string|int|bool $id The ID of the record to read or false to read all top level nodes
+ * @param array|string|int|bool|null $id The ID of the record to read or false to read all top level nodes
* @param bool $direct whether to count direct, or all, children
* @return int number of child nodes
* @link https://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::childCount
*/
- public function childCount(Model $model, $id = null, $direct = false)
- {
+ public function childCount(
+ Model $model,
+ array|string|int|bool|null $id = null,
+ bool $direct = false,
+ ): int {
if (is_array($id)) {
extract(array_merge(['id' => null], $id));
}
+
if ($id === null && $model->id) {
$id = $model->id;
} elseif (!$id) {
$id = null;
}
- extract($this->settings[$model->alias]);
+
+ $left = $this->settings[$model->alias]['left'] ?? null;
+ $right = $this->settings[$model->alias]['right'] ?? null;
+ $parent = $this->settings[$model->alias]['parent'] ?? null;
+ $scope = $this->settings[$model->alias]['scope'] ?? null;
if ($direct) {
return $model->find('count', ['conditions' => [$scope, $model->escapeField($parent) => $id]]);
@@ -381,23 +406,32 @@ public function childCount(Model $model, $id = null, $direct = false)
* If false is passed for the id parameter, top level, or all (depending on direct parameter appropriate) are counted.
*
* @param Model $model Model using this behavior
- * @param string|int $id The ID of the record to read
- * @param bool $direct whether to return only the direct, or all, children
- * @param array|string $fields Either a single string of a field name, or an array of field names
- * @param string $order SQL ORDER BY conditions (e.g. "price DESC" or "name ASC") defaults to the tree order
- * @param int $limit SQL LIMIT clause, for calculating items per page.
- * @param int $page Page number, for accessing paged data
- * @param int $recursive The number of levels deep to fetch associated records
- * @return array Array of child nodes
+ * @param array|string|int|null $id The ID of the record to read
+ * @param bool|null $direct whether to return only the direct, or all, children
+ * @param array|string|null $fields Either a single string of a field name, or an array of field names
+ * @param string|null $order SQL ORDER BY conditions (e.g. "price DESC" or "name ASC") defaults to the tree order
+ * @param int|null $limit SQL LIMIT clause, for calculating items per page.
+ * @param int|null $page Page number, for accessing paged data
+ * @param int|null $recursive The number of levels deep to fetch associated records
+ * @return array|false Array of child nodes
* @link https://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::children
*/
- public function children(Model $model, $id = null, $direct = false, $fields = null, $order = null, $limit = null, $page = 1, $recursive = null)
- {
+ public function children(
+ Model $model,
+ array|string|int|null $id = null,
+ ?bool $direct = false,
+ array|string|null $fields = null,
+ ?string $order = null,
+ ?int $limit = null,
+ ?int $page = 1,
+ ?int $recursive = null,
+ ): array|false {
$options = [];
if (is_array($id)) {
$options = $this->_getOptions($id);
extract(array_merge(['id' => null], $id));
}
+
$overrideRecursive = $recursive;
if ($id === null && $model->id) {
@@ -406,7 +440,13 @@ public function children(Model $model, $id = null, $direct = false, $fields = nu
$id = null;
}
- extract($this->settings[$model->alias]);
+ $left = $this->settings[$model->alias]['left'] ?? null;
+ $right = $this->settings[$model->alias]['right'] ?? null;
+ $parent = $this->settings[$model->alias]['parent'] ?? null;
+ $scope = $this->settings[$model->alias]['scope'] ?? null;
+ if (isset($this->settings[$model->alias]['recursive'])) {
+ $recursive = $this->settings[$model->alias]['recursive'];
+ }
if ($overrideRecursive !== null) {
$recursive = $overrideRecursive;
@@ -454,18 +494,31 @@ public function children(Model $model, $id = null, $direct = false, $fields = nu
* A convenience method for returning a hierarchical array used for HTML select boxes
*
* @param Model $model Model using this behavior
- * @param array|string $conditions SQL conditions as a string or as an array('field' =>'value',...)
- * @param string $keyPath A string path to the key, i.e. "{n}.Post.id"
- * @param string $valuePath A string path to the value, i.e. "{n}.Post.title"
+ * @param array|string|null $conditions SQL conditions as a string or as an array('field' =>'value',...)
+ * @param string|null $keyPath A string path to the key, i.e. "{n}.Post.id"
+ * @param array|string|null $valuePath A string path to the value, i.e. "{n}.Post.title"
* @param string $spacer The character or characters which will be repeated
- * @param int $recursive The number of levels deep to fetch associated records
+ * @param int|null $recursive The number of levels deep to fetch associated records
* @return array An associative array of records, where the id is the key, and the display field is the value
* @link https://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::generateTreeList
*/
- public function generateTreeList(Model $model, $conditions = null, $keyPath = null, $valuePath = null, $spacer = '_', $recursive = null)
- {
+ public function generateTreeList(
+ Model $model,
+ array|string|null $conditions = null,
+ ?string $keyPath = null,
+ array|string|null $valuePath = null,
+ string $spacer = '_',
+ ?int $recursive = null,
+ ): array {
$overrideRecursive = $recursive;
- extract($this->settings[$model->alias]);
+
+ $left = $this->settings[$model->alias]['left'] ?? null;
+ $right = $this->settings[$model->alias]['right'] ?? null;
+ $scope = $this->settings[$model->alias]['scope'] ?? null;
+ if (isset($this->settings[$model->alias]['recursive'])) {
+ $recursive = $this->settings[$model->alias]['recursive'];
+ }
+
if ($overrideRecursive !== null) {
$recursive = $overrideRecursive;
}
@@ -503,11 +556,15 @@ public function generateTreeList(Model $model, $conditions = null, $keyPath = nu
* @param array $options Options
* @return array An associative array of records, where the id is the key, and the display field is the value
*/
- public function formatTreeList(Model $model, array $results, array $options = [])
- {
+ public function formatTreeList(
+ Model $model,
+ array $results,
+ array $options = [],
+ ): array {
if (empty($results)) {
return [];
}
+
$defaults = [
'keyPath' => null,
'valuePath' => null,
@@ -515,7 +572,7 @@ public function formatTreeList(Model $model, array $results, array $options = []
];
$options += $defaults;
- extract($this->settings[$model->alias]);
+ $right = $this->settings[$model->alias]['right'] ?? null;
if (!$options['keyPath']) {
$options['keyPath'] = '{n}.' . $model->alias . '.' . $model->primaryKey;
@@ -550,14 +607,18 @@ public function formatTreeList(Model $model, array $results, array $options = []
* reads the parent id and returns this node
*
* @param Model $model Model using this behavior
- * @param string|int $id The ID of the record to read
- * @param array|string $fields Fields to get
- * @param int $recursive The number of levels deep to fetch associated records
- * @return array|bool Array of data for the parent node
+ * @param array|string|int|null $id The ID of the record to read
+ * @param array|string|null $fields Fields to get
+ * @param int|null $recursive The number of levels deep to fetch associated records
+ * @return array|false Array of data for the parent node
* @link https://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::getParentNode
*/
- public function getParentNode(Model $model, $id = null, $fields = null, $recursive = null)
- {
+ public function getParentNode(
+ Model $model,
+ array|string|int|null $id = null,
+ array|string|null $fields = null,
+ int|null $recursive = null,
+ ): array|false {
$options = [];
if (is_array($id)) {
$options = $this->_getOptions($id);
@@ -567,7 +628,12 @@ public function getParentNode(Model $model, $id = null, $fields = null, $recursi
if (empty($id)) {
$id = $model->id;
}
- extract($this->settings[$model->alias]);
+
+ $parent = $this->settings[$model->alias]['parent'] ?? null;
+ if (isset($this->settings[$model->alias]['recursive'])) {
+ $recursive = $this->settings[$model->alias]['recursive'] ?? null;
+ }
+
if ($overrideRecursive !== null) {
$recursive = $overrideRecursive;
}
@@ -586,9 +652,8 @@ public function getParentNode(Model $model, $id = null, $fields = null, $recursi
'order' => false,
'recursive' => $recursive,
], $options);
- $parent = $model->find('first', $options);
- return $parent;
+ return $model->find('first', $options);
}
return false;
@@ -601,25 +666,29 @@ public function getParentNode(Model $model, $id = null, $fields = null, $recursi
* @param array $arg Array
* @return array Options array
*/
- protected function _getOptions($arg)
+ protected function _getOptions(array $arg): array
{
- return count(array_filter(array_keys($arg), 'is_string')) > 0 ?
- $arg :
- [];
+ return count(array_filter(array_keys($arg), 'is_string')) > 0
+ ? $arg
+ : [];
}
/**
* Get the path to the given node
*
* @param Model $model Model using this behavior
- * @param string|int|null $id The ID of the record to read
+ * @param array|string|int|null $id The ID of the record to read
* @param array|string|null $fields Either a single string of a field name, or an array of field names
* @param int|null $recursive The number of levels deep to fetch associated records
* @return array Array of nodes from top most parent to current node
* @link https://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::getPath
*/
- public function getPath(Model $model, $id = null, $fields = null, $recursive = null)
- {
+ public function getPath(
+ Model $model,
+ array|string|int|null $id = null,
+ array|string|null $fields = null,
+ int|null $recursive = null,
+ ): array {
$options = [];
if (is_array($id)) {
$options = $this->_getOptions($id);
@@ -639,7 +708,14 @@ public function getPath(Model $model, $id = null, $fields = null, $recursive = n
if (empty($id)) {
$id = $model->id;
}
- extract($this->settings[$model->alias]);
+
+ $left = $this->settings[$model->alias]['left'] ?? null;
+ $right = $this->settings[$model->alias]['right'] ?? null;
+ $scope = $this->settings[$model->alias]['scope'] ?? null;
+ if (isset($this->settings[$model->alias]['recursive'])) {
+ $recursive = $this->settings[$model->alias]['recursive'];
+ }
+
if ($overrideRecursive !== null) {
$recursive = $overrideRecursive;
}
@@ -665,9 +741,8 @@ public function getPath(Model $model, $id = null, $fields = null, $recursive = n
'order' => [$model->escapeField($left) => 'asc'],
'recursive' => $recursive,
], $options);
- $results = $model->find('all', $options);
- return $results;
+ return $model->find('all', $options);
}
/**
@@ -676,13 +751,16 @@ public function getPath(Model $model, $id = null, $fields = null, $recursive = n
* If the node is the last child, or is a top level node with no subsequent node this method will return false
*
* @param Model $model Model using this behavior
- * @param string|int|null $id The ID of the record to move
+ * @param array|string|int|null $id The ID of the record to move
* @param int|bool $number how many places to move the node or true to move to last position
* @return bool true on success, false on failure
* @link https://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::moveDown
*/
- public function moveDown(Model $model, $id = null, $number = 1)
- {
+ public function moveDown(
+ Model $model,
+ array|string|int|null $id = null,
+ int|bool $number = 1,
+ ): bool {
if (is_array($id)) {
extract(array_merge(['id' => null], $id));
}
@@ -692,7 +770,13 @@ public function moveDown(Model $model, $id = null, $number = 1)
if (empty($id)) {
$id = $model->id;
}
- extract($this->settings[$model->alias]);
+
+ $left = $this->settings[$model->alias]['left'] ?? null;
+ $right = $this->settings[$model->alias]['right'] ?? null;
+ $parent = $this->settings[$model->alias]['parent'] ?? null;
+ $scope = $this->settings[$model->alias]['scope'] ?? null;
+ $recursive = $this->settings[$model->alias]['recursive'] ?? null;
+
[$node] = array_values($this->_getNode($model, $id));
if ($node[$parent]) {
[$parentNode] = array_values($this->_getNode($model, $node[$parent]));
@@ -704,7 +788,8 @@ public function moveDown(Model $model, $id = null, $number = 1)
'conditions' => [$scope, $model->escapeField($left) => $node[$right] + 1],
'fields' => [$model->primaryKey, $left, $right],
'order' => false,
- 'recursive' => $recursive],);
+ 'recursive' => $recursive,
+ ]);
if ($nextNode) {
[$nextNode] = array_values($nextNode);
} else {
@@ -731,13 +816,16 @@ public function moveDown(Model $model, $id = null, $number = 1)
* If the node is the first child, or is a top level node with no previous node this method will return false
*
* @param Model $model Model using this behavior
- * @param string|int|null $id The ID of the record to move
+ * @param array|string|int|null $id The ID of the record to move
* @param int|bool $number how many places to move the node, or true to move to first position
* @return bool true on success, false on failure
* @link https://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::moveUp
*/
- public function moveUp(Model $model, $id = null, $number = 1)
- {
+ public function moveUp(
+ Model $model,
+ array|string|int|null $id = null,
+ int|bool $number = 1,
+ ): bool {
if (is_array($id)) {
extract(array_merge(['id' => null], $id));
}
@@ -747,7 +835,13 @@ public function moveUp(Model $model, $id = null, $number = 1)
if (empty($id)) {
$id = $model->id;
}
- extract($this->settings[$model->alias]);
+
+ $left = $this->settings[$model->alias]['left'] ?? null;
+ $right = $this->settings[$model->alias]['right'] ?? null;
+ $parent = $this->settings[$model->alias]['parent'] ?? null;
+ $scope = $this->settings[$model->alias]['scope'] ?? null;
+ $recursive = $this->settings[$model->alias]['recursive'] ?? null;
+
[$node] = array_values($this->_getNode($model, $id));
if ($node[$parent]) {
[$parentNode] = array_values($this->_getNode($model, $node[$parent]));
@@ -790,18 +884,28 @@ public function moveUp(Model $model, $id = null, $number = 1)
* parameter only applies to "parent" mode and determines what to do if the parent field contains an id that is not present.
*
* @param Model $model Model using this behavior
- * @param string $mode parent or tree
+ * @param array|string $mode parent or tree
* @param string|int|null $missingParentAction 'return' to do nothing and return, 'delete' to
* delete, or the id of the parent to set as the parent_id
* @return bool true on success, false on failure
* @link https://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::recover
*/
- public function recover(Model $model, $mode = 'parent', $missingParentAction = null)
- {
+ public function recover(
+ Model $model,
+ array|string $mode = 'parent',
+ string|int|null $missingParentAction = null,
+ ): bool {
if (is_array($mode)) {
extract(array_merge(['mode' => 'parent'], $mode));
}
+
extract($this->settings[$model->alias]);
+ $left = $this->settings[$model->alias]['left'] ?? null;
+ $right = $this->settings[$model->alias]['right'] ?? null;
+ $parent = $this->settings[$model->alias]['parent'] ?? null;
+ $scope = $this->settings[$model->alias]['scope'] ?? null;
+ $recursive = $this->settings[$model->alias]['recursive'] ?? null;
+
$model->recursive = $recursive;
if ($mode === 'parent') {
$model->bindModel(['belongsTo' => ['VerifyParent' => [
@@ -857,8 +961,11 @@ public function recover(Model $model, $mode = 'parent', $missingParentAction = n
* @param string|int|null $parentId Parent record Id
* @return int counter
*/
- protected function _recoverByParentId(Model $model, $counter = 1, $parentId = null)
- {
+ protected function _recoverByParentId(
+ Model $model,
+ int $counter = 1,
+ string|int|null $parentId = null,
+ ): int {
$params = [
'conditions' => [
$this->settings[$model->alias]['parent'] => $parentId,
@@ -939,15 +1046,32 @@ protected function _recoverByParentId(Model $model, $counter = 1, $parentId = nu
* @return bool true on success, false on failure
* @link https://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::reorder
*/
- public function reorder(Model $model, $options = [])
- {
- $options += ['id' => null, 'field' => $model->displayField, 'order' => 'ASC', 'verify' => true];
- extract($options);
+ public function reorder(
+ Model $model,
+ array $options = [],
+ ): bool {
+ $options += [
+ 'id' => null,
+ 'field' => $model->displayField,
+ 'order' => 'ASC',
+ 'verify' => true,
+ ];
+
+ $id = $options['id'] ?? null;
+ $field = $options['field'] ?? null;
+ $order = $options['order'] ?? null;
+ $verify = $options['verify'] ?? true;
+
if ($verify && !$this->verify($model)) {
return false;
}
+
$verify = false;
- extract($this->settings[$model->alias]);
+
+ $left = $this->settings[$model->alias]['left'] ?? null;
+ $right = $this->settings[$model->alias]['right'] ?? null;
+ $recursive = $this->settings[$model->alias]['recursive'] ?? null;
+
$fields = [$model->primaryKey, $field, $left, $right];
$sort = $field . ' ' . $order;
$nodes = $this->children($model, $id, true, $fields, $sort, null, null, $recursive);
@@ -975,17 +1099,25 @@ public function reorder(Model $model, $options = [])
* after the children are reparented.
*
* @param Model $model Model using this behavior
- * @param string|int|null $id The ID of the record to remove
+ * @param array|string|int|null $id The ID of the record to remove
* @param bool $delete whether to delete the node after reparenting children (if any)
- * @return bool true on success, false on failure
+ * @return array|bool true on success, false on failure
* @link https://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::removeFromTree
*/
- public function removeFromTree(Model $model, $id = null, $delete = false)
- {
+ public function removeFromTree(
+ Model $model,
+ array|string|int|null $id = null,
+ bool $delete = false,
+ ): array|bool {
if (is_array($id)) {
extract(array_merge(['id' => null], $id));
}
- extract($this->settings[$model->alias]);
+
+ $left = $this->settings[$model->alias]['left'] ?? null;
+ $right = $this->settings[$model->alias]['right'] ?? null;
+ $scope = $this->settings[$model->alias]['scope'] ?? null;
+ $parent = $this->settings[$model->alias]['parent'] ?? null;
+ $recursive = $this->settings[$model->alias]['recursive'] ?? null;
[$node] = array_values($this->_getNode($model, $id));
@@ -1041,23 +1173,32 @@ public function removeFromTree(Model $model, $id = null, $delete = false)
* Returns true if the tree is valid otherwise an array of (type, incorrect left/right index, message)
*
* @param Model $model Model using this behavior
- * @return mixed true if the tree is valid or empty, otherwise an array of (error type [index, node],
+ * @return array|bool true if the tree is valid or empty, otherwise an array of (error type [index, node],
* [incorrect left/right index,node id], message)
* @link https://book.cakephp.org/2.0/en/core-libraries/behaviors/tree.html#TreeBehavior::verify
*/
- public function verify(Model $model)
+ public function verify(Model $model): array|bool
{
- extract($this->settings[$model->alias]);
+ $left = $this->settings[$model->alias]['left'] ?? null;
+ $right = $this->settings[$model->alias]['right'] ?? null;
+ $scope = $this->settings[$model->alias]['scope'] ?? null;
+ $parent = $this->settings[$model->alias]['parent'] ?? null;
+ $recursive = $this->settings[$model->alias]['recursive'] ?? null;
+
if (!$model->find('count', ['conditions' => $scope])) {
return true;
}
+
$min = $this->_getMin($model, $scope, $left, $recursive);
$edge = $this->_getMax($model, $scope, $right, $recursive);
$errors = [];
for ($i = $min; $i <= $edge; $i++) {
$count = $model->find('count', ['conditions' => [
- $scope, 'OR' => [$model->escapeField($left) => $i, $model->escapeField($right) => $i],
+ $scope, 'OR' => [
+ $model->escapeField($left) => $i,
+ $model->escapeField($right) => $i,
+ ],
]]);
if ($count != 1) {
if (!$count) {
@@ -1105,6 +1246,7 @@ public function verify(Model $model)
$errors[] = ['node', $instance[$model->alias][$model->primaryKey], 'The parent field is blank, but has a parent'];
}
}
+
if ($errors) {
return $errors;
}
@@ -1117,10 +1259,12 @@ public function verify(Model $model)
*
* @param Model $model Model using this behavior
* @param string|int|null $id The primary key for record to get the level of.
- * @return int|bool Integer of the level or false if the node does not exist.
+ * @return int|false Integer of the level or false if the node does not exist.
*/
- public function getLevel(Model $model, $id = null)
- {
+ public function getLevel(
+ Model $model,
+ string|int|null $id = null,
+ ): int|false {
if ($id === null) {
$id = $model->id;
}
@@ -1135,7 +1279,9 @@ public function getLevel(Model $model, $id = null)
return false;
}
- extract($this->settings[$model->alias]);
+ $left = $this->settings[$model->alias]['left'] ?? null;
+ $right = $this->settings[$model->alias]['right'] ?? null;
+ $scope = $this->settings[$model->alias]['scope'] ?? null;
return $model->find('count', [
'conditions' => [
@@ -1160,9 +1306,18 @@ public function getLevel(Model $model, $id = null)
* @param bool $created True if newly created record else false.
* @return bool true on success, false on failure
*/
- protected function _setParent(Model $model, $parentId = null, $created = false)
- {
+ protected function _setParent(
+ Model $model,
+ string|int|null $parentId = null,
+ bool $created = false,
+ ): bool {
extract($this->settings[$model->alias]);
+ $left = $this->settings[$model->alias]['left'] ?? null;
+ $right = $this->settings[$model->alias]['right'] ?? null;
+ $scope = $this->settings[$model->alias]['scope'] ?? null;
+ $parent = $this->settings[$model->alias]['parent'] ?? null;
+ $recursive = $this->settings[$model->alias]['recursive'] ?? null;
+
[$node] = array_values($this->_getNode($model, $model->id));
$edge = $this->_getMax($model, $scope, $right, $recursive, $created);
@@ -1220,14 +1375,20 @@ protected function _setParent(Model $model, $parentId = null, $created = false)
* get the maximum index value in the table.
*
* @param Model $model Model Instance.
- * @param string $scope Scoping conditions.
+ * @param mixed $scope Scoping conditions.
* @param string $right Right value
* @param int $recursive Recursive find value.
* @param bool $created Whether it's a new record.
* @return int
*/
- protected function _getMax(Model $model, $scope, $right, $recursive = -1, $created = false)
- {
+ protected function _getMax(
+ Model $model,
+ mixed $scope,
+ string $right,
+ int $recursive = -1,
+ bool $created = false,
+ ): int {
+ /** @var DboSource $db */
$db = ConnectionManager::getDataSource($model->useDbConfig);
if ($created) {
if (is_string($scope)) {
@@ -1253,13 +1414,18 @@ protected function _getMax(Model $model, $scope, $right, $recursive = -1, $creat
* get the minimum index value in the table.
*
* @param Model $model Model instance.
- * @param string $scope Scoping conditions.
+ * @param mixed $scope Scoping conditions.
* @param string $left Left value.
* @param int $recursive Recurursive find value.
* @return int
*/
- protected function _getMin(Model $model, $scope, $left, $recursive = -1)
- {
+ protected function _getMin(
+ Model $model,
+ mixed $scope,
+ string $left,
+ int $recursive = -1,
+ ): int {
+ /** @var DboSource $db */
$db = ConnectionManager::getDataSource($model->useDbConfig);
$name = $model->escapeField($left);
[$edge] = array_values($model->find('first', [
@@ -1281,15 +1447,26 @@ protected function _getMin(Model $model, $scope, $left, $recursive = -1)
* @param Model $model Model instance.
* @param int $shift Shift by.
* @param string $dir Direction.
- * @param array $conditions Conditions.
+ * @param array|string $conditions Conditions.
* @param bool $created Whether it's a new record.
* @param string $field Field type.
* @return void
*/
- protected function _sync(Model $model, $shift, $dir = '+', $conditions = [], $created = false, $field = 'both')
- {
+ protected function _sync(
+ Model $model,
+ int $shift,
+ string $dir = '+',
+ array|string $conditions = [],
+ bool $created = false,
+ string $field = 'both',
+ ): void {
$ModelRecursive = $model->recursive;
- extract($this->settings[$model->alias]);
+
+ $recursive = $this->settings[$model->alias]['recursive'] ?? null;
+ $left = $this->settings[$model->alias]['left'] ?? null;
+ $right = $this->settings[$model->alias]['right'] ?? null;
+ $scope = $this->settings[$model->alias]['scope'] ?? null;
+
$model->recursive = $recursive;
if ($field === 'both') {
diff --git a/src/Model/BehaviorCollection.php b/src/Model/BehaviorCollection.php
index a4227948fd..0a772ea628 100644
--- a/src/Model/BehaviorCollection.php
+++ b/src/Model/BehaviorCollection.php
@@ -45,14 +45,14 @@ class BehaviorCollection extends ObjectCollection implements CakeEventListener
/**
* Keeps a list of all methods of attached behaviors
*
- * @var array
+ * @var array
*/
protected array $_methods = [];
/**
* Keeps a list of all methods which have been mapped with regular expressions
*
- * @var array
+ * @var array
*/
protected array $_mappedMethods = [];
@@ -82,7 +82,7 @@ public function init(string $modelName, array $behaviors = []): void
* @return bool true.
* @deprecated 3.0.0 Will be removed in 3.0. Replaced with load().
*/
- public function attach($behavior, $config = [])
+ public function attach(string $behavior, array $config = []): bool
{
return $this->load($behavior, $config);
}
@@ -102,31 +102,31 @@ public function attach($behavior, $config = [])
* ```
* All calls to the `Tree` behavior would use `AliasedTree` instead.
*
- * @param string $behavior CamelCased name of the behavior to load
- * @param array $config Behavior configuration parameters
+ * @param string $name CamelCased name of the behavior to load
+ * @param array $options Behavior configuration parameters
* @return bool True on success.
* @throws MissingBehaviorException when a behavior could not be found.
*/
- public function load($behavior, $config = [])
+ public function load(string $name, array $options = []): bool
{
- if (isset($config['className'])) {
- $alias = $behavior;
- $behavior = $config['className'];
+ if (isset($options['className'])) {
+ $alias = $name;
+ $name = $options['className'];
}
- $configDisabled = isset($config['enabled']) && $config['enabled'] === false;
- $priority = $config['priority'] ?? $this->defaultPriority;
- unset($config['enabled'], $config['className'], $config['priority']);
+ $configDisabled = isset($options['enabled']) && $options['enabled'] === false;
+ $priority = $options['priority'] ?? $this->defaultPriority;
+ unset($options['enabled'], $options['className'], $options['priority']);
- [$plugin, $name] = pluginSplit($behavior, true);
+ [$plugin, $_name] = pluginSplit($name, true);
if (!isset($alias)) {
- $alias = $name;
+ $alias = $_name;
}
- $class = App::className($behavior, 'Model/Behavior', 'Behavior');
+ $class = App::className($name, 'Model/Behavior', 'Behavior');
if (!$class) {
throw new MissingBehaviorException([
- 'class' => $name . 'Behavior',
+ 'class' => $_name . 'Behavior',
'plugin' => $plugin ? substr($plugin, 0, -1) : null,
]);
}
@@ -138,18 +138,15 @@ public function load($behavior, $config = [])
$this->_loaded[$alias] = new $class();
ClassRegistry::addObject($class, $this->_loaded[$alias]);
}
- } elseif (isset($this->_loaded[$alias]->settings) && isset($this->_loaded[$alias]->settings[$this->modelName])) {
- if ($config !== null && $config !== false) {
- $config = array_merge($this->_loaded[$alias]->settings[$this->modelName], $config);
- } else {
- $config = [];
- }
+ } elseif (isset($this->_loaded[$alias]?->settings[$this->modelName])) {
+ $options = array_merge($this->_loaded[$alias]->settings[$this->modelName], $options);
}
- if (empty($config)) {
- $config = [];
+ if (empty($options)) {
+ $options = [];
}
+
$this->_loaded[$alias]->settings['priority'] = $priority;
- $this->_loaded[$alias]->setup(ClassRegistry::getObject($this->modelName), $config);
+ $this->_loaded[$alias]->setup(ClassRegistry::getObject($this->modelName), $options);
foreach ($this->_loaded[$alias]->mapMethods as $method => $methodAlias) {
$this->_mappedMethods[$method] = [$alias, $methodAlias];
@@ -190,7 +187,7 @@ public function load($behavior, $config = [])
* @param string $name CamelCased name of the behavior to unload
* @return void
*/
- public function unload($name): void
+ public function unload(string $name): void
{
[, $name] = pluginSplit($name);
if (isset($this->_loaded[$name])) {
@@ -211,7 +208,7 @@ public function unload($name): void
* @return void
* @deprecated 3.0.0 Will be removed in 3.0. Use unload instead.
*/
- public function detach($name): void
+ public function detach(string $name): void
{
$this->unload($name);
}
@@ -226,10 +223,14 @@ public function detach($name): void
* @param string $method The method called.
* @param array $params Parameters for the called method.
* @param bool $strict If methods are not found, trigger an error.
- * @return array All methods for all behaviors attached to this object
+ * @return mixed All methods for all behaviors attached to this object
*/
- public function dispatchMethod($model, $method, $params = [], $strict = false)
- {
+ public function dispatchMethod(
+ Model $model,
+ string $method,
+ array $params = [],
+ bool $strict = false,
+ ): mixed {
$method = $this->hasMethod($method, true);
if ($strict && empty($method)) {
@@ -257,7 +258,7 @@ public function dispatchMethod($model, $method, $params = [], $strict = false)
*
* @return array All public methods for all behaviors attached to this collection
*/
- public function methods()
+ public function methods(): array
{
return $this->_methods;
}
@@ -268,10 +269,10 @@ public function methods()
*
* @param string $method The method to find.
* @param bool $callback Return the callback for the method.
- * @return mixed If $callback is false, a boolean will be returned, if its true, an array
+ * @return array|bool If $callback is false, a boolean will be returned, if its true, an array
* containing callback information will be returned. For mapped methods the array will have 3 elements.
*/
- public function hasMethod($method, $callback = false)
+ public function hasMethod(string $method, bool $callback = false): array|bool
{
if (isset($this->_methods[$method])) {
return $callback ? $this->_methods[$method] : true;
diff --git a/src/Model/CakeSchema.php b/src/Model/CakeSchema.php
index 5494b22fe4..a1ff8e0176 100644
--- a/src/Model/CakeSchema.php
+++ b/src/Model/CakeSchema.php
@@ -24,6 +24,7 @@
use Cake\Core\CakePlugin;
use Cake\Core\Configure;
use Cake\Error\CakeException;
+use Cake\Model\Datasource\DboSource;
use Cake\Utility\ClassRegistry;
use Cake\Utility\File;
use Cake\Utility\Inflector;
@@ -41,51 +42,51 @@ class CakeSchema extends CakeObject
/**
* Name of the schema.
*
- * @var string
+ * @var string|null
*/
- public $name = null;
+ public ?string $name = null;
/**
* Path to write location.
*
- * @var string
+ * @var string|null
*/
- public $path = null;
+ public ?string $path = null;
/**
* File to write.
*
* @var string
*/
- public $file = 'schema.php';
+ public string $file = 'schema.php';
/**
* Connection used for read.
*
* @var string
*/
- public $connection = 'default';
+ public string $connection = 'default';
/**
* Plugin name.
*
- * @var string
+ * @var string|null
*/
- public $plugin = null;
+ public ?string $plugin = null;
/**
* Set of tables.
*
* @var array
*/
- public $tables = [];
+ public array $tables = [];
/**
* Constructor
*
* @param array $options Optional load object properties.
*/
- public function __construct($options = [])
+ public function __construct(array $options = [])
{
parent::__construct();
@@ -96,7 +97,7 @@ public function __construct($options = [])
$this->plugin = $options['plugin'];
}
- if (strtolower($this->name) === 'cake' || static::class == CakeSchema::class) {
+ if (strtolower($this->name) === 'cake' || static::class === CakeSchema::class) {
$this->name = 'App';
}
@@ -114,7 +115,7 @@ public function __construct($options = [])
* @param array $data Loaded object properties.
* @return void
*/
- public function build($data)
+ public function build(array $data): void
{
$file = null;
foreach ($data as $key => $val) {
@@ -152,7 +153,7 @@ public function build($data)
* @param array $event Schema object properties.
* @return bool Should process continue.
*/
- public function before($event = [])
+ public function before(array $event = []): bool
{
return true;
}
@@ -163,17 +164,17 @@ public function before($event = [])
* @param array $event Schema object properties.
* @return void
*/
- public function after($event = [])
+ public function after(array $event = []): void
{
}
/**
* Reads database and creates schema tables.
*
- * @param array $options Schema object properties.
+ * @param array|string $options Schema object properties.
* @return CakeSchema|false Set of name and tables.
*/
- public function load($options = [])
+ public function load(array|string $options = []): CakeSchema|false
{
if (is_string($options)) {
$options = ['path' => $options];
@@ -190,9 +191,7 @@ public function load($options = [])
}
if (class_exists($class)) {
- $Schema = new $class($options);
-
- return $Schema;
+ return new $class($options);
}
return false;
@@ -207,11 +206,20 @@ public function load($options = [])
* - 'name' - name of the schema
* - 'models' - a list of models to use, or false to ignore models
*
- * @param array $options Schema object properties.
+ * @param array{
+ * connection?: string,
+ * name?: string|null,
+ * models?: array|bool
+ * } $options Schema object properties.
* @return array Array indexed by name and tables.
*/
- public function read($options = [])
+ public function read(array $options = []): array
{
+ /** @var array{
+ * connection: string,
+ * name: string|null,
+ * models: array|bool
+ * } $options */
$options = array_merge(
[
'connection' => $this->connection,
@@ -220,6 +228,8 @@ public function read($options = [])
],
$options,
);
+
+ /** @var DboSource $db */
$db = ConnectionManager::getDataSource($options['connection']);
if (isset($this->plugin)) {
@@ -268,14 +278,19 @@ public function read($options = [])
}
try {
- $object = ClassRegistry::init(['class' => $plugin . $model, 'ds' => $options['connection']]);
+ $object = ClassRegistry::init([
+ 'class' => $plugin . $model,
+ 'ds' => $options['connection'],
+ ]);
} catch (CakeException) {
continue;
}
- if (!is_object($object) || $object->useTable === false) {
+ if (!($object instanceof Model) || $object->useTable === false) {
continue;
}
+
+ /** @var DboSource $db */
$db = $object->getDataSource();
$fulltable = $table = $db->fullTableName($object, false, false);
@@ -301,6 +316,8 @@ public function read($options = [])
foreach ($object->hasAndBelongsToMany as $assocData) {
if (isset($assocData['with'])) {
$class = $assocData['with'];
+ } else {
+ continue;
}
if (!is_object($object->$class)) {
continue;
@@ -364,11 +381,11 @@ public function read($options = [])
/**
* Writes schema file from object or options.
*
- * @param object|array $object Schema object or options array.
+ * @param object|array|null $object Schema object or options array.
* @param array $options Schema object properties to override object.
- * @return mixed False or string written to file.
+ * @return string|false False or string written to file.
*/
- public function write($object, $options = [])
+ public function write(object|array|null $object, array $options = []): string|false
{
if (is_object($object)) {
$object = get_object_vars($object);
@@ -388,18 +405,18 @@ public function write($object, $options = [])
$out = "class {$options['name']}Schema extends CakeSchema {\n\n";
if ($options['path'] !== $this->path) {
- $out .= "\tpublic \$path = '{$options['path']}';\n\n";
+ $out .= "\tpublic ?string \$path = '{$options['path']}';\n\n";
}
if ($options['file'] !== $this->file) {
- $out .= "\tpublic \$file = '{$options['file']}';\n\n";
+ $out .= "\tpublic string \$file = '{$options['file']}';\n\n";
}
if ($options['connection'] !== 'default') {
- $out .= "\tpublic \$connection = '{$options['connection']}';\n\n";
+ $out .= "\tpublic string \$connection = '{$options['connection']}';\n\n";
}
- $out .= "\tpublic function before(\$event = array()) {\n\t\treturn true;\n\t}\n\n\tpublic function after(\$event = array()) {\n\t}\n\n";
+ $out .= "\tpublic function before(array \$event = []): bool {\n\t\treturn true;\n\t}\n\n\tpublic function after(array \$event = []): void {\n\t}\n\n";
if (empty($options['tables'])) {
$this->read();
@@ -428,48 +445,48 @@ public function write($object, $options = [])
* escaped variable declaration to be used in schema classes.
*
* @param string $table Table name you want returned.
- * @param array $fields Array of field information to generate the table with.
+ * @param array|null $fields Array of field information to generate the table with.
* @return string Variable declaration for a schema class.
* @throws Exception
*/
- public function generateTable($table, $fields)
+ public function generateTable(string $table, ?array $fields): string
{
// Valid var name regex (http://www.php.net/manual/en/language.variables.basics.php)
if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $table)) {
throw new Exception("Invalid table name '{$table}'");
}
- $out = "\tpublic \${$table} = array(\n";
+ $out = "\tpublic array \${$table} = [\n";
if (is_array($fields)) {
$cols = [];
foreach ($fields as $field => $value) {
- if ($field !== 'indexes' && $field !== 'tableParameters') {
- if (is_string($value)) {
- $type = $value;
- $value = ['type' => $type];
- }
- $value['type'] = addslashes($value['type']);
- $col = "\t\t'{$field}' => array('type' => '" . $value['type'] . "', ";
- unset($value['type']);
- $col .= implode(', ', $this->_values($value));
- } elseif ($field === 'indexes') {
- $col = "\t\t'indexes' => array(\n\t\t\t";
+ if ($field === 'indexes') {
+ $col = "\t\t'indexes' => [\n\t\t\t";
$props = [];
foreach ((array)$value as $key => $index) {
- $props[] = "'{$key}' => array(" . implode(', ', $this->_values($index)) . ')';
+ $props[] = "'{$key}' => [" . implode(', ', $this->_values($index)) . ']';
}
$col .= implode(",\n\t\t\t", $props) . "\n\t\t";
} elseif ($field === 'tableParameters') {
- $col = "\t\t'tableParameters' => array(";
+ $col = "\t\t'tableParameters' => [";
$props = $this->_values($value);
$col .= implode(', ', $props);
+ } else {
+ if (is_string($value)) {
+ $type = $value;
+ $value = ['type' => $type];
+ }
+ $value['type'] = addslashes($value['type']);
+ $col = "\t\t'{$field}' => ['type' => '" . $value['type'] . "', ";
+ unset($value['type']);
+ $col .= implode(', ', $this->_values($value));
}
- $col .= ')';
+ $col .= ']';
$cols[] = $col;
}
$out .= implode(",\n", $cols);
}
- $out .= "\n\t);\n\n";
+ $out .= "\n\t];\n\n";
return $out;
}
@@ -477,11 +494,11 @@ public function generateTable($table, $fields)
/**
* Compares two sets of schemas.
*
- * @param object|array $old Schema object or array.
- * @param object|array $new Schema object or array.
+ * @param object|array|null $old Schema object or array.
+ * @param object|array|null $new Schema object or array.
* @return array Tables (that are added, dropped, or changed.)
*/
- public function compare($old, $new = null)
+ public function compare(object|array|null $old, object|array|null $new = null): array
{
if (empty($new)) {
$new = $this;
@@ -540,8 +557,8 @@ public function compare($old, $new = null)
}
}
- if (isset($old[$table]['indexes']) && isset($new[$table]['indexes'])) {
- $diff = $this->_compareIndexes($new[$table]['indexes'], $old[$table]['indexes']);
+ if (isset($old[$table]['indexes']) && isset($fields['indexes'])) {
+ $diff = $this->_compareIndexes($fields['indexes'], $old[$table]['indexes']);
if ($diff) {
if (!isset($tables[$table])) {
$tables[$table] = [];
@@ -549,13 +566,13 @@ public function compare($old, $new = null)
if (isset($diff['drop'])) {
$tables[$table]['drop']['indexes'] = $diff['drop'];
}
- if ($diff && isset($diff['add'])) {
+ if (isset($diff['add'])) {
$tables[$table]['add']['indexes'] = $diff['add'];
}
}
}
- if (isset($old[$table]['tableParameters']) && isset($new[$table]['tableParameters'])) {
- $diff = $this->_compareTableParameters($new[$table]['tableParameters'], $old[$table]['tableParameters']);
+ if (isset($old[$table]['tableParameters']) && isset($fields['tableParameters'])) {
+ $diff = $this->_compareTableParameters($fields['tableParameters'], $old[$table]['tableParameters']);
if ($diff) {
$tables[$table]['change']['tableParameters'] = $diff;
}
@@ -578,7 +595,7 @@ public function compare($old, $new = null)
* @return array Difference as array with array(keys => values) from input array
* where match was not found.
*/
- protected function _arrayDiffAssoc($array1, $array2)
+ protected function _arrayDiffAssoc(array $array1, array $array2): array
{
$difference = [];
foreach ($array1 as $key => $value) {
@@ -610,10 +627,10 @@ protected function _arrayDiffAssoc($array1, $array2)
/**
* Formats Schema columns from Model Object.
*
- * @param array $values Options keys(type, null, default, key, length, extra).
+ * @param array|null $values Options keys(type, null, default, key, length, extra).
* @return array Formatted values.
*/
- protected function _values($values)
+ protected function _values(array|null $values): array
{
$vals = [];
if (is_array($values)) {
@@ -640,10 +657,10 @@ protected function _values($values)
/**
* Formats Schema columns from Model Object.
*
- * @param Model &$model model object.
+ * @param Model $model model object.
* @return array Formatted columns.
*/
- protected function _columns(&$model)
+ protected function _columns(Model $model): array
{
$db = $model->getDataSource();
$fields = $model->schema(true);
@@ -694,28 +711,27 @@ protected function _columns(&$model)
/**
* Compare two schema files table Parameters.
*
- * @param array $new New indexes.
- * @param array $old Old indexes.
- * @return mixed False on failure, or an array of parameters to add & drop.
+ * @param array|null $new New indexes.
+ * @param array|null $old Old indexes.
+ * @return array|false False on failure, or an array of parameters to add & drop.
*/
- protected function _compareTableParameters($new, $old)
+ protected function _compareTableParameters(?array $new, ?array $old): array|false
{
if (!is_array($new) || !is_array($old)) {
return false;
}
- $change = $this->_arrayDiffAssoc($new, $old);
- return $change;
+ return $this->_arrayDiffAssoc($new, $old);
}
/**
* Compare two schema indexes.
*
- * @param array $new New indexes.
- * @param array $old Old indexes.
- * @return mixed False on failure or array of indexes to add and drop.
+ * @param array|null $new New indexes.
+ * @param array|null $old Old indexes.
+ * @return array|false False on failure or array of indexes to add and drop.
*/
- protected function _compareIndexes($new, $old)
+ protected function _compareIndexes(?array $new, ?array $old): array|false
{
if (!is_array($new) || !is_array($old)) {
return false;
@@ -740,8 +756,6 @@ protected function _compareIndexes($new, $old)
$newColumn = $value['column'];
$oldColumn = $old[$name]['column'];
- $diff = false;
-
if ($newUnique != $oldUnique) {
$diff = true;
} elseif (is_array($newColumn) && is_array($oldColumn)) {
@@ -769,9 +783,9 @@ protected function _compareIndexes($new, $old)
* @param string $table Full table name.
* @return string Prefix-less table name.
*/
- protected function _noPrefixTable($prefix, $table)
+ protected function _noPrefixTable(string $prefix, string $table): string
{
- return preg_replace('/^' . preg_quote($prefix) . '/', '', $table);
+ return preg_replace('/^' . preg_quote($prefix, '/') . '/', '', $table);
}
/**
@@ -781,7 +795,7 @@ protected function _noPrefixTable($prefix, $table)
* @param string $file Filesystem basename of the file.
* @return bool True when a file was successfully included, false on failure.
*/
- protected function _requireFile($path, $file)
+ protected function _requireFile(string $path, string $file): bool
{
if (file_exists($path . DS . $file) && is_file($path . DS . $file)) {
require_once $path . DS . $file;
diff --git a/src/Model/ConnectionManager.php b/src/Model/ConnectionManager.php
index 010588e5a6..220cb02887 100644
--- a/src/Model/ConnectionManager.php
+++ b/src/Model/ConnectionManager.php
@@ -41,41 +41,41 @@ class ConnectionManager
/**
* Holds a loaded instance of the Connections object
*
- * @var DATABASE_CONFIG
+ * @var DATABASE_CONFIG|null
*/
- public static $config = null;
+ public static ?DATABASE_CONFIG $config = null;
/**
* Holds instances DataSource objects
*
* @var array
*/
- protected static $_dataSources = [];
+ protected static array $_dataSources = [];
/**
* Contains a list of all file and class names used in Connection settings
*
* @var array
*/
- protected static $_connectionsEnum = [];
+ protected static array $_connectionsEnum = [];
/**
* Indicates if the init code for this class has already been executed
*
* @var bool
*/
- protected static $_init = false;
+ protected static bool $_init = false;
/**
* Loads connections configuration.
*
* @return void
*/
- protected static function _init()
+ protected static function _init(): void
{
try {
require_once CONFIG . 'database.php';
- } catch (Throwable $e) {
+ } catch (Throwable) {
}
if (class_exists(DATABASE_CONFIG::class)) {
static::$config = new DATABASE_CONFIG();
@@ -87,7 +87,7 @@ protected static function _init()
* Gets a reference to a DataSource object
*
* @param string $name The name of the DataSource, as defined in app/Config/database.php
- * @return DataSource|DboSource Instance
+ * @return DataSource Instance
* @throws MissingDatasourceException
*/
public static function getDataSource(string $name): DataSource
@@ -108,7 +108,7 @@ public static function getDataSource(string $name): DataSource
$conn = static::$_connectionsEnum[$name];
$class = $conn['classname'];
- if (!class_exists($class) && !str_contains(App::location($class), 'Datasource')) {
+ if (!class_exists($class)) {
throw new MissingDatasourceException([
'class' => $class,
'plugin' => null,
@@ -121,6 +121,31 @@ public static function getDataSource(string $name): DataSource
return static::$_dataSources[$name];
}
+ /**
+ * Gets a reference to a DataSource object
+ *
+ * @param string $name The name of the DataSource, as defined in app/Config/database.php
+ * @return DboSource Instance
+ * @throws MissingDatasourceException
+ */
+ public static function getDboSource(string $name): DboSource
+ {
+ $dataSource = static::getDataSource($name);
+
+ if (!$dataSource instanceof DboSource) {
+ $conn = static::$_connectionsEnum[$name];
+ $class = $conn['classname'];
+
+ throw new MissingDatasourceException([
+ 'class' => $class,
+ 'plugin' => null,
+ 'message' => 'DboSource is not found in Model/Datasource package.',
+ ]);
+ }
+
+ return $dataSource;
+ }
+
/**
* Gets the list of available DataSource connections
* This will only return the datasources instantiated by this manager
@@ -128,7 +153,7 @@ public static function getDataSource(string $name): DataSource
*
* @return array List of available connections
*/
- public static function sourceList()
+ public static function sourceList(): array
{
if (empty(static::$_init)) {
static::_init();
@@ -140,11 +165,11 @@ public static function sourceList()
/**
* Gets a DataSource name from an object reference.
*
- * @param DataSource $source DataSource object
+ * @param object $source DataSource object
* @return string|null Datasource name, or null if source is not present
* in the ConnectionManager.
*/
- public static function getSourceName($source)
+ public static function getSourceName(object $source): ?string
{
if (empty(static::$_init)) {
static::_init();
@@ -214,7 +239,7 @@ public static function loadDataSource(array|string $connName): bool
* @return array An associative array of elements where the key is the connection name
* (as defined in Connections), and the value is an array with keys 'filename' and 'classname'.
*/
- public static function enumConnectionObjects()
+ public static function enumConnectionObjects(): array
{
if (empty(static::$_init)) {
static::_init();
@@ -226,11 +251,11 @@ public static function enumConnectionObjects()
/**
* Dynamically creates a DataSource object at runtime, with the given name and settings
*
- * @param string $name The DataSource name
- * @param array $config The DataSource configuration settings
+ * @param string|null $name The DataSource name
+ * @param array|null $config The DataSource configuration settings
* @return DataSource|null A reference to the DataSource object, or null if creation failed
*/
- public static function create($name = '', $config = [])
+ public static function create(?string $name = '', ?array $config = []): ?DataSource
{
if (empty(static::$_init)) {
static::_init();
@@ -241,9 +266,8 @@ public static function create($name = '', $config = [])
}
static::$config->{$name} = $config;
static::$_connectionsEnum[$name] = static::_connectionData($config);
- $return = static::getDataSource($name);
- return $return;
+ return static::getDataSource($name);
}
/**
@@ -252,7 +276,7 @@ public static function create($name = '', $config = [])
* @param string $name the connection name as it was created
* @return bool success if connection was removed, false if it does not exist
*/
- public static function drop($name)
+ public static function drop(string $name): bool
{
if (empty(static::$_init)) {
static::_init();
@@ -273,7 +297,7 @@ public static function drop($name)
* @return void
* @throws MissingDatasourceConfigException
*/
- protected static function _getConnectionObject($name)
+ protected static function _getConnectionObject(string $name): void
{
if (!empty(static::$config->{$name})) {
static::$_connectionsEnum[$name] = static::_connectionData(static::$config->{$name});
@@ -290,7 +314,7 @@ protected static function _getConnectionObject($name)
*/
protected static function _connectionData(array $config): array
{
- $package = $classname = $plugin = null;
+ $package = null;
[$plugin, $classname] = pluginSplit($config['datasource']);
if (str_contains($classname, '/')) {
diff --git a/src/Model/Datasource/CakeSession.php b/src/Model/Datasource/CakeSession.php
index cda77da7bd..996efecb98 100644
--- a/src/Model/Datasource/CakeSession.php
+++ b/src/Model/Datasource/CakeSession.php
@@ -44,77 +44,77 @@ class CakeSession
*
* @var bool
*/
- public static $valid = false;
+ public static bool $valid = false;
/**
* Error messages for this session
*
- * @var array
+ * @var array|false
*/
- public static $error = false;
+ public static array|false $error = false;
/**
* User agent string
*
* @var string
*/
- protected static $_userAgent = '';
+ protected static string $_userAgent = '';
/**
* Path to where the session is active.
*
* @var string
*/
- public static $path = '/';
+ public static string $path = '/';
/**
* Error number of last occurred error
*
- * @var int
+ * @var int|null
*/
- public static $lastError = null;
+ public static ?int $lastError = null;
/**
* Start time for this session.
*
- * @var int
+ * @var int|false
*/
- public static $time = false;
+ public static int|false $time = false;
/**
* Cookie lifetime
*
* @var int
*/
- public static $cookieLifeTime;
+ public static int $cookieLifeTime;
/**
* Time when this session becomes invalid.
*
- * @var int
+ * @var int|false
*/
- public static $sessionTime = false;
+ public static int|false $sessionTime = false;
/**
* Current Session id
*
- * @var string
+ * @var string|null
*/
- public static $id = null;
+ public static ?string $id = null;
/**
* Hostname
*
- * @var string
+ * @var string|null
*/
- public static $host = null;
+ public static ?string $host = null;
/**
* Session timeout multiplier factor
*
- * @var int
+ * @var int|null
*/
- public static $timeout = null;
+ public static ?int $timeout = null;
/**
* Number of requests that can occur during a session time without the session being renewed.
@@ -123,35 +123,35 @@ class CakeSession
* @var int
* @see CakeSession::_checkValid()
*/
- public static $requestCountdown = 10;
+ public static int $requestCountdown = 10;
/**
* Whether or not the init function in this class was already called
*
* @var bool
*/
- protected static $_initialized = false;
+ protected static bool $_initialized = false;
/**
* Session cookie name
*
- * @var string
+ * @var string|null
*/
- protected static $_cookieName = null;
+ protected static ?string $_cookieName = null;
/**
* Whether or not to make `_validAgentAndTime` 3.x compatible.
*
* @var bool
*/
- protected static $_useForwardsCompatibleTimeout = false;
+ protected static bool $_useForwardsCompatibleTimeout = false;
/**
* Whether this session is running under a CLI environment
*
* @var bool
*/
- protected static $_isCLI = false;
+ protected static bool $_isCLI = false;
/**
* Pseudo constructor.
@@ -159,7 +159,7 @@ class CakeSession
* @param string|null $base The base path for the Session
* @return void
*/
- public static function init($base = null)
+ public static function init(?string $base = null): void
{
static::$time = time();
@@ -184,7 +184,7 @@ public static function init($base = null)
* @param string|null $base base path
* @return void
*/
- protected static function _setPath($base = null)
+ protected static function _setPath(?string $base = null): void
{
if (empty($base)) {
static::$path = '/';
@@ -203,10 +203,10 @@ protected static function _setPath($base = null)
/**
* Set the host name
*
- * @param string $host Hostname
+ * @param string|null $host Hostname
* @return void
*/
- protected static function _setHost($host)
+ protected static function _setHost(?string $host): void
{
static::$host = $host;
if (str_contains(static::$host ?? '', ':')) {
@@ -219,7 +219,7 @@ protected static function _setHost($host)
*
* @return bool True if session was started
*/
- public static function start()
+ public static function start(): bool
{
if (static::started()) {
return true;
@@ -227,7 +227,7 @@ public static function start()
$id = static::id();
static::_startSession();
- if (!$id && static::started()) {
+ if (!$id && static::started()) { // @phpstan-ignore-line
static::_checkValid();
}
@@ -242,7 +242,7 @@ public static function start()
*
* @return bool True if session has been started.
*/
- public static function started()
+ public static function started(): bool
{
if (function_exists('session_status')) {
return isset($_SESSION) && (session_status() === PHP_SESSION_ACTIVE);
@@ -254,10 +254,10 @@ public static function started()
/**
* Returns true if given variable is set in session.
*
- * @param string $name Variable name to check for
+ * @param string|null $name Variable name to check for
* @return bool True if variable is there
*/
- public static function check($name)
+ public static function check(?string $name): bool
{
if (!static::_hasSession() || !static::start()) {
return false;
@@ -281,9 +281,9 @@ public static function check($name)
* characters in the range a-z A-Z 0-9 , (comma) and - (minus).
*
* @param string|null $id Id to replace the current session id
- * @return string Session id
+ * @return string|null Session id
*/
- public static function id($id = null)
+ public static function id(?string $id = null): ?string
{
if ($id) {
static::$id = $id;
@@ -299,10 +299,10 @@ public static function id($id = null)
/**
* Removes a variable from session.
*
- * @param string $name Session variable to remove
+ * @param string|null $name Session variable to remove
* @return bool Success
*/
- public static function delete($name)
+ public static function delete(?string $name): bool
{
if (static::check($name)) {
static::_overwrite($_SESSION, Hash::remove($_SESSION, $name));
@@ -320,7 +320,7 @@ public static function delete($name)
* @param array $new New set of variable => value
* @return void
*/
- protected static function _overwrite(&$old, $new)
+ protected static function _overwrite(array &$old, array $new): void
{
if (!empty($old)) {
foreach ($old as $key => $var) {
@@ -338,9 +338,9 @@ protected static function _overwrite(&$old, $new)
* Return error description for given error number.
*
* @param int $errorNumber Error to set
- * @return string Error as string
+ * @return string|false Error as string
*/
- protected static function _error($errorNumber)
+ protected static function _error(int $errorNumber): string|false
{
if (!is_array(static::$error) || !array_key_exists($errorNumber, static::$error)) {
return false;
@@ -352,9 +352,9 @@ protected static function _error($errorNumber)
/**
* Returns last occurred error as a string, if any.
*
- * @return mixed Error description as a string, or false.
+ * @return string|false Error description as a string, or false.
*/
- public static function error()
+ public static function error(): string|false
{
if (static::$lastError) {
return static::_error(static::$lastError);
@@ -368,7 +368,7 @@ public static function error()
*
* @return bool Success
*/
- public static function valid()
+ public static function valid(): bool
{
if (static::start() && static::read('Config')) {
if (static::_validAgentAndTime() && static::$error === false) {
@@ -390,7 +390,7 @@ public static function valid()
*
* @return bool
*/
- protected static function _validAgentAndTime()
+ protected static function _validAgentAndTime(): bool
{
$userAgent = static::read('Config.userAgent');
$time = static::read('Config.time');
@@ -411,7 +411,7 @@ protected static function _validAgentAndTime()
* @param string|null $userAgent Set the user agent
* @return string Current user agent.
*/
- public static function userAgent($userAgent = null)
+ public static function userAgent(?string $userAgent = null): string
{
if ($userAgent) {
static::$_userAgent = $userAgent;
@@ -430,7 +430,7 @@ public static function userAgent($userAgent = null)
* @return mixed The value of the session variable, null if session not available,
* session not started, or provided name not found in the session, false on failure.
*/
- public static function read($name = null)
+ public static function read(?string $name = null): mixed
{
if (!static::_hasSession() || !static::start()) {
return null;
@@ -446,9 +446,9 @@ public static function read($name = null)
/**
* Returns all session variables.
*
- * @return mixed Full $_SESSION array, or false on error.
+ * @return array|false Full $_SESSION array, or false on error.
*/
- protected static function _returnSessionVars()
+ protected static function _returnSessionVars(): array|false
{
if (!empty($_SESSION)) {
return $_SESSION;
@@ -465,7 +465,7 @@ protected static function _returnSessionVars()
* @param mixed $value Value to write
* @return bool True if the write was successful, false if the write failed
*/
- public static function write($name, $value = null)
+ public static function write(array|string $name, mixed $value = null): bool
{
if (!static::start()) {
return false;
@@ -488,11 +488,11 @@ public static function write($name, $value = null)
/**
* Reads and deletes a variable from session.
*
- * @param string $name The key to read and remove (or a path as sent to Hash.extract).
+ * @param string|null $name The key to read and remove (or a path as sent to Hash.extract).
* @return mixed The value of the session variable, null if session not available,
* session not started, or provided name not found in the session.
*/
- public static function consume($name)
+ public static function consume(?string $name): mixed
{
if (empty($name)) {
return null;
@@ -510,7 +510,7 @@ public static function consume($name)
*
* @return void
*/
- public static function destroy()
+ public static function destroy(): void
{
if (!static::started()) {
static::_startSession();
@@ -538,7 +538,7 @@ public static function destroy()
* @param bool $renew If the session should also be renewed. Defaults to true.
* @return void
*/
- public static function clear($renew = true)
+ public static function clear(bool $renew = true): void
{
if (!$renew) {
$_SESSION = [];
@@ -559,7 +559,7 @@ public static function clear($renew = true)
* @return void
* @throws CakeSessionException Throws exceptions when ini_set() fails.
*/
- protected static function _configureSession()
+ protected static function _configureSession(): void
{
$sessionConfig = Configure::read('Session');
@@ -643,7 +643,7 @@ protected static function _configureSession()
*
* @return string
*/
- protected static function _cookieName()
+ protected static function _cookieName(): string
{
if (static::$_cookieName !== null) {
return static::$_cookieName;
@@ -660,7 +660,7 @@ protected static function _cookieName()
*
* @return bool
*/
- protected static function _hasSession()
+ protected static function _hasSession(): bool
{
return static::started()
|| !ini_get('session.use_cookies')
@@ -676,7 +676,7 @@ protected static function _hasSession()
* @return CakeSessionHandlerInterface
* @throws CakeSessionException
*/
- protected static function _getHandler($handler)
+ protected static function _getHandler(string $handler): CakeSessionHandlerInterface
{
$className = App::className($handler, 'Model/Datasource/Session');
if (!$className) {
@@ -687,6 +687,7 @@ protected static function _getHandler($handler)
if ($handler instanceof CakeSessionHandlerInterface) {
return $handler;
}
+
throw new CakeSessionException(__d('cake_dev', 'Chosen SessionHandler does not implement CakeSessionHandlerInterface it cannot be used with an engine key.'));
}
@@ -694,9 +695,9 @@ protected static function _getHandler($handler)
* Get one of the prebaked default session configurations.
*
* @param string $name Config name.
- * @return array|bool
+ * @return array|false
*/
- protected static function _defaultConfig($name)
+ protected static function _defaultConfig(string $name): array|false
{
$defaults = [
'php' => [
@@ -761,7 +762,7 @@ protected static function _defaultConfig($name)
*
* @return bool Success
*/
- protected static function _startSession()
+ protected static function _startSession(): bool
{
static::init();
session_write_close();
@@ -787,7 +788,7 @@ protected static function _startSession()
*
* @return void
*/
- protected static function _checkValid()
+ protected static function _checkValid(): void
{
$config = static::read('Config');
if ($config) {
@@ -822,7 +823,7 @@ protected static function _checkValid()
*
* @return void
*/
- protected static function _writeConfig()
+ protected static function _writeConfig(): void
{
static::write('Config.userAgent', static::$_userAgent);
static::write('Config.time', static::$sessionTime);
@@ -834,7 +835,7 @@ protected static function _writeConfig()
*
* @return void
*/
- public static function renew()
+ public static function renew(): void
{
if (session_id() === '') {
return;
@@ -856,7 +857,7 @@ public static function renew()
* @param string $errorMessage Description of the error
* @return void
*/
- protected static function _setError($errorNumber, $errorMessage)
+ protected static function _setError(int $errorNumber, string $errorMessage): void
{
if (static::$error === false) {
static::$error = [];
diff --git a/src/Model/Datasource/DataSource.php b/src/Model/Datasource/DataSource.php
index 0f027483dc..f6c06991ef 100644
--- a/src/Model/Datasource/DataSource.php
+++ b/src/Model/Datasource/DataSource.php
@@ -83,12 +83,17 @@ class DataSource extends CakeObject
*/
public bool $cacheSources = true;
+ /**
+ * @var array
+ */
+ public array $columns = [];
+
/**
* Constructor.
*
* @param array $config Array of configuration information for the datasource.
*/
- public function __construct($config = [])
+ public function __construct(array $config = [])
{
parent::__construct();
$this->setConfig($config);
@@ -126,9 +131,9 @@ public function listSources(?array $data = null): ?array
* Returns a Model description (metadata) or null if none found.
*
* @param Model|string $model The model to describe.
- * @return array|null Array of Metadata for the $model
+ * @return array|false|null Array of Metadata for the $model
*/
- public function describe(Model|string $model)
+ public function describe(Model|string $model): array|false|null
{
if ($this->cacheSources === false) {
return null;
@@ -158,7 +163,7 @@ public function describe(Model|string $model)
*
* @return bool Returns true if a transaction is not in progress
*/
- public function begin()
+ public function begin(): bool
{
return !$this->_transactionStarted;
}
@@ -168,7 +173,7 @@ public function begin()
*
* @return bool Returns true if a transaction is in progress
*/
- public function commit()
+ public function commit(): bool
{
return $this->_transactionStarted;
}
@@ -178,7 +183,7 @@ public function commit()
*
* @return bool Returns true if a transaction is in progress
*/
- public function rollback()
+ public function rollback(): bool
{
return $this->_transactionStarted;
}
@@ -186,10 +191,10 @@ public function rollback()
/**
* Converts column types to basic types
*
- * @param string $real Real column type (i.e. "varchar(255)")
- * @return string Abstract column type (i.e. "string")
+ * @param mixed $real Real column type (i.e. "varchar(255)")
+ * @return string|false Abstract column type (i.e. "string")
*/
- public function column($real)
+ public function column(mixed $real): string|false
{
return false;
}
@@ -200,12 +205,15 @@ public function column($real)
* To-be-overridden in subclasses.
*
* @param Model $model The Model to be created.
- * @param array $fields An Array of fields to be saved.
- * @param array $values An Array of values to save.
+ * @param array|null $fields An Array of fields to be saved.
+ * @param array|null $values An Array of values to save.
* @return bool success
*/
- public function create(Model $model, $fields = null, $values = null)
- {
+ public function create(
+ Model $model,
+ ?array $fields = null,
+ ?array $values = null,
+ ): bool {
return false;
}
@@ -216,11 +224,14 @@ public function create(Model $model, $fields = null, $values = null)
*
* @param Model $model The model being read.
* @param array $queryData An array of query data used to find the data you want
- * @param int $recursive Number of levels of association
- * @return mixed
+ * @param int|null $recursive Number of levels of association
+ * @return array|false
*/
- public function read(Model $model, $queryData = [], $recursive = null)
- {
+ public function read(
+ Model $model,
+ array $queryData = [],
+ ?int $recursive = null,
+ ): array|false {
return false;
}
@@ -230,13 +241,17 @@ public function read(Model $model, $queryData = [], $recursive = null)
* To-be-overridden in subclasses.
*
* @param Model $model Instance of the model class being updated
- * @param array $fields Array of fields to be updated
- * @param array $values Array of values to be update $fields to.
+ * @param array|null $fields Array of fields to be updated
+ * @param array|null $values Array of values to be update $fields to.
* @param mixed $conditions The array of conditions to use.
* @return bool Success
*/
- public function update(Model $model, $fields = null, $values = null, $conditions = null)
- {
+ public function update(
+ Model $model,
+ ?array $fields = null,
+ ?array $values = null,
+ mixed $conditions = null,
+ ): bool {
return false;
}
@@ -249,8 +264,10 @@ public function update(Model $model, $fields = null, $values = null, $conditions
* @param mixed $conditions The conditions to use for deleting.
* @return bool Success
*/
- public function delete(Model $model, $conditions = null)
- {
+ public function delete(
+ Model $model,
+ mixed $conditions = null,
+ ): bool {
return false;
}
@@ -260,7 +277,7 @@ public function delete(Model $model, $conditions = null)
* @param mixed $source The source name.
* @return mixed Last ID key generated in previous INSERT
*/
- public function lastInsertId($source = null)
+ public function lastInsertId(mixed $source = null): mixed
{
return false;
}
@@ -269,9 +286,9 @@ public function lastInsertId($source = null)
* Returns the number of rows returned by last operation.
*
* @param mixed $source The source name.
- * @return int Number of rows returned by last operation
+ * @return int|false Number of rows returned by last operation
*/
- public function lastNumRows($source = null)
+ public function lastNumRows(mixed $source = null): int|false
{
return false;
}
@@ -280,9 +297,9 @@ public function lastNumRows($source = null)
* Returns the number of rows affected by last query.
*
* @param mixed $source The source name.
- * @return int Number of rows affected by last query.
+ * @return int|false Number of rows affected by last query.
*/
- public function lastAffected($source = null)
+ public function lastAffected(mixed $source = null): int|false
{
return false;
}
@@ -294,7 +311,7 @@ public function lastAffected($source = null)
*
* @return bool Whether or not the Datasources conditions for use are met.
*/
- public function enabled()
+ public function enabled(): bool
{
return true;
}
@@ -306,7 +323,7 @@ public function enabled()
* @param array $config The configuration array
* @return void
*/
- public function setConfig($config = [])
+ public function setConfig(array $config = []): void
{
$this->config = array_merge($this->_baseConfig, $this->config, $config);
}
@@ -318,7 +335,7 @@ public function setConfig($config = [])
* @param mixed $data The description of the model, usually a string or array
* @return mixed
*/
- protected function _cacheDescription($object, $data = null)
+ protected function _cacheDescription(string $object, mixed $data = null): mixed
{
if ($this->cacheSources === false) {
return null;
@@ -347,10 +364,15 @@ protected function _cacheDescription($object, $data = null)
* @param string $association Name of association model being replaced.
* @param Model $model Model instance.
* @param array $stack The context stack.
- * @return mixed String of query data with placeholders replaced, or false on failure.
+ * @return array|string|bool String of query data with placeholders replaced, or false on failure.
*/
- public function insertQueryData($query, $data, $association, Model $model, $stack)
- {
+ public function insertQueryData(
+ string $query,
+ array $data,
+ string $association,
+ Model $model,
+ array $stack,
+ ): array|string|bool {
$keys = ['{$__cakeID__$}', '{$__cakeForeignKey__$}'];
$modelAlias = $model->alias;
@@ -395,7 +417,7 @@ public function insertQueryData($query, $data, $association, Model $model, $stac
} else {
$found = false;
foreach (array_reverse($stack) as $assocData) {
- if (is_string($assocData) && isset($data[$assocData]) && isset($data[$assocData][$insertKey])) {
+ if (is_string($assocData) && isset($data[$assocData][$insertKey])) {
$val = $data[$assocData][$insertKey];
$found = true;
break;
@@ -428,7 +450,7 @@ public function insertQueryData($query, $data, $association, Model $model, $stac
* @param bool $null Column allows NULL values
* @return array|string Quoted and escaped data
*/
- public function value($data, ?string $column = null, bool $null = true): array|string
+ public function value(mixed $data, ?string $column = null, bool $null = true): array|string
{
return '';
}
@@ -440,8 +462,10 @@ public function value($data, ?string $column = null, bool $null = true): array|s
* @param string $key Key name to make
* @return string Key name for model.
*/
- public function resolveKey(Model $model, $key)
- {
+ public function resolveKey(
+ Model $model,
+ string $key,
+ ): string {
return $model->alias . $key;
}
@@ -450,7 +474,7 @@ public function resolveKey(Model $model, $key)
*
* @return string|null The schema name
*/
- public function getSchemaName()
+ public function getSchemaName(): ?string
{
return null;
}
@@ -458,11 +482,11 @@ public function getSchemaName()
/**
* Closes a connection. Override in subclasses.
*
- * @return bool
+ * @return void
*/
- public function close()
+ public function close(): void
{
- return $this->connected = false;
+ $this->connected = false;
}
/**
diff --git a/src/Model/Datasource/Database/Mysql.php b/src/Model/Datasource/Database/Mysql.php
index 3e96b59b43..06c02641b2 100644
--- a/src/Model/Datasource/Database/Mysql.php
+++ b/src/Model/Datasource/Database/Mysql.php
@@ -162,7 +162,7 @@ class Mysql extends DboSource
*
* @var array
*/
- protected $_charsets = [];
+ protected array $_charsets = [];
/**
* Server type.
@@ -189,7 +189,7 @@ class Mysql extends DboSource
* @return bool True if the database could be connected, else false
* @throws MissingConnectionException
*/
- public function connect()
+ public function connect(): bool
{
$config = $this->config;
$this->connected = false;
@@ -246,7 +246,7 @@ public function connect()
*
* @return bool
*/
- public function enabled()
+ public function enabled(): bool
{
return in_array('mysql', PDO::getAvailableDrivers());
}
@@ -254,8 +254,8 @@ public function enabled()
/**
* Returns an array of sources (tables) in the database.
*
- * @param mixed $data List of tables.
- * @return array Array of table names in the database
+ * @param array|null $data List of tables.
+ * @return array|null Array of table names in the database
*/
public function listSources(?array $data = null): ?array
{
@@ -266,8 +266,6 @@ public function listSources(?array $data = null): ?array
$result = $this->_execute('SHOW TABLES FROM ' . $this->name($this->config['database']));
if (!$result) {
- $result->closeCursor();
-
return [];
}
$tables = [];
@@ -288,7 +286,7 @@ public function listSources(?array $data = null): ?array
* @param PDOStatement $results The results to format.
* @return void
*/
- public function resultSet($results)
+ public function resultSet(PDOStatement $results): void
{
$this->map = [];
$numFields = $results->columnCount();
@@ -312,9 +310,9 @@ public function resultSet($results)
/**
* Fetches the next row from the current result set
*
- * @return mixed array with results fetched and mapped to column names or false if there is no results left to fetch
+ * @return array|false array with results fetched and mapped to column names or false if there is no results left to fetch
*/
- public function fetchResult()
+ public function fetchResult(): array|false
{
if ($row = $this->_result->fetch(PDO::FETCH_NUM)) {
$resultRow = [];
@@ -338,7 +336,7 @@ public function fetchResult()
*
* @return string The database encoding
*/
- public function getEncoding()
+ public function getEncoding(): string
{
return $this->_execute('SHOW VARIABLES LIKE ?', ['character_set_client'])->fetchObject()->Value;
}
@@ -349,9 +347,9 @@ public function getEncoding()
* @param string $name Collation name
* @return string|false Character set name
*/
- public function getCharsetName($name)
+ public function getCharsetName(string $name): string|false
{
- if ((bool)version_compare($this->getVersion(), '5', '<')) {
+ if (version_compare($this->getVersion(), '5', '<')) {
return false;
}
if (isset($this->_charsets[$name])) {
@@ -376,10 +374,10 @@ public function getCharsetName($name)
* Returns an array of the fields in given table name.
*
* @param Model|string $model Name of database table to inspect or model instance
- * @return array|bool Fields in table. Keys are name and type. Returns false if result is empty.
+ * @return array|false|null Fields in table. Keys are name and type. Returns false if result is empty.
* @throws CakeException
*/
- public function describe(string|Model $model)
+ public function describe(Model|string $model): array|false|null
{
$key = $this->fullTableName($model, false);
$cache = parent::describe($key);
@@ -397,7 +395,7 @@ public function describe(string|Model $model)
while ($column = $cols->fetch(PDO::FETCH_OBJ)) {
$fields[$column->Field] = [
'type' => $this->column($column->Type),
- 'null' => ($column->Null === 'YES' ? true : false),
+ 'null' => $column->Null === 'YES',
'default' => $column->Default,
'length' => $this->length($column->Type),
];
@@ -441,13 +439,17 @@ public function describe(string|Model $model)
* Generates and executes an SQL UPDATE statement for given model, fields, and values.
*
* @param Model $model The model to update.
- * @param array $fields The fields to update.
- * @param array $values The values to set.
+ * @param array|null $fields The fields to update.
+ * @param array|null $values The values to set.
* @param mixed $conditions The conditions to use.
* @return bool
*/
- public function update(Model $model, $fields = [], $values = null, $conditions = null)
- {
+ public function update(
+ Model $model,
+ ?array $fields = [],
+ ?array $values = null,
+ mixed $conditions = null,
+ ): bool {
if (!$this->_useAlias) {
return parent::update($model, $fields, $values, $conditions);
}
@@ -470,7 +472,6 @@ public function update(Model $model, $fields = [], $values = null, $conditions =
}
}
$conditions = $this->conditions($this->defaultConditions($model, $conditions, $alias), true, true, $model);
-
if ($conditions === false) {
return false;
}
@@ -491,8 +492,10 @@ public function update(Model $model, $fields = [], $values = null, $conditions =
* @param mixed $conditions The conditions to use.
* @return bool Success
*/
- public function delete(Model $model, $conditions = null)
- {
+ public function delete(
+ Model $model,
+ mixed $conditions = null,
+ ): bool {
if (!$this->_useAlias) {
return parent::delete($model, $conditions);
}
@@ -509,9 +512,6 @@ public function delete(Model $model, $conditions = null)
}
$conditions = $this->conditions($this->defaultConditions($model, $conditions, $alias), true, true, $model);
- if ($conditions === false) {
- return false;
- }
if ($this->execute($this->renderStatement('delete', compact('alias', 'table', 'joins', 'conditions'))) === false) {
$model->onError();
@@ -528,7 +528,7 @@ public function delete(Model $model, $conditions = null)
* @param mixed $conditions The conditions to use.
* @return bool Whether or not complex conditions are needed
*/
- protected function _deleteNeedsComplexConditions(Model $model, $conditions)
+ protected function _deleteNeedsComplexConditions(Model $model, mixed $conditions): bool
{
$fields = array_keys($this->describe($model));
foreach ((array)$conditions as $key => $value) {
@@ -550,7 +550,7 @@ protected function _deleteNeedsComplexConditions(Model $model, $conditions)
* @param string $enc Database encoding
* @return bool
*/
- public function setEncoding($enc)
+ public function setEncoding(string $enc): bool
{
return $this->_execute('SET NAMES ' . $enc) !== false;
}
@@ -561,14 +561,14 @@ public function setEncoding($enc)
* @param Model|string $model Name of model to inspect
* @return array Fields in table. Keys are column and unique
*/
- public function index($model)
+ public function index(Model|string $model): array
{
$index = [];
$table = $this->fullTableName($model);
$old = version_compare($this->getVersion(), '4.1', '<=');
if ($table) {
$indexes = $this->_execute('SHOW INDEX FROM ' . $table);
- // @codingStandardsIgnoreStart
+
// MySQL columns don't match the cakephp conventions.
while ($idx = $indexes->fetch(PDO::FETCH_OBJ)) {
if ($old) {
@@ -597,7 +597,7 @@ public function index($model)
$index[$idx->Key_name]['length'][$idx->Column_name] = $idx->Sub_part;
}
}
- // @codingStandardsIgnoreEnd
+
$indexes->closeCursor();
}
@@ -607,17 +607,16 @@ public function index($model)
/**
* Generate a MySQL Alter Table syntax for the given Schema comparison
*
- * @param array $compare Result of a CakeSchema::compare()
- * @param string $table The table name.
+ * @param mixed $compare Result of a CakeSchema::compare()
+ * @param string|null $table The table name.
* @return string|false String of alter statements to make.
*/
- public function alterSchema($compare, $table = null)
+ public function alterSchema(mixed $compare, ?string $table = null): string|false
{
if (!is_array($compare)) {
return false;
}
$out = '';
- $colList = [];
foreach ($compare as $curTable => $types) {
$indexes = $tableParameters = $colList = [];
if (!$table || $table === $curTable) {
@@ -677,7 +676,7 @@ public function alterSchema($compare, $table = null)
* @param Model|string $table Name of the table to drop
* @return string Drop table SQL statement
*/
- protected function _dropTable($table): string
+ protected function _dropTable(Model|string $table): string
{
return 'DROP TABLE IF EXISTS ' . $this->fullTableName($table) . ';';
}
@@ -689,7 +688,7 @@ protected function _dropTable($table): string
* @param array $parameters Parameters to add & drop.
* @return array Array of table property alteration statements.
*/
- protected function _alterTableParameters($table, $parameters)
+ protected function _alterTableParameters(string $table, array $parameters): array
{
if (isset($parameters['change'])) {
return $this->buildTableParameters($parameters['change']);
@@ -730,14 +729,15 @@ public function buildIndex(array $indexes, ?string $table = null): array
$vals = [];
foreach ($value['column'] as $column) {
$name = $this->name($column);
- if (isset($value['length'])) {
- $name .= $this->_buildIndexSubPart($value['length'], $column);
- }
+ $name .= $this->_buildIndexSubPart($value['length'], $column);
+
$vals[] = $name;
}
$out .= implode(', ', $vals);
} else {
- $out .= implode(', ', array_map([&$this, 'name'], $value['column']));
+ /** @var array $_column */
+ $_column = array_map([&$this, 'name'], $value['column']);
+ $out .= implode(', ', $_column);
}
} else {
$out .= $this->name($value['column']);
@@ -759,7 +759,7 @@ public function buildIndex(array $indexes, ?string $table = null): array
* @param array $indexes Indexes to add and drop
* @return array Index alteration statements
*/
- protected function _alterIndexes($table, $indexes)
+ protected function _alterIndexes(string $table, array $indexes): array
{
$alter = [];
if (isset($indexes['drop'])) {
@@ -786,11 +786,11 @@ protected function _alterIndexes($table, $indexes)
/**
* Format length for text indexes
*
- * @param array $lengths An array of lengths for a single index
+ * @param array|null $lengths An array of lengths for a single index
* @param string $column The column for which to generate the index length
* @return string Formatted length part of an index field
*/
- protected function _buildIndexSubPart($lengths, $column)
+ protected function _buildIndexSubPart(?array $lengths, string $column): string
{
if ($lengths === null) {
return '';
@@ -805,10 +805,10 @@ protected function _buildIndexSubPart($lengths, $column)
/**
* Returns a detailed array of sources (tables) in the database.
*
- * @param string $name Table name to get parameters
+ * @param string|null $name Table name to get parameters
* @return array Array of table names in the database
*/
- public function listDetailedSources($name = null)
+ public function listDetailedSources(?string $name = null): array
{
$condition = '';
if (is_string($name)) {
@@ -817,8 +817,6 @@ public function listDetailedSources($name = null)
$result = $this->_connection->query('SHOW TABLE STATUS ' . $condition, PDO::FETCH_ASSOC);
if (!$result) {
- $result->closeCursor();
-
return [];
}
$tables = [];
@@ -843,10 +841,10 @@ public function listDetailedSources($name = null)
/**
* Converts database-layer column types to basic types
*
- * @param string $real Real database-layer column type (i.e. "varchar(255)")
- * @return string Abstract column type (i.e. "string")
+ * @param mixed $real Real database-layer column type (i.e. "varchar(255)")
+ * @return string|false Abstract column type (i.e. "string")
*/
- public function column($real)
+ public function column(mixed $real): string|false
{
if (is_array($real)) {
$col = $real['name'];
@@ -858,6 +856,7 @@ public function column($real)
}
$col = str_replace(')', '', $real);
+ $vals = '';
$limit = $this->length($real);
if (str_contains($col, '(')) {
[$col, $vals] = explode('(', $col);
@@ -909,7 +908,7 @@ public function column($real)
/**
* @inheritDoc
*/
- public function value($data, ?string $column = null, bool $null = true): array|string
+ public function value(mixed $data, ?string $column = null, bool $null = true): array|string
{
$value = parent::value($data, $column, $null);
if (is_numeric($value) && $column !== null && str_starts_with($column, 'set')) {
@@ -924,7 +923,7 @@ public function value($data, ?string $column = null, bool $null = true): array|s
*
* @return string The schema name
*/
- public function getSchemaName()
+ public function getSchemaName(): string
{
return $this->config['database'];
}
@@ -944,7 +943,7 @@ public function nestedTransactionSupported(): bool
*
* @return string Server type (MySQL, Aurora MySQL, or MariaDB)
*/
- public function getServerType()
+ public function getServerType(): string
{
// Ensure version has been fetched to determine server type
$this->getVersion();
@@ -957,7 +956,7 @@ public function getServerType()
*
* @return bool
*/
- public function utf8mb4Supported()
+ public function utf8mb4Supported(): bool
{
// MariaDB 5.5+ supports utf8mb4
if ($this->getServerType() === self::SERVER_TYPE_MARIADB) {
@@ -983,7 +982,7 @@ public function utf8mb4Supported()
*
* @return bool
*/
- public function integerDisplayWidthDeprecated()
+ public function integerDisplayWidthDeprecated(): bool
{
// Only applies to MySQL and Aurora MySQL 8.0.17+, not MariaDB
if ($this->getServerType() === self::SERVER_TYPE_MARIADB) {
@@ -1008,7 +1007,7 @@ public function integerDisplayWidthDeprecated()
* @param string $real Real database-layer column type (i.e. "varchar(255)")
* @return bool True if column is unsigned, false otherwise
*/
- protected function _unsigned($real)
+ protected function _unsigned(string $real): bool
{
return str_contains(strtolower($real), 'unsigned');
}
@@ -1018,17 +1017,19 @@ protected function _unsigned($real)
* multiple rows.
*
* @param Model|string $table The table being inserted into.
- * @param array $fields The array of field/column names being inserted.
+ * @param array|string $fields The array of field/column names being inserted.
* @param array $values The array of values to insert. The values should
* be an array of rows. Each row should have values keyed by the column name.
* Each row must have the values in the same order as $fields.
* @return bool
*/
- public function insertMulti(Model|string $table, array $fields, array $values): bool
+ public function insertMulti(Model|string $table, array|string $fields, array $values): bool
{
$table = $this->fullTableName($table);
- $holder = implode(', ', array_fill(0, count($fields), '?'));
- $fields = implode(', ', array_map([$this, 'name'], $fields));
+ /** @var array $_fields */
+ $_fields = array_map([$this, 'name'], (array)$fields);
+ $holder = implode(', ', array_fill(0, count((array)$fields), '?'));
+ $fields = implode(', ', $_fields);
$pdoMap = [
'integer' => PDO::PARAM_INT,
'float' => PDO::PARAM_STR,
diff --git a/src/Model/Datasource/Database/Postgres.php b/src/Model/Datasource/Database/Postgres.php
index c2f6ac815b..86e112f141 100644
--- a/src/Model/Datasource/Database/Postgres.php
+++ b/src/Model/Datasource/Database/Postgres.php
@@ -22,6 +22,7 @@
use Cake\Utility\Hash;
use PDO;
use PDOException;
+use PDOStatement;
/**
* PostgreSQL layer for DBO.
@@ -106,9 +107,21 @@ class Postgres extends DboSource
/**
* The set of valid SQL operations usable in a WHERE statement
*
- * @var array
+ * @var array
*/
- protected array $_sqlOps = ['like', 'ilike', 'or', 'not', 'in', 'between', '~', '~\*', '\!~', '\!~\*', 'similar to'];
+ protected array $_sqlOps = [
+ 'like',
+ 'ilike',
+ 'or',
+ 'not',
+ 'in',
+ 'between',
+ '~',
+ '~\*',
+ '\!~',
+ '\!~\*',
+ 'similar to',
+ ];
/**
* Connects to the database using options in the given configuration array.
@@ -116,7 +129,7 @@ class Postgres extends DboSource
* @return bool True if successfully connected.
* @throws MissingConnectionException
*/
- public function connect()
+ public function connect(): bool
{
$config = $this->config;
$this->connected = false;
@@ -166,7 +179,7 @@ public function connect()
*
* @return bool
*/
- public function enabled()
+ public function enabled(): bool
{
return in_array('pgsql', PDO::getAvailableDrivers());
}
@@ -174,8 +187,8 @@ public function enabled()
/**
* Returns an array of tables in the database. If there are no tables, an error is raised and the application exits.
*
- * @param mixed $data The sources to list.
- * @return array Array of table names in the database
+ * @param array|null $data The sources to list.
+ * @return array|null Array of table names in the database
*/
public function listSources(?array $data = null): ?array
{
@@ -209,9 +222,9 @@ public function listSources(?array $data = null): ?array
* Returns an array of the fields in given table name.
*
* @param Model|string $model Name of database table to inspect
- * @return array Fields in table. Keys are name and type
+ * @return array|false|null Fields in table. Keys are name and type
*/
- public function describe(string|Model $model)
+ public function describe(Model|string $model): array|false|null
{
$table = $this->fullTableName($model, false, false);
$fields = parent::describe($table);
@@ -239,7 +252,6 @@ public function describe(string|Model $model)
[$table, $this->config['schema'], $this->config['database']],
);
- // @codingStandardsIgnoreStart
// Postgres columns don't match the coding standards.
foreach ($cols as $c) {
$type = $c->type;
@@ -263,7 +275,7 @@ public function describe(string|Model $model)
}
$fields[$c->name] = [
'type' => $this->column($type),
- 'null' => ($c->null === 'NO' ? false : true),
+ 'null' => $c->null !== 'NO',
'default' => $c->default ? preg_replace(
"/^'(.*)'$/",
'$1',
@@ -315,7 +327,6 @@ public function describe(string|Model $model)
}
$this->_cacheDescription($table, $fields);
}
- // @codingStandardsIgnoreEnd
if (isset($model->sequence)) {
$this->_sequenceMap[$table][$model->primaryKey] = $model->sequence;
@@ -331,12 +342,14 @@ public function describe(string|Model $model)
/**
* Returns the ID generated from the previous INSERT operation.
*
- * @param string $source Name of the database table
+ * @param mixed $source Name of the database table
* @param string $field Name of the ID database field. Defaults to "id"
- * @return int
+ * @return string|bool
*/
- public function lastInsertId($source = null, $field = 'id')
- {
+ public function lastInsertId(
+ mixed $source = null,
+ string $field = 'id',
+ ): string|bool {
$seq = $this->getSequence($source, $field);
return $this->_connection->lastInsertId($seq);
@@ -349,7 +362,7 @@ public function lastInsertId($source = null, $field = 'id')
* @param string $field Name of the ID database field. Defaults to "id"
* @return string The associated sequence name from the sequence map, defaults to "{$table}_{$field}_seq"
*/
- public function getSequence($table, $field = 'id')
+ public function getSequence(Model|string $table, string $field = 'id'): string
{
if (is_object($table)) {
$table = $this->fullTableName($table, false, false);
@@ -388,10 +401,12 @@ public function resetSequence(string $table, string $column): bool
* @param Model|string $table A string or model class representing the table to be truncated
* @param bool $reset true for resetting the sequence, false to leave it as is.
* and if 1, sequences are not modified
- * @return bool SQL TRUNCATE TABLE statement, false if not applicable.
+ * @return PDOStatement|bool|null SQL TRUNCATE TABLE statement, false if not applicable.
*/
- public function truncate(Model|string $table, bool $reset = false)
- {
+ public function truncate(
+ Model|string $table,
+ bool $reset = false,
+ ): PDOStatement|bool|null {
$table = $this->fullTableName($table, false, false);
if (!isset($this->_sequenceMap[$table])) {
$cache = $this->cacheSources;
@@ -400,7 +415,7 @@ public function truncate(Model|string $table, bool $reset = false)
$this->cacheSources = $cache;
}
if ($this->execute('DELETE FROM ' . $this->fullTableName($table))) {
- if (isset($this->_sequenceMap[$table]) && $reset != true) {
+ if (isset($this->_sequenceMap[$table]) && !$reset) {
foreach ($this->_sequenceMap[$table] as $sequence) {
$quoted = $this->name($sequence);
$this->_execute("ALTER SEQUENCE {$quoted} RESTART WITH 1");
@@ -416,10 +431,10 @@ public function truncate(Model|string $table, bool $reset = false)
/**
* Prepares field names to be quoted by parent
*
- * @param string $data The name to format.
- * @return string SQL field
+ * @param mixed $data The name to format.
+ * @return array|string SQL field
*/
- public function name($data)
+ public function name(mixed $data): array|string
{
if (is_string($data)) {
$data = str_replace('"__"', '__', $data);
@@ -432,13 +447,17 @@ public function name($data)
* Generates the fields list of an SQL query.
*
* @param Model $model The model to get fields for.
- * @param string $alias Alias table name.
+ * @param string|null $alias Alias table name.
* @param mixed $fields The list of fields to get.
* @param bool $quote Whether or not to quote identifiers.
* @return array
*/
- public function fields(Model $model, $alias = null, $fields = [], $quote = true)
- {
+ public function fields(
+ Model $model,
+ ?string $alias = null,
+ mixed $fields = [],
+ bool $quote = true,
+ ): array {
if (empty($alias)) {
$alias = $model->alias;
}
@@ -494,10 +513,10 @@ public function fields(Model $model, $alias = null, $fields = [], $quote = true)
* Auxiliary function to quote matched `(Model.fields)` from a preg_replace_callback call
* Quotes the fields in a function call.
*
- * @param string $match matched string
+ * @param array $match matched string
* @return string quoted string
*/
- protected function _quoteFunctionField($match)
+ protected function _quoteFunctionField(array $match): string
{
$prepend = '';
if (str_contains($match[1], 'DISTINCT')) {
@@ -524,7 +543,7 @@ protected function _quoteFunctionField($match)
* @param Model|string $model Name of model to inspect
* @return array Fields in table. Keys are column and unique
*/
- public function index($model)
+ public function index(Model|string $model): array
{
$index = [];
$table = $this->fullTableName($model, false, false);
@@ -561,17 +580,17 @@ public function index($model)
/**
* Alter the Schema of a table.
*
- * @param array $compare Results of CakeSchema::compare()
- * @param string $table name of the table
- * @return array
+ * @param mixed $compare Results of CakeSchema::compare()
+ * @param string|null $table name of the table
+ * @return string|false
*/
- public function alterSchema($compare, $table = null)
+ public function alterSchema(mixed $compare, ?string $table = null): string|false
{
if (!is_array($compare)) {
return false;
}
+
$out = '';
- $colList = [];
foreach ($compare as $curTable => $types) {
$indexes = $colList = [];
if (!$table || $table === $curTable) {
@@ -681,7 +700,7 @@ public function alterSchema($compare, $table = null)
* @param array $indexes Indexes to add and drop
* @return array Index alteration statements
*/
- protected function _alterIndexes($table, $indexes)
+ protected function _alterIndexes(string $table, array $indexes): array
{
$alter = [];
if (isset($indexes['drop'])) {
@@ -707,7 +726,9 @@ protected function _alterIndexes($table, $indexes)
$out .= 'INDEX ';
}
if (is_array($value['column'])) {
- $out .= $name . ' ON ' . $table . ' (' . implode(', ', array_map([&$this, 'name'], $value['column'])) . ')';
+ /** @var array $_column */
+ $_column = array_map([&$this, 'name'], $value['column']);
+ $out .= $name . ' ON ' . $table . ' (' . implode(', ', $_column) . ')';
} else {
$out .= $name . ' ON ' . $table . ' (' . $this->name($value['column']) . ')';
}
@@ -721,17 +742,20 @@ protected function _alterIndexes($table, $indexes)
/**
* Returns a limit statement in the correct format for the particular database.
*
- * @param int $limit Limit of results returned
- * @param int|null $offset Offset from which to start results
+ * @param array|string|int|null $limit Limit of results returned
+ * @param array|string|int|null $offset Offset from which to start results
* @return string|null SQL limit/offset statement
*/
- public function limit($limit, $offset = null)
- {
+ public function limit(
+ array|string|int|null $limit,
+ array|string|int|null $offset = null,
+ ): ?string {
if ($limit) {
// Suppress PHP 8.5+ warning for backward compatibility with existing limit/offset behavior
// The sprintf %u format behavior is undefined for values outside int range, but must remain
// consistent with previous PHP versions for query generation
set_error_handler(function () {
+ return true;
}, E_WARNING);
$rt = sprintf(' LIMIT %u', $limit);
if ($offset) {
@@ -748,10 +772,10 @@ public function limit($limit, $offset = null)
/**
* Converts database-layer column types to basic types
*
- * @param string $real Real database-layer column type (i.e. "varchar(255)")
- * @return string Abstract column type (i.e. "string")
+ * @param mixed $real Real database-layer column type (i.e. "varchar(255)")
+ * @return string|false Abstract column type (i.e. "string")
*/
- public function column($real)
+ public function column(mixed $real): string|false
{
if (is_array($real)) {
$col = $real['name'];
@@ -772,43 +796,30 @@ public function column($real)
'float', 'float4', 'float8', 'double', 'double precision', 'real',
];
- switch (true) {
- case in_array($col, ['date', 'time', 'inet', 'boolean']):
- return $col;
- case str_contains($col, 'timestamp'):
- return 'datetime';
- case str_starts_with($col, 'time'):
- return 'time';
- case $col === 'bigint':
- return 'biginteger';
- case $col === 'smallint':
- return 'smallinteger';
- case str_contains($col, 'int') && $col !== 'interval':
- return 'integer';
- case str_contains($col, 'char'):
- return 'string';
- case str_contains($col, 'uuid'):
- return 'uuid';
- case str_contains($col, 'text'):
- return 'text';
- case str_contains($col, 'bytea'):
- return 'binary';
- case $col === 'decimal' || $col === 'numeric':
- return 'decimal';
- case in_array($col, $floats):
- return 'float';
- default:
- return 'text';
- }
+ return match (true) {
+ in_array($col, ['date', 'time', 'inet', 'boolean']) => $col,
+ str_contains($col, 'timestamp') => 'datetime',
+ str_starts_with($col, 'time') => 'time',
+ $col === 'bigint' => 'biginteger',
+ $col === 'smallint' => 'smallinteger',
+ str_contains($col, 'int') && $col !== 'interval' => 'integer',
+ str_contains($col, 'char') => 'string',
+ str_contains($col, 'uuid') => 'uuid',
+ str_contains($col, 'text') => 'text',
+ str_contains($col, 'bytea') => 'binary',
+ $col === 'decimal' || $col === 'numeric' => 'decimal',
+ in_array($col, $floats) => 'float',
+ default => 'text',
+ };
}
/**
* Gets the length of a database-native column description, or null if no length
*
- * @param string $real Real database-layer column type (i.e. "varchar(255)")
- * @return int An integer representing the length of the column
+ * @param object|string $real Real database-layer column type (i.e. "varchar(255)")
+ * @return string|int|null An integer representing the length of the column
*/
- public function length($real)
+ public function length(object|string $real): string|int|null
{
$col = $real;
if (str_contains($real, '(')) {
@@ -827,7 +838,7 @@ public function length($real)
* @param PDOStatement $results The results
* @return void
*/
- public function resultSet($results)
+ public function resultSet(PDOStatement $results): void
{
$this->map = [];
$numFields = $results->columnCount();
@@ -849,9 +860,9 @@ public function resultSet($results)
/**
* Fetches the next row from the current result set
*
- * @return array
+ * @return array|false
*/
- public function fetchResult()
+ public function fetchResult(): array|false
{
if ($row = $this->_result->fetch(PDO::FETCH_NUM)) {
$resultRow = [];
@@ -859,17 +870,11 @@ public function fetchResult()
foreach ($this->map as $index => $meta) {
[$table, $column, $type] = $meta;
- switch ($type) {
- case 'bool':
- $resultRow[$table][$column] = $row[$index] === null ? null : $this->boolean($row[$index]);
- break;
- case 'binary':
- case 'bytea':
- $resultRow[$table][$column] = $row[$index] === null ? null : stream_get_contents($row[$index]);
- break;
- default:
- $resultRow[$table][$column] = $row[$index];
- }
+ $resultRow[$table][$column] = match ($type) {
+ 'bool' => $row[$index] === null ? null : $this->boolean($row[$index]),
+ 'binary', 'bytea' => $row[$index] === null ? null : stream_get_contents($row[$index]),
+ default => $row[$index],
+ };
}
return $resultRow;
@@ -884,32 +889,23 @@ public function fetchResult()
*
* @param mixed $data Value to be translated
* @param bool $quote true to quote a boolean to be used in a query, false to return the boolean value
- * @return bool Converted boolean value
+ * @return string|bool Converted boolean value
*/
- public function boolean($data, $quote = false)
+ public function boolean(mixed $data, bool $quote = false): string|bool
{
- switch (true) {
- case $data === true || $data === false:
- $result = $data;
- break;
- case $data === 't' || $data === 'f':
- $result = ($data === 't');
- break;
- case $data === 'true' || $data === 'false':
- $result = ($data === 'true');
- break;
- case $data === 'TRUE' || $data === 'FALSE':
- $result = ($data === 'TRUE');
- break;
- default:
- $result = (bool)$data;
- }
+ $result = match (true) {
+ $data === true || $data === false => $data,
+ $data === 't' || $data === 'f' => ($data === 't'),
+ $data === 'true' || $data === 'false' => ($data === 'true'),
+ $data === 'TRUE' || $data === 'FALSE' => ($data === 'TRUE'),
+ default => (bool)$data,
+ };
if ($quote) {
return $result ? 'TRUE' : 'FALSE';
}
- return (bool)$result;
+ return $result;
}
/**
@@ -918,7 +914,7 @@ public function boolean($data, $quote = false)
* @param mixed $enc Database encoding
* @return bool True on success, false on failure
*/
- public function setEncoding($enc)
+ public function setEncoding(mixed $enc): bool
{
return $this->_execute('SET NAMES ' . $this->value($enc)) !== false;
}
@@ -926,9 +922,9 @@ public function setEncoding($enc)
/**
* Gets the database encoding
*
- * @return string The database encoding
+ * @return string|false The database encoding
*/
- public function getEncoding()
+ public function getEncoding(): string|false
{
$result = $this->_execute('SHOW client_encoding')->fetch();
if ($result === false) {
@@ -944,9 +940,9 @@ public function getEncoding()
* @param array $column An array structured like the following:
* array('name'=>'value', 'type'=>'value'[, options]),
* where options can be 'default', 'length', or 'key'.
- * @return string
+ * @return string|null
*/
- public function buildColumn($column)
+ public function buildColumn(array $column): ?string
{
$col = $this->columns[$column['type']];
if (!isset($col['length']) && !isset($col['limit'])) {
@@ -997,9 +993,6 @@ public function buildColumn($column)
public function buildIndex(array $indexes, ?string $table = null): array
{
$join = [];
- if (!is_array($indexes)) {
- return [];
- }
foreach ($indexes as $name => $value) {
if ($name === 'PRIMARY') {
$out = 'PRIMARY KEY (' . $this->name($value['column']) . ')';
@@ -1009,7 +1002,9 @@ public function buildIndex(array $indexes, ?string $table = null): array
$out .= 'UNIQUE ';
}
if (is_array($value['column'])) {
- $value['column'] = implode(', ', array_map([&$this, 'name'], $value['column']));
+ /** @var array $_column */
+ $_column = array_map([&$this, 'name'], $value['column']);
+ $value['column'] = implode(', ', $_column);
} else {
$value['column'] = $this->name($value['column']);
}
@@ -1024,10 +1019,10 @@ public function buildIndex(array $indexes, ?string $table = null): array
/**
* @inheritDoc
*/
- public function value($data, ?string $column = null, bool $null = true): array|string
+ public function value(mixed $data, ?string $column = null, bool $null = true): array|string
{
$value = parent::value($data, $column, $null);
- if ($column === 'uuid' && is_scalar($data) && $data === '') {
+ if ($column === 'uuid' && $data === '') {
return 'NULL';
}
@@ -1038,17 +1033,45 @@ public function value($data, ?string $column = null, bool $null = true): array|s
* Overrides DboSource::renderStatement to handle schema generation with Postgres-style indexes
*
* @param string $type The query type.
- * @param array $data The array of data to render.
- * @return string
+ * @param array{
+ * fields: string|null,
+ * table: string|null,
+ * alias: string|null,
+ * joins?: string|null,
+ * conditions?: string|null,
+ * group?: string|null,
+ * having?: string|null,
+ * order?: string|null,
+ * limit?: string|null,
+ * lock?: string|null
+ * }|array{
+ * fields: string|null,
+ * table: string|null,
+ * values?: string|null
+ * }|array{
+ * fields: string|null,
+ * table: string|null,
+ * alias: string|null,
+ * joins?: string|null,
+ * conditions?: string|null
+ * }|array{
+ * table: string|null,
+ * columns?: mixed,
+ * indexes?: mixed,
+ * tableParameters?: mixed
+ * } $data $data The array of data to render.
+ * @return string|null
*/
- public function renderStatement($type, $data)
+ public function renderStatement(string $type, array $data): ?string
{
switch (strtolower($type)) {
case 'schema':
- extract($data);
+ $table = $data['table'] ?? null;
+ $columns = $data['columns'] ?? [];
+ $indexes = $data['indexes'] ?? [];
foreach ($indexes as $i => $index) {
- if (preg_match('/PRIMARY KEY/', $index)) {
+ if (str_contains($index, 'PRIMARY KEY')) {
unset($indexes[$i]);
$columns[] = $index;
break;
@@ -1073,7 +1096,7 @@ public function renderStatement($type, $data)
*
* @return string The schema name
*/
- public function getSchemaName()
+ public function getSchemaName(): string
{
return $this->config['schema'];
}
diff --git a/src/Model/Datasource/Database/Sqlite.php b/src/Model/Datasource/Database/Sqlite.php
index e1b2a1d0a3..d57430600b 100644
--- a/src/Model/Datasource/Database/Sqlite.php
+++ b/src/Model/Datasource/Database/Sqlite.php
@@ -116,7 +116,7 @@ class Sqlite extends DboSource
* @return bool
* @throws MissingConnectionException
*/
- public function connect()
+ public function connect(): bool
{
$config = $this->config;
$flags = $config['flags'] + [
@@ -141,7 +141,7 @@ public function connect()
*
* @return bool
*/
- public function enabled()
+ public function enabled(): bool
{
return in_array('sqlite', PDO::getAvailableDrivers());
}
@@ -149,8 +149,8 @@ public function enabled()
/**
* Returns an array of tables in the database. If there are no tables, an error is raised and the application exits.
*
- * @param mixed $data Unused.
- * @return array Array of table names in the database
+ * @param array|null $data Unused.
+ * @return array|null Array of table names in the database
*/
public function listSources(?array $data = null): ?array
{
@@ -161,7 +161,7 @@ public function listSources(?array $data = null): ?array
$result = $this->fetchAll("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;", false);
- if (!$result || empty($result)) {
+ if (empty($result)) {
return [];
}
@@ -178,9 +178,9 @@ public function listSources(?array $data = null): ?array
* Returns an array of the fields in given table name.
*
* @param Model|string $model Either the model or table name you want described.
- * @return array Fields in table. Keys are name and type
+ * @return array|false|null Fields in table. Keys are name and type
*/
- public function describe(string|Model $model)
+ public function describe(Model|string $model): array|false|null
{
$table = $this->fullTableName($model, false, false);
$cache = parent::describe($table);
@@ -223,13 +223,17 @@ public function describe(string|Model $model)
* Generates and executes an SQL UPDATE statement for given model, fields, and values.
*
* @param Model $model The model instance to update.
- * @param array $fields The fields to update.
- * @param array $values The values to set columns to.
+ * @param array|null $fields The fields to update.
+ * @param array|null $values The values to set columns to.
* @param mixed $conditions array of conditions to use.
* @return bool
*/
- public function update(Model $model, $fields = [], $values = null, $conditions = null)
- {
+ public function update(
+ Model $model,
+ ?array $fields = [],
+ ?array $values = null,
+ mixed $conditions = null,
+ ): bool {
if (empty($values) && !empty($fields)) {
foreach ($fields as $field => $value) {
if (str_contains($field, $model->alias . '.')) {
@@ -249,10 +253,11 @@ public function update(Model $model, $fields = [], $values = null, $conditions =
* primary key, where applicable.
*
* @param Model|string $table A string or model class representing the table to be truncated
- * @return bool SQL TRUNCATE TABLE statement, false if not applicable.
+ * @return PDOStatement|bool|null SQL TRUNCATE TABLE statement, false if not applicable.
*/
- public function truncate(Model|string $table)
- {
+ public function truncate(
+ Model|string $table,
+ ): PDOStatement|bool|null {
if (in_array('sqlite_sequence', $this->listSources())) {
$this->_execute('DELETE FROM sqlite_sequence where name=' . $this->startQuote . $this->fullTableName($table, false, false) . $this->endQuote);
}
@@ -263,10 +268,10 @@ public function truncate(Model|string $table)
/**
* Converts database-layer column types to basic types
*
- * @param string $real Real database-layer column type (i.e. "varchar(255)")
- * @return string Abstract column type (i.e. "string")
+ * @param mixed $real Real database-layer column type (i.e. "varchar(255)")
+ * @return string|false Abstract column type (i.e. "string")
*/
- public function column($real)
+ public function column(mixed $real): string|false
{
if (is_array($real)) {
$col = $real['name'];
@@ -323,7 +328,7 @@ public function column($real)
* @param PDOStatement $results The results to modify.
* @return void
*/
- public function resultSet($results)
+ public function resultSet(PDOStatement $results): void
{
$this->_result = $results;
$this->map = [];
@@ -370,9 +375,7 @@ public function resultSet($results)
$metaType = false;
try {
$metaData = (array)$results->getColumnMeta($j);
- if (!empty($metaData['sqlite:decl_type'])) {
- $metaType = trim($metaData['sqlite:decl_type']);
- }
+ $metaType = trim($metaData['sqlite:decl_type'] ?? '') ?: false;
} catch (Exception) {
}
@@ -389,9 +392,9 @@ public function resultSet($results)
/**
* Fetches the next row from the current result set
*
- * @return mixed array with results fetched and mapped to column names or false if there is no results left to fetch
+ * @return array|false array with results fetched and mapped to column names or false if there is no results left to fetch
*/
- public function fetchResult()
+ public function fetchResult(): array|false
{
if ($row = $this->_result->fetch(PDO::FETCH_NUM)) {
$resultRow = [];
@@ -413,17 +416,20 @@ public function fetchResult()
/**
* Returns a limit statement in the correct format for the particular database.
*
- * @param int $limit Limit of results returned
- * @param int|null $offset Offset from which to start results
+ * @param array|string|int|null $limit Limit of results returned
+ * @param array|string|int|null $offset Offset from which to start results
* @return string|null SQL limit/offset statement
*/
- public function limit($limit, $offset = null)
- {
+ public function limit(
+ array|string|int|null $limit,
+ array|string|int|null $offset = null,
+ ): ?string {
if ($limit) {
// Suppress PHP 8.5+ warning for backward compatibility with existing limit/offset behavior
// The sprintf %u format behavior is undefined for values outside int range, but must remain
// consistent with previous PHP versions for query generation
set_error_handler(function () {
+ return true;
}, E_WARNING);
$rt = sprintf(' LIMIT %u', $limit);
if ($offset) {
@@ -442,13 +448,12 @@ public function limit($limit, $offset = null)
*
* @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]),
* where options can be 'default', 'length', or 'key'.
- * @return string
+ * @return string|null
*/
- public function buildColumn($column)
+ public function buildColumn(array $column): ?string
{
- $name = $type = null;
- $column += ['null' => true];
- extract($column);
+ $name = $column['name'] ?? null;
+ $type = $column['type'] ?? null;
if (empty($name) || empty($type)) {
trigger_error(__d('cake_dev', 'Column name or type not defined in schema'), E_USER_WARNING);
@@ -485,7 +490,7 @@ public function buildColumn($column)
* @param string $enc Database encoding
* @return bool
*/
- public function setEncoding($enc)
+ public function setEncoding(string $enc): bool
{
if (!in_array($enc, ['UTF-8', 'UTF-16', 'UTF-16le', 'UTF-16be'])) {
return false;
@@ -497,9 +502,9 @@ public function setEncoding($enc)
/**
* Gets the database encoding
*
- * @return string The database encoding
+ * @return array|false The database encoding
*/
- public function getEncoding()
+ public function getEncoding(): array|false
{
return $this->fetchRow('PRAGMA encoding');
}
@@ -529,7 +534,9 @@ public function buildIndex(array $indexes, ?string $table = null): array
$out .= 'UNIQUE ';
}
if (is_array($value['column'])) {
- $value['column'] = implode(', ', array_map([&$this, 'name'], $value['column']));
+ /** @var array $_column */
+ $_column = array_map([&$this, 'name'], $value['column']);
+ $value['column'] = implode(', ', $_column);
} else {
$value['column'] = $this->name($value['column']);
}
@@ -550,7 +557,7 @@ public function buildIndex(array $indexes, ?string $table = null): array
* @param Model|string $model Name of model to inspect
* @return array Fields in table. Keys are column and unique
*/
- public function index($model)
+ public function index(Model|string $model): array
{
$index = [];
$table = $this->fullTableName($model, false, false);
@@ -566,7 +573,7 @@ public function index($model)
foreach ($keyInfo as $keyCol) {
if (!isset($index[$key['name']])) {
$col = [];
- if (preg_match('/autoindex/', $key['name'])) {
+ if (str_contains($key['name'], 'autoindex')) {
$key['name'] = 'PRIMARY';
}
$index[$key['name']]['column'] = $keyCol[0]['name'];
@@ -589,14 +596,43 @@ public function index($model)
* Overrides DboSource::renderStatement to handle schema generation with SQLite-style indexes
*
* @param string $type The type of statement being rendered.
- * @param array $data The data to convert to SQL.
- * @return string
+ * @param array{
+ * fields: string|null,
+ * table: string|null,
+ * alias: string|null,
+ * joins?: string|null,
+ * conditions?: string|null,
+ * group?: string|null,
+ * having?: string|null,
+ * order?: string|null,
+ * limit?: string|null,
+ * lock?: string|null
+ * }|array{
+ * fields: string|null,
+ * table: string|null,
+ * values?: string|null
+ * }|array{
+ * fields: string|null,
+ * table: string|null,
+ * alias: string|null,
+ * joins?: string|null,
+ * conditions?: string|null
+ * }|array{
+ * table: string|null,
+ * columns?: mixed,
+ * indexes?: mixed,
+ * tableParameters?: mixed
+ * } $data The data to convert to SQL.
+ * @return string|null
*/
- public function renderStatement($type, $data)
+ public function renderStatement(string $type, array $data): ?string
{
switch (strtolower($type)) {
case 'schema':
- extract($data);
+ $table = $data['table'] ?? '';
+ $columns = $data['columns'] ?? [];
+ $indexes = $data['indexes'] ?? [];
+
if (is_array($columns)) {
$columns = "\t" . implode(",\n\t", array_filter($columns));
}
@@ -615,7 +651,7 @@ public function renderStatement($type, $data)
*
* @return bool
*/
- public function hasResult()
+ public function hasResult(): bool
{
return is_object($this->_result);
}
@@ -626,7 +662,7 @@ public function hasResult()
* @param Model|string $table Name of the table to drop
* @return string Drop table SQL statement
*/
- protected function _dropTable($table): string
+ protected function _dropTable(Model|string $table): string
{
return 'DROP TABLE IF EXISTS ' . $this->fullTableName($table) . ';';
}
@@ -636,7 +672,7 @@ protected function _dropTable($table): string
*
* @return string The schema name
*/
- public function getSchemaName()
+ public function getSchemaName(): string
{
return 'main'; // Sqlite Datasource does not support multidb
}
@@ -659,7 +695,7 @@ public function nestedTransactionSupported(): bool
* @param mixed $mode Lock mode
* @return string|null Null
*/
- public function getLockingHint($mode)
+ public function getLockingHint(mixed $mode): ?string
{
return null;
}
diff --git a/src/Model/Datasource/Database/Sqlserver.php b/src/Model/Datasource/Database/Sqlserver.php
index 654b82e162..65023f0547 100644
--- a/src/Model/Datasource/Database/Sqlserver.php
+++ b/src/Model/Datasource/Database/Sqlserver.php
@@ -21,8 +21,10 @@
use Cake\Error\CakeException;
use Cake\Error\MissingConnectionException;
use Cake\Model\Datasource\DboSource;
+use Cake\Model\Datasource\PDOExceptionWithQueryString;
use Cake\Model\Model;
use InvalidArgumentException;
+use Override;
use PDO;
use PDOException;
use PDOStatement;
@@ -70,9 +72,9 @@ class Sqlserver extends DboSource
/**
* Storing the last affected value
*
- * @var mixed
+ * @var int|false
*/
- protected $_lastAffected = false;
+ protected int|false $_lastAffected = false;
/**
* Base configuration settings for MS SQL driver
@@ -114,7 +116,7 @@ class Sqlserver extends DboSource
'boolean' => ['name' => 'bit'],
];
- public $error = null;
+ public mixed $error = null;
/**
* Magic column name used to provide pagination support for SQLServer 2008
@@ -136,7 +138,7 @@ class Sqlserver extends DboSource
* @throws InvalidArgumentException if an unsupported setting is in the database config
* @throws MissingConnectionException
*/
- public function connect()
+ public function connect(): bool
{
// Set default schema to 'dbo' if not specified
if (!isset($this->config['schema']) || $this->config['schema'] === '') {
@@ -210,7 +212,7 @@ public function connect()
*
* @return bool
*/
- public function enabled()
+ public function enabled(): bool
{
return in_array('sqlsrv', PDO::getAvailableDrivers());
}
@@ -218,8 +220,8 @@ public function enabled()
/**
* Returns an array of sources (tables) in the database.
*
- * @param mixed $data The names
- * @return array Array of table names in the database
+ * @param array|null $data The names
+ * @return array|null Array of table names in the database
*/
public function listSources(?array $data = null): ?array
{
@@ -228,18 +230,15 @@ public function listSources(?array $data = null): ?array
return $cache;
}
- $schema = $this->getSchemaName() ?? false;
+ $schema = $this->getSchemaName();
// Filter tables by current database catalog
$result = $this->_execute('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES' . ($schema ? " WHERE TABLE_SCHEMA = '" . $schema . "'" : ''));
-
if (!$result) {
- $result->closeCursor();
-
return [];
}
- $tables = [];
+ $tables = [];
while ($line = $result->fetch(PDO::FETCH_NUM)) {
$tables[] = $line[0];
}
@@ -254,10 +253,10 @@ public function listSources(?array $data = null): ?array
* Returns an array of the fields in given table name.
*
* @param Model|string $model Model object to describe, or a string table name.
- * @return array Fields in table. Keys are name and type
+ * @return array|false|null Fields in table. Keys are name and type
* @throws CakeException
*/
- public function describe(string|Model $model): array
+ public function describe(Model|string $model): array|false|null
{
$table = $this->fullTableName($model, false, false);
$fulltable = $this->fullTableName($model, false, true);
@@ -268,7 +267,7 @@ public function describe(string|Model $model): array
}
$fields = [];
- $schema = is_object($model) ? $model->schemaName : $this->getSchemaName() ?? false;
+ $schema = is_object($model) ? $model->schemaName ?? false : $this->getSchemaName();
$cols = $this->_execute(
"SELECT
@@ -291,7 +290,7 @@ public function describe(string|Model $model): array
$field = $column->Field;
$fields[$field] = [
'type' => $this->column($column),
- 'null' => ($column->Null === 'YES' ? true : false),
+ 'null' => $column->Null === 'YES',
'default' => $column->Default,
'length' => $this->length($column),
'key' => $column->Key == '1' ? 'primary' : false,
@@ -331,13 +330,17 @@ public function describe(string|Model $model): array
* Generates the fields list of an SQL query.
*
* @param Model $model The model to get fields for.
- * @param string $alias Alias table name
+ * @param string|null $alias Alias table name
* @param array $fields The fields so far.
* @param bool $quote Whether or not to quote identfiers.
* @return array
*/
- public function fields(Model $model, $alias = null, $fields = [], $quote = true)
- {
+ public function fields(
+ Model $model,
+ ?string $alias = null,
+ mixed $fields = [],
+ bool $quote = true,
+ ): array {
if (empty($alias)) {
$alias = $model->alias;
}
@@ -420,12 +423,15 @@ public function fields(Model $model, $alias = null, $fields = [], $quote = true)
* value is empty.
*
* @param Model $model The model to insert into.
- * @param array $fields The fields to set.
- * @param array $values The values to set.
- * @return array
+ * @param array|null $fields The fields to set.
+ * @param array|null $values The values to set.
+ * @return bool
*/
- public function create(Model $model, $fields = null, $values = null)
- {
+ public function create(
+ Model $model,
+ ?array $fields = null,
+ ?array $values = null,
+ ): bool {
if (!empty($values)) {
$fields = array_combine($fields, $values);
}
@@ -451,13 +457,17 @@ public function create(Model $model, $fields = null, $values = null)
* Removes Identity (primary key) column from update data before returning to parent.
*
* @param Model $model The model to update.
- * @param array $fields The fields to set.
- * @param array $values The values to set.
+ * @param array|null $fields The fields to set.
+ * @param array|null $values The values to set.
* @param mixed $conditions The conditions to use.
- * @return array
+ * @return bool
*/
- public function update(Model $model, $fields = [], $values = null, $conditions = null)
- {
+ public function update(
+ Model $model,
+ ?array $fields = [],
+ ?array $values = null,
+ mixed $conditions = null,
+ ): bool {
if (!empty($values)) {
$fields = array_combine($fields, $values);
}
@@ -481,12 +491,14 @@ public function update(Model $model, $fields = [], $values = null, $conditions =
/**
* Returns a limit statement in the correct format for the particular database.
*
- * @param int $limit Limit of results returned
- * @param int|null $offset Offset from which to start results
- * @return string SQL limit/offset statement
+ * @param array|string|int|null $limit Limit of results returned
+ * @param array|string|int|null $offset Offset from which to start results
+ * @return string|null SQL limit/offset statement
*/
- public function limit($limit, $offset = null)
- {
+ public function limit(
+ array|string|int|null $limit,
+ array|string|int|null $offset = null,
+ ): ?string {
if ($limit) {
$rt = '';
if (!strpos(strtolower($limit), 'top') || str_starts_with(strtolower($limit), 'top')) {
@@ -508,9 +520,9 @@ public function limit($limit, $offset = null)
*
* @param mixed $real Either the string value of the fields type.
* or the Result object from Sqlserver::describe()
- * @return string Abstract column type (i.e. "string")
+ * @return string|false Abstract column type (i.e. "string")
*/
- public function column($real)
+ public function column(mixed $real): string|false
{
$limit = null;
$col = $real;
@@ -566,30 +578,31 @@ public function column($real)
* Handle SQLServer specific length properties.
* SQLServer handles text types as nvarchar/varchar with a length of -1.
*
- * @param object|string $length Either the length as a string, or a Column descriptor object.
- * @return mixed null|integer with length of column.
+ * @param object|string $real Either the length as a string, or a Column descriptor object.
+ * @return string|int|null with length of column.
*/
- public function length($length)
+ #[Override]
+ public function length(object|string $real): string|int|null
{
- if (is_object($length)) {
- if (!isset($length->Length) || $length->Length === null) {
+ if (is_object($real)) {
+ if (!isset($real->Length)) {
return null;
}
- if ($length->Length == -1 && str_contains($length->Type, 'char')) {
+ if ($real->Length == -1 && str_contains($real->Type, 'char')) {
return null;
}
- if (in_array($length->Type, ['nchar', 'nvarchar'])) {
- return floor($length->Length / 2);
+ if (in_array($real->Type, ['nchar', 'nvarchar'])) {
+ return (int)floor($real->Length / 2);
}
- if ($length->Type === 'text') {
+ if ($real->Type === 'text') {
return null;
}
- return $length->Length;
+ return $real->Length;
}
- return parent::length($length);
+ return parent::length($real);
}
/**
@@ -598,7 +611,7 @@ public function length($length)
* @param PDOStatement $results The result to modify.
* @return void
*/
- public function resultSet($results)
+ public function resultSet(PDOStatement $results): void
{
$this->map = [];
$numFields = $results->columnCount();
@@ -619,7 +632,7 @@ public function resultSet($results)
} else {
$map = [0, $name];
}
- $map[] = $column['sqlsrv:decl_type'] === 'bit' ? 'boolean' : $column['native_type'];
+ $map[] = isset($column['sqlsrv:decl_type']) && $column['sqlsrv:decl_type'] === 'bit' ? 'boolean' : $column['native_type'];
$this->map[$index++] = $map;
}
}
@@ -628,14 +641,50 @@ public function resultSet($results)
* Builds final SQL statement
*
* @param string $type Query type
- * @param array $data Query data
- * @return string
+ * @param array{
+ * fields: string|null,
+ * table: string|null,
+ * alias: string|null,
+ * joins?: string|null,
+ * conditions?: string|null,
+ * group?: string|null,
+ * having?: string|null,
+ * order?: string|null,
+ * limit?: string|null,
+ * lock?: string|null
+ * }|array{
+ * fields: string|null,
+ * table: string|null,
+ * values?: string|null
+ * }|array{
+ * fields: string|null,
+ * table: string|null,
+ * alias: string|null,
+ * joins?: string|null,
+ * conditions?: string|null
+ * }|array{
+ * table: string|null,
+ * columns?: mixed,
+ * indexes?: mixed,
+ * tableParameters?: mixed
+ * } $data Query data
+ * @return string|null
*/
- public function renderStatement($type, $data)
+ public function renderStatement(string $type, array $data): ?string
{
switch (strtolower($type)) {
case 'select':
- extract($data);
+ $fields = $data['fields'] ?? '';
+ $table = $data['table'] ?? '';
+ $alias = $data['alias'] ?? '';
+ $joins = $data['joins'] ?? '';
+ $conditions = $data['conditions'] ?? '';
+ $group = $data['group'] ?? '';
+ $having = $data['having'] ?? '';
+ $order = $data['order'] ?? '';
+ $limit = $data['limit'] ?? '';
+ $lock = $data['lock'] ?? '';
+
$fields = trim($fields);
$having = !empty($having) ? " $having" : '';
@@ -676,10 +725,12 @@ public function renderStatement($type, $data)
return trim("SELECT {$limit} {$fields} FROM {$table} {$alias}{$lock} {$joins} {$conditions} {$group}{$having} {$order}");
case 'schema':
- extract($data);
+ $table = $data['table'] ?? '';
+ $columns = $data['columns'] ?? [];
+ $indexes = $data['indexes'] ?? [];
foreach ($indexes as $i => $index) {
- if (preg_match('/PRIMARY KEY/', $index)) {
+ if (str_contains($index, 'PRIMARY KEY')) {
unset($indexes[$i]);
break;
}
@@ -700,7 +751,7 @@ public function renderStatement($type, $data)
/**
* @inheritDoc
*/
- public function value($data, ?string $column = null, bool $null = true): array|string
+ public function value(mixed $data, ?string $column = null, bool $null = true): array|string
{
if ($data === null || is_array($data) || is_object($data)) {
return parent::value($data, $column, $null);
@@ -713,13 +764,10 @@ public function value($data, ?string $column = null, bool $null = true): array|s
$column = $this->introspectType($data);
}
- switch ($column) {
- case 'string':
- case 'text':
- return 'N' . $this->_connection->quote($data, PDO::PARAM_STR);
- default:
- return parent::value($data, $column, $null);
- }
+ return match ($column) {
+ 'string', 'text' => 'N' . $this->_connection->quote($data, PDO::PARAM_STR),
+ default => parent::value($data, $column, $null),
+ };
}
/**
@@ -728,11 +776,14 @@ public function value($data, ?string $column = null, bool $null = true): array|s
*
* @param Model $model The model to read from
* @param array $queryData The query data
- * @param int $recursive How many layers to go.
+ * @param int|null $recursive How many layers to go.
* @return array|false Array of resultset rows, or false if no rows matched
*/
- public function read(Model $model, $queryData = [], $recursive = null)
- {
+ public function read(
+ Model $model,
+ array $queryData = [],
+ ?int $recursive = null,
+ ): false|array {
$results = parent::read($model, $queryData, $recursive);
$this->_fieldMappings = [];
@@ -743,9 +794,9 @@ public function read(Model $model, $queryData = [], $recursive = null)
* Fetches the next row from the current result set.
* Eats the magic ROW_COUNTER variable.
*
- * @return mixed
+ * @return array|false
*/
- public function fetchResult()
+ public function fetchResult(): array|false
{
if ($row = $this->_result->fetch(PDO::FETCH_NUM)) {
$resultRow = [];
@@ -771,17 +822,18 @@ public function fetchResult()
* Inserts multiple values into a table
*
* @param Model|string $table The table to insert into.
- * @param array $fields The fields to set.
+ * @param array|string $fields The fields to set.
* @param array $values The values to set.
* @return bool
*/
- public function insertMulti(Model|string $table, array $fields, array $values): bool
+ public function insertMulti(Model|string $table, array|string $fields, array $values): bool
{
$primaryKey = $this->_getPrimaryKey($table);
- $hasPrimaryKey = $primaryKey && (
- (is_array($fields) && in_array($primaryKey, $fields)
- || (is_string($fields) && str_contains($fields, $this->startQuote . $primaryKey . $this->endQuote)))
- );
+ $hasPrimaryKey = $primaryKey
+ && (
+ (is_array($fields) && in_array($primaryKey, $fields)
+ || (is_string($fields) && str_contains($fields, $this->startQuote . $primaryKey . $this->endQuote)))
+ );
if ($hasPrimaryKey) {
$this->_execute('SET IDENTITY_INSERT ' . $this->fullTableName($table) . ' ON');
@@ -802,9 +854,9 @@ public function insertMulti(Model|string $table, array $fields, array $values):
* @param array $column An array structured like the
* following: array('name'=>'value', 'type'=>'value'[, options]),
* where options can be 'default', 'length', or 'key'.
- * @return string
+ * @return string|null
*/
- public function buildColumn($column)
+ public function buildColumn(array $column): ?string
{
$result = parent::buildColumn($column);
$result = preg_replace('/(bigint|int|integer)\([0-9]+\)/i', '$1', $result);
@@ -841,7 +893,6 @@ public function buildColumn($column)
public function buildIndex(array $indexes, ?string $table = null): array
{
$join = [];
-
foreach ($indexes as $name => $value) {
if ($name === 'PRIMARY') {
$join[] = 'PRIMARY KEY (' . $this->name($value['column']) . ')';
@@ -849,7 +900,9 @@ public function buildIndex(array $indexes, ?string $table = null): array
$out = "ALTER TABLE {$table} ADD CONSTRAINT {$name} UNIQUE";
if (is_array($value['column'])) {
- $value['column'] = implode(', ', array_map([&$this, 'name'], $value['column']));
+ /** @var array $_column */
+ $_column = array_map([&$this, 'name'], $value['column']);
+ $value['column'] = implode(', ', $_column);
} else {
$value['column'] = $this->name($value['column']);
}
@@ -865,9 +918,9 @@ public function buildIndex(array $indexes, ?string $table = null): array
* Makes sure it will return the primary key
*
* @param Model|string $model Model instance of table name
- * @return string
+ * @return string|null
*/
- protected function _getPrimaryKey($model)
+ protected function _getPrimaryKey(Model|string $model): ?string
{
$schema = $this->describe($model);
foreach ($schema as $field => $props) {
@@ -884,16 +937,11 @@ protected function _getPrimaryKey($model)
* this returns false.
*
* @param mixed $source Unused
- * @return int Number of affected rows
+ * @return int|false Number of affected rows
*/
- public function lastAffected($source = null)
+ public function lastAffected(mixed $source = null): int|false
{
- $affected = parent::lastAffected();
- if ($affected === null && $this->_lastAffected !== false) {
- return $this->_lastAffected;
- }
-
- return $affected;
+ return parent::lastAffected();
}
/**
@@ -902,12 +950,15 @@ public function lastAffected($source = null)
* @param string $sql SQL statement
* @param array $params list of params to be bound to query (supported only in select)
* @param array $prepareOptions Options to be used in the prepare statement
- * @return mixed PDOStatement if query executes with no problem, true as the result of a successful, false on error
+ * @return PDOStatement|bool PDOStatement if query executes with no problem, true as the result of a successful, false on error
* query returning no rows, such as a CREATE statement, false otherwise
* @throws PDOException
*/
- protected function _execute($sql, $params = [], $prepareOptions = [])
- {
+ protected function _execute(
+ string $sql,
+ array $params = [],
+ array $prepareOptions = [],
+ ): PDOStatement|bool {
$this->_lastAffected = false;
$sql = trim($sql);
if (strncasecmp($sql, 'SELECT', 6) === 0 || preg_match('/^EXEC(?:UTE)?\s/mi', $sql) > 0) {
@@ -926,7 +977,9 @@ protected function _execute($sql, $params = [], $prepareOptions = [])
return true;
} catch (PDOException $e) {
+ $e = new PDOExceptionWithQueryString($e);
$e->queryString = $sql;
+
throw $e;
}
}
@@ -937,7 +990,7 @@ protected function _execute($sql, $params = [], $prepareOptions = [])
* @param Model|string $table Name of the table to drop
* @return string Drop table SQL statement
*/
- protected function _dropTable($table): string
+ protected function _dropTable(Model|string $table): string
{
return "IF OBJECT_ID('" . $this->fullTableName($table, false) . "', 'U') IS NOT NULL DROP TABLE " . $this->fullTableName($table) . ';';
}
@@ -947,7 +1000,7 @@ protected function _dropTable($table): string
*
* @return string The schema name
*/
- public function getSchemaName()
+ public function getSchemaName(): string
{
return $this->config['schema'] ?? 'dbo';
}
@@ -960,7 +1013,7 @@ public function getSchemaName()
* @param mixed $mode Lock mode
* @return string|null WITH (UPDLOCK) clause or null
*/
- public function getLockingHint($mode)
+ public function getLockingHint(mixed $mode): ?string
{
if ($mode !== true) {
return null;
diff --git a/src/Model/Datasource/DboSource.php b/src/Model/Datasource/DboSource.php
index e4f38591e3..9aca67cbe3 100644
--- a/src/Model/Datasource/DboSource.php
+++ b/src/Model/Datasource/DboSource.php
@@ -104,23 +104,23 @@ class DboSource extends DataSource
/**
* String to hold how many rows were affected by the last SQL operation.
*
- * @var string|null
+ * @var int|false|null
*/
- public ?string $affected = null;
+ public int|false|null $affected = null;
/**
* Number of rows in current resultset
*
- * @var int|null
+ * @var int|false|null
*/
- public ?int $numRows = null;
+ public int|false|null $numRows = null;
/**
* Time the last query took
*
- * @var int|null
+ * @var int|false|null
*/
- public ?int $took = null;
+ public int|false|null $took = null;
/**
* Result
@@ -284,7 +284,7 @@ class DboSource extends DataSource
* @param bool $autoConnect Whether or not the datasource should automatically connect.
* @throws MissingConnectionException when a connection cannot be made.
*/
- public function __construct($config = null, $autoConnect = true)
+ public function __construct(array $config = [], bool $autoConnect = true)
{
if (!isset($config['prefix'])) {
$config['prefix'] = '';
@@ -308,7 +308,7 @@ public function __construct($config = null, $autoConnect = true)
*
* @return bool
*/
- public function connect()
+ public function connect(): bool
{
// This method is implemented in subclasses
return $this->connected;
@@ -320,7 +320,7 @@ public function connect()
* @param array $config An array defining the new configuration settings
* @return bool True on success, false on failure
*/
- public function reconnect($config = [])
+ public function reconnect(array $config = []): bool
{
$this->disconnect();
$this->setConfig($config);
@@ -334,7 +334,7 @@ public function reconnect($config = [])
*
* @return bool Always true
*/
- public function disconnect()
+ public function disconnect(): bool
{
if ($this->_result instanceof PDOStatement) {
$this->_result->closeCursor();
@@ -351,7 +351,7 @@ public function disconnect()
*
* @return PDO
*/
- public function getConnection()
+ public function getConnection(): PDO
{
return $this->_connection;
}
@@ -369,8 +369,11 @@ public function getVersion(): string
/**
* @inheritDoc
*/
- public function value($data, ?string $column = null, bool $null = true): array|string
- {
+ public function value(
+ mixed $data,
+ ?string $column = null,
+ bool $null = true,
+ ): array|string {
if (is_array($data) && !empty($data)) {
return array_map(
[&$this, 'value'],
@@ -444,7 +447,7 @@ public function value($data, ?string $column = null, bool $null = true): array|s
* @param string $identifier A SQL expression to be used as an identifier
* @return stdClass An object representing a database identifier to be used in a query
*/
- public function identifier($identifier)
+ public function identifier(string $identifier): stdClass
{
$obj = new stdClass();
$obj->type = 'identifier';
@@ -460,7 +463,7 @@ public function identifier($identifier)
* @param string $expression An arbitrary SQL expression to be inserted into a query.
* @return stdClass An object representing a database expression to be used in a query
*/
- public function expression($expression)
+ public function expression(string $expression): stdClass
{
$obj = new stdClass();
$obj->type = 'expression';
@@ -474,10 +477,12 @@ public function expression($expression)
*
* @param string $sql SQL statement
* @param array $params Additional options for the query.
- * @return mixed Resource or object representing the result set, or false on failure
+ * @return PDOStatement|bool|null Resource or object representing the result set, or false on failure
*/
- public function rawQuery($sql, $params = [])
- {
+ public function rawQuery(
+ string $sql,
+ array $params = [],
+ ): PDOStatement|bool|null {
$this->took = $this->numRows = null;
return $this->execute($sql, [], $params);
@@ -495,10 +500,13 @@ public function rawQuery($sql, $params = [])
* @param string $sql SQL statement
* @param array $options The options for executing the query.
* @param array $params values to be bound to the query.
- * @return mixed Resource or object representing the result set, or false on failure
+ * @return PDOStatement|bool|null Resource or object representing the result set, or false on failure
*/
- public function execute($sql, $options = [], $params = [])
- {
+ public function execute(
+ string $sql,
+ array $options = [],
+ array $params = [],
+ ): PDOStatement|bool|null {
$options += ['log' => $this->fullDebug];
$t = microtime(true);
@@ -519,19 +527,22 @@ public function execute($sql, $options = [], $params = [])
* @param string $sql SQL statement
* @param array $params list of params to be bound to query
* @param array $prepareOptions Options to be used in the prepare statement
- * @return mixed PDOStatement if query executes with no problem, true as the result of a successful, false on error
+ * @return PDOStatement|bool PDOStatement if query executes with no problem, true as the result of a successful, false on error
* query returning no rows, such as a CREATE statement, false otherwise
* @throws PDOException
*/
- protected function _execute($sql, $params = [], $prepareOptions = [])
- {
+ protected function _execute(
+ string $sql,
+ array $params = [],
+ array $prepareOptions = [],
+ ): PDOStatement|bool {
$sql = trim($sql);
if (preg_match('/^(?:CREATE|ALTER|DROP)\s+(?:UNIQUE\s+)?(?:TABLE|INDEX)/i', $sql)) {
$statements = array_filter(explode(';', $sql));
if (count($statements) > 1) {
$result = array_map([$this, '_execute'], $statements);
- return array_search(false, $result) === false;
+ return !in_array(false, $result, true);
}
}
@@ -566,10 +577,10 @@ protected function _execute($sql, $params = [], $prepareOptions = [])
/**
* Returns a formatted error message from previous database operation.
*
- * @param PDOStatement $query the query to extract the error from if any
- * @return string Error message with error number
+ * @param PDOStatement|null $query the query to extract the error from if any
+ * @return string|null Error message with error number
*/
- public function lastError(?PDOStatement $query = null)
+ public function lastError(?PDOStatement $query = null): ?string
{
if ($query) {
$error = $query->errorInfo();
@@ -588,9 +599,9 @@ public function lastError(?PDOStatement $query = null)
* this returns false.
*
* @param mixed $source The source to check.
- * @return int Number of affected rows
+ * @return int|false Number of affected rows
*/
- public function lastAffected($source = null)
+ public function lastAffected(mixed $source = null): int|false
{
if ($this->hasResult()) {
return $this->_result->rowCount();
@@ -604,9 +615,9 @@ public function lastAffected($source = null)
* this returns false.
*
* @param mixed $source Not used
- * @return int Number of rows in resultset
+ * @return int|false Number of rows in resultset
*/
- public function lastNumRows($source = null)
+ public function lastNumRows(mixed $source = null): int|false
{
return $this->lastAffected();
}
@@ -617,7 +628,7 @@ public function lastNumRows($source = null)
* @param mixed ...$args Query arguments
* @return mixed Result resource identifier.
*/
- public function query(...$args)
+ public function query(mixed ...$args): mixed
{
$fields = null;
$order = null;
@@ -685,16 +696,17 @@ public function query(...$args)
return $args[2]->find('first', compact('conditions', 'fields', 'order', 'recursive'));
}
- if (isset($args[1]) && $args[1] === true) {
+
+ if (!isset($args[1])) {
+ return null;
+ }
+
+ if ($args[1] === true) {
return $this->fetchAll($args[0], true);
- } elseif (isset($args[1]) && !is_array($args[1])) {
+ } elseif (!is_array($args[1])) {
return $this->fetchAll($args[0], false);
- } elseif (isset($args[1]) && is_array($args[1])) {
- if (isset($args[2])) {
- $cache = $args[2];
- } else {
- $cache = true;
- }
+ } else {
+ $cache = $args[2] ?? true;
return $this->fetchAll($args[0], $args[1], ['cache' => $cache]);
}
@@ -706,7 +718,7 @@ public function query(...$args)
* @param PDOStatement $results The results to format.
* @return void
*/
- public function resultSet($results)
+ public function resultSet(PDOStatement $results): void
{
// This method is implemented in subclasses
}
@@ -714,10 +726,10 @@ public function resultSet($results)
/**
* Returns a row from current resultset as an array
*
- * @param string $sql Some SQL to be executed.
- * @return array The fetched row as an array
+ * @param PDOStatement|string|null $sql Some SQL to be executed.
+ * @return array|false|null The fetched row as an array
*/
- public function fetchRow($sql = null)
+ public function fetchRow(PDOStatement|string|null $sql = null): array|false|null
{
if (is_string($sql) && strlen($sql) > 5 && !$this->execute($sql)) {
return null;
@@ -751,11 +763,14 @@ public function fetchRow($sql = null)
* @param string $sql SQL statement
* @param array|bool $params Either parameters to be bound as values for the SQL statement,
* or a boolean to control query caching.
- * @param array $options additional options for the query.
+ * @param array|string $options additional options for the query.
* @return array|bool Array of resultset rows, or false if no rows matched
*/
- public function fetchAll($sql, $params = [], $options = [])
- {
+ public function fetchAll(
+ string $sql,
+ array|bool $params = [],
+ array|string $options = [],
+ ): array|bool {
if (is_string($options)) {
$options = ['modelName' => $options];
}
@@ -802,9 +817,9 @@ public function fetchAll($sql, $params = [], $options = [])
/**
* Fetches the next row from the current result set
*
- * @return bool
+ * @return array|false
*/
- public function fetchResult()
+ public function fetchResult(): array|false
{
return false;
}
@@ -815,7 +830,7 @@ public function fetchResult()
* @param array &$result Reference to the fetched row
* @return void
*/
- public function fetchVirtualField(&$result)
+ public function fetchVirtualField(array &$result): void
{
if (isset($result[0]) && is_array($result[0])) {
foreach ($result[0] as $field => $value) {
@@ -829,6 +844,7 @@ public function fetchVirtualField(&$result)
return;
}
+ /** @var Model $model */
$model = ClassRegistry::getObject($alias);
if ($model->isVirtualField($virtual)) {
@@ -849,7 +865,7 @@ public function fetchVirtualField(&$result)
* @param string $sql The SQL query.
* @return mixed Value of field read, or false if not found.
*/
- public function field($name, $sql)
+ public function field(string $name, string $sql): mixed
{
$data = $this->fetchRow($sql);
if (empty($data[$name])) {
@@ -865,7 +881,7 @@ public function field($name, $sql)
*
* @return void
*/
- public function flushMethodCache()
+ public function flushMethodCache(): void
{
$this->_methodCacheChange = true;
static::$methodCache = [];
@@ -883,7 +899,7 @@ public function flushMethodCache()
* @param mixed $value The value to cache into memory.
* @return mixed Either null on failure, or the value if its set.
*/
- public function cacheMethod($method, $key, $value = null)
+ public function cacheMethod(string $method, string $key, mixed $value = null): mixed
{
if ($this->cacheMethods === false) {
return $value;
@@ -898,8 +914,9 @@ public function cacheMethod($method, $key, $value = null)
return $value;
}
$this->_methodCacheChange = true;
+ static::$methodCache[$method][$key] = $value;
- return static::$methodCache[$method][$key] = $value;
+ return static::$methodCache[$method][$key];
}
/**
@@ -942,8 +959,11 @@ public function cacheMethod($method, $key, $value = null)
* @param mixed $value The value to cache into memory.
* @return bool Whether or not to cache
*/
- public function cacheMethodFilter($method, $key, $value)
- {
+ public function cacheMethodFilter(
+ string $method,
+ string $key,
+ mixed $value,
+ ): bool {
return true;
}
@@ -958,7 +978,7 @@ public function cacheMethodFilter($method, $key, $value)
* @see http://php.net/manual/en/function.hash-algos.php
* @see http://softwareengineering.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed
*/
- public function cacheMethodHasher($value)
+ public function cacheMethodHasher(string $value): string
{
return md5($value);
}
@@ -973,9 +993,9 @@ public function cacheMethodHasher($value)
*
* @param mixed $data Either a string with a column to quote. An array of columns to quote or an
* object from DboSource::expression() or DboSource::identifier()
- * @return string SQL field
+ * @return array|string SQL field
*/
- public function name($data)
+ public function name(mixed $data): array|string
{
if (is_object($data) && isset($data->type)) {
return $data->value;
@@ -1021,7 +1041,7 @@ public function name($data)
$matches[1] . '(' . $this->name($matches[2]) . ')',
);
}
- if (preg_match('/^([\w-]+(\.[\w-]+|\(.*\))*)\s+' . preg_quote($this->alias) . '\s*([\w-]+)$/i', $data, $matches)) {
+ if (preg_match('/^([\w-]+(\.[\w-]+|\(.*\))*)\s+' . preg_quote($this->alias, '/') . '\s*([\w-]+)$/i', $data, $matches)) {
return $this->cacheMethod(
__FUNCTION__,
$cacheKey,
@@ -1044,7 +1064,7 @@ public function name($data)
*
* @return bool True if the database is connected, else false
*/
- public function isConnected()
+ public function isConnected(): bool
{
if ($this->_connection === null) {
$connected = false;
@@ -1065,7 +1085,7 @@ public function isConnected()
*
* @return bool True if the result is valid else false
*/
- public function hasResult()
+ public function hasResult(): bool
{
return $this->_result instanceof PDOStatement;
}
@@ -1075,12 +1095,16 @@ public function hasResult()
*
* @param bool $sorted Get the queries sorted by time taken, defaults to false.
* @param bool $clear If True the existing log will cleared.
- * @return array Array of queries run as an array
+ * @return array{
+ * log: array|null,
+ * count: int,
+ * time: int|null
+ * } Array of queries run as an array
*/
- public function getLog($sorted = false, $clear = true)
+ public function getLog(bool $sorted = false, bool $clear = true): array
{
if ($sorted) {
- $log = sortByKey($this->_queriesLog, 'took', 'desc', SORT_NUMERIC);
+ $log = sortByKey($this->_queriesLog, 'took', 'desc');
} else {
$log = $this->_queriesLog;
}
@@ -1098,7 +1122,7 @@ public function getLog($sorted = false, $clear = true)
* @param bool $sorted Get the queries sorted by time taken, defaults to false.
* @return void
*/
- public function showLog($sorted = false)
+ public function showLog(bool $sorted = false): void
{
$log = $this->getLog($sorted, false);
if (empty($log['log'])) {
@@ -1106,9 +1130,9 @@ public function showLog($sorted = false)
}
if (PHP_SAPI !== 'cli') {
$controller = null;
- $View = new View($controller, false);
- $View->set('sqlLogs', [$this->configKeyName => $log]);
- echo $View->element('sql_dump', ['_forced_from_dbo_' => true]);
+ $view = new View($controller);
+ $view->set('sqlLogs', [$this->configKeyName => $log]);
+ echo $view->element('sql_dump', ['_forced_from_dbo_' => true]);
} else {
foreach ($log['log'] as $k => $i) {
print ($k + 1) . ". {$i['query']}\n";
@@ -1123,10 +1147,10 @@ public function showLog($sorted = false)
* @param array $params Values binded to the query (prepared statements)
* @return void
*/
- public function logQuery($sql, $params = [])
+ public function logQuery(string $sql, array $params = []): void
{
$this->_queriesCnt++;
- $this->_queriesTime += $this->took;
+ $this->_queriesTime += (int)$this->took;
$this->_queriesLog[] = [
'query' => $sql,
'params' => $params,
@@ -1142,18 +1166,21 @@ public function logQuery($sql, $params = [])
/**
* Gets full table name including prefix
*
- * @param Model|string $model Either a Model object or a string table name.
+ * @param stdClass|Model|string|null $model Either a Model object or a string table name.
* @param bool $quote Whether you want the table name quoted.
* @param bool $schema Whether you want the schema name included.
* @return string Full quoted table name
*/
- public function fullTableName($model, $quote = true, $schema = true)
- {
+ public function fullTableName(
+ stdClass|Model|string|null $model,
+ bool $quote = true,
+ bool $schema = true,
+ ): string {
if (is_object($model)) {
$schemaName = $model->schemaName;
$table = $model->tablePrefix . $model->table;
} elseif (!empty($this->config['prefix']) && !str_starts_with($model, $this->config['prefix'])) {
- $table = $this->config['prefix'] . strval($model);
+ $table = $this->config['prefix'] . $model;
} else {
$table = strval($model);
}
@@ -1187,14 +1214,17 @@ public function fullTableName($model, $quote = true, $schema = true)
* Creates new records in the database.
*
* @param Model $model Model object that the record is for.
- * @param array $fields An array of field names to insert. If null, $Model->data will be
+ * @param array|null $fields An array of field names to insert. If null, $Model->data will be
* used to generate field names.
- * @param array $values An array of values with keys matching the fields. If null, $Model->data will
+ * @param array|null $values An array of values with keys matching the fields. If null, $Model->data will
* be used to generate values.
* @return bool Success
*/
- public function create(Model $model, $fields = null, $values = null)
- {
+ public function create(
+ Model $model,
+ ?array $fields = null,
+ ?array $values = null,
+ ): bool {
$id = null;
if (!$fields) {
@@ -1204,10 +1234,12 @@ public function create(Model $model, $fields = null, $values = null)
}
$count = count($fields);
+ $valueInsert = [];
+ $fieldInsert = [];
for ($i = 0; $i < $count; $i++) {
$schema = $model->schema();
- $valueInsert[] = $this->value($values[$i], $model->getColumnType($fields[$i]), $schema[$fields[$i]]['null'] ?? true);
- $fieldInsert[] = $this->name($fields[$i]);
+ $valueInsert[] = (string)$this->value($values[$i], $model->getColumnType($fields[$i]), $schema[$fields[$i]]['null'] ?? true);
+ $fieldInsert[] = (string)$this->name($fields[$i]);
if ($fields[$i] === $model->primaryKey) {
$id = $values[$i];
}
@@ -1241,11 +1273,14 @@ public function create(Model $model, $fields = null, $values = null)
*
* @param Model $model A Model object that the query is for.
* @param array $queryData An array of queryData information containing keys similar to Model::find().
- * @param int $recursive Number of levels of association
- * @return mixed boolean false on error/failure. An array of results on success.
- */
- public function read(Model $model, $queryData = [], $recursive = null)
- {
+ * @param int|null $recursive Number of levels of association
+ * @return array|false boolean false on error/failure. An array of results on success.
+ */
+ public function read(
+ Model $model,
+ array $queryData = [],
+ ?int $recursive = null,
+ ): array|false {
$queryData = $this->_scrubQueryData($queryData);
$array = ['callbacks' => $queryData['callbacks']];
@@ -1330,7 +1365,7 @@ public function read(Model $model, $queryData = [], $recursive = null)
if ($model->recursive > -1) {
$joined = [];
if (isset($queryData['joins'][0]['alias'])) {
- $joined[$model->alias] = (array)Hash::extract($queryData['joins'], '{n}.alias');
+ $joined[$model->alias] = Hash::extract($queryData['joins'], '{n}.alias');
}
foreach ($associations as $type) {
@@ -1374,13 +1409,16 @@ public function read(Model $model, $queryData = [], $recursive = null)
*
* The primary model is always excluded, because the filtering is later done by Model::_filterResults().
*
- * @param array &$resultSet Reference of resultset to be filtered.
+ * @param mixed &$resultSet Reference of resultset to be filtered.
* @param Model $model Instance of model to operate against.
* @param array $filtered List of classes already filtered, to be skipped.
* @return array Array of results that have been filtered through $Model->afterFind.
*/
- protected function _filterResults(&$resultSet, Model $model, $filtered = [])
- {
+ protected function _filterResults(
+ mixed &$resultSet,
+ Model $model,
+ array $filtered = [],
+ ): array {
if (!is_array($resultSet)) {
return [];
}
@@ -1420,13 +1458,16 @@ protected function _filterResults(&$resultSet, Model $model, $filtered = [])
* Similar to DboSource::_filterResults(), but this filters only specified models.
* The primary model can not be specified, because this call DboSource::_filterResults() internally.
*
- * @param array &$resultSet Reference of resultset to be filtered.
+ * @param mixed &$resultSet Reference of resultset to be filtered.
* @param Model $model Instance of model to operate against.
* @param array $toBeFiltered List of classes to be filtered.
* @return array Array of results that have been filtered through $Model->afterFind.
*/
- protected function _filterResultsInclusive(&$resultSet, Model $model, $toBeFiltered = [])
- {
+ protected function _filterResultsInclusive(
+ mixed &$resultSet,
+ Model $model,
+ array $toBeFiltered = [],
+ ): array {
$exclude = [];
if (is_array($resultSet)) {
@@ -1457,14 +1498,24 @@ protected function _filterResultsInclusive(&$resultSet, Model $model, $toBeFilte
* @param array $assocData Association data.
* @param array &$queryData An array of queryData information containing keys similar to Model::find().
* @param bool $external Whether or not the association query is on an external datasource.
- * @param array &$resultSet Existing results.
+ * @param array|false &$resultSet Existing results.
* @param int $recursive Number of levels of association.
* @param array $stack A list with joined models.
* @return void
* @throws CakeException when results cannot be created.
*/
- public function queryAssociation(Model $model, Model $LinkModel, $type, $association, $assocData, &$queryData, $external, &$resultSet, $recursive, $stack): void
- {
+ public function queryAssociation(
+ Model $model,
+ Model $LinkModel,
+ string $type,
+ string $association,
+ array $assocData,
+ array &$queryData,
+ bool $external,
+ array|false &$resultSet,
+ int $recursive,
+ array $stack,
+ ): void {
if (isset($stack['_joined'])) {
$joined = $stack['_joined'];
unset($stack['_joined']);
@@ -1495,7 +1546,7 @@ public function queryAssociation(Model $model, Model $LinkModel, $type, $associa
}
// Recursively query associations
- if ($recursive > 0 && !empty($assocResultSet) && is_array($assocResultSet)) {
+ if ($recursive > 0 && is_array($assocResultSet) && !empty($assocResultSet)) {
foreach ($LinkModel->associations() as $type1) {
foreach ($LinkModel->{$type1} as $assoc1 => $assocData1) {
$DeepModel = $LinkModel->{$assoc1};
@@ -1543,12 +1594,17 @@ public function queryAssociation(Model $model, Model $LinkModel, $type, $associa
if ($queryData['callbacks'] === true || $queryData['callbacks'] === 'after') {
$this->_filterResultsInclusive($assocResultSet, $model, [$association, $with]);
}
+ } else {
+ $foreignKey = null;
+ $with = null;
+ $habtmFieldsCount = 0;
}
$modelAlias = $model->alias;
$primaryKey = $model->primaryKey;
$selfJoin = ($model->name === $LinkModel->name);
+ $prefetched = false;
foreach ($resultSet as &$row) {
if ($type === 'hasOne' || $type === 'belongsTo' || $type === 'hasMany') {
$assocResultSet = [];
@@ -1620,7 +1676,7 @@ public function queryAssociation(Model $model, Model $LinkModel, $type, $associa
}
if ($type !== 'hasAndBelongsToMany' && isset($row[$association]) && !$prefetched && !$LinkModel->useConsistentAfterFind) {
- $row[$association] = $LinkModel->afterFind($row[$association], false);
+ $row[$association] = $LinkModel->afterFind($row[$association]);
}
} else {
$tempArray[0][$association] = false;
@@ -1637,11 +1693,14 @@ public function queryAssociation(Model $model, Model $LinkModel, $type, $associa
* @param Model $model Primary model object.
* @param string $query Association query template.
* @param array $ids Array of IDs of associated records.
- * @return array Association results.
+ * @return array|false Association results.
* @see DboSource::_fetchHasMany()
*/
- public function fetchAssociated(Model $model, $query, $ids)
- {
+ public function fetchAssociated(
+ Model $model,
+ string $query,
+ array $ids,
+ ): array|false {
return $this->_fetchHasMany($model, $query, $ids);
}
@@ -1651,10 +1710,13 @@ public function fetchAssociated(Model $model, $query, $ids)
* @param Model $model Primary model object.
* @param string $query Association query template.
* @param array $ids Array of IDs of associated records.
- * @return array Association results.
+ * @return array|false Association results.
*/
- protected function _fetchHasMany(Model $model, $query, $ids)
- {
+ protected function _fetchHasMany(
+ Model $model,
+ string $query,
+ array $ids,
+ ): array|false {
$ids = array_unique($ids);
if (count($ids) > 1) {
@@ -1674,8 +1736,12 @@ protected function _fetchHasMany(Model $model, $query, $ids)
* @param string $association Association name.
* @return array Association results.
*/
- protected function _fetchHasAndBelongsToMany(Model $model, $query, $ids, $association)
- {
+ protected function _fetchHasAndBelongsToMany(
+ Model $model,
+ string $query,
+ array $ids,
+ string $association,
+ ): array {
$ids = array_unique($ids);
if (count($ids) > 1) {
@@ -1695,13 +1761,17 @@ protected function _fetchHasAndBelongsToMany(Model $model, $query, $ids, $associ
* Note: this function also deals with the formatting of the data.
*
* @param array &$resultSet Data to merge into.
- * @param array $assocResultSet Data to merge.
+ * @param array|false $assocResultSet Data to merge.
* @param string $association Name of Model being merged.
* @param Model $model Model being merged onto.
* @return void
*/
- protected function _mergeHasMany(&$resultSet, $assocResultSet, $association, Model $model): void
- {
+ protected function _mergeHasMany(
+ array &$resultSet,
+ array|false $assocResultSet,
+ string $association,
+ Model $model,
+ ): void {
$modelAlias = $model->alias;
$primaryKey = $model->primaryKey;
$foreignKey = $model->hasMany[$association]['foreignKey'];
@@ -1754,14 +1824,20 @@ protected function _mergeHasMany(&$resultSet, $assocResultSet, $association, Mod
* @param bool $selfJoin Whether or not this is a self join.
* @return void
*/
- protected function _mergeAssociation(&$data, &$merge, $association, $type, $selfJoin = false)
- {
+ protected function _mergeAssociation(
+ array &$data,
+ array &$merge,
+ string $association,
+ string $type,
+ bool $selfJoin = false,
+ ): void {
if (isset($merge[0]) && !isset($merge[0][$association])) {
$association = Inflector::pluralize($association);
}
$dataAssociation =& $data[$association];
+ $dataAssocTmp = [];
if ($type === 'belongsTo' || $type === 'hasOne') {
if (isset($merge[$association])) {
$dataAssociation = $merge[$association][0];
@@ -1821,7 +1897,7 @@ protected function _mergeAssociation(&$data, &$merge, $association, $type, $self
unset($insert[$association]);
}
- if (empty($dataAssociation) || (isset($dataAssociation) && !in_array($insert, $dataAssociation, true))) {
+ if (empty($dataAssociation) || !in_array($insert, $dataAssociation, true)) {
$dataAssociation[] = $insert;
}
}
@@ -1838,8 +1914,10 @@ protected function _mergeAssociation(&$data, &$merge, $association, $type, $self
* @param array $queryData An array of queryData information containing keys similar to Model::find().
* @return array Array containing SQL fields.
*/
- public function prepareFields(Model $model, $queryData)
- {
+ public function prepareFields(
+ Model $model,
+ array $queryData,
+ ): array {
if (empty($queryData['fields'])) {
$queryData['fields'] = $this->fields($model);
} elseif (!empty($model->hasMany) && $model->recursive > -1) {
@@ -1868,8 +1946,10 @@ public function prepareFields(Model $model, $queryData)
* @return string String containing an SQL statement.
* @see DboSource::buildStatement()
*/
- public function buildAssociationQuery(Model $model, $queryData)
- {
+ public function buildAssociationQuery(
+ Model $model,
+ array $queryData,
+ ): string {
$queryData = $this->_scrubQueryData($queryData);
return $this->buildStatement(
@@ -1897,17 +1977,24 @@ public function buildAssociationQuery(Model $model, $queryData)
*
* @param Model $model Primary Model object.
* @param Model|null $LinkModel Linked model object.
- * @param string $type Association type, one of the model association types ie. hasMany.
- * @param string $association Association name.
- * @param array $assocData Association data.
+ * @param string|null $type Association type, one of the model association types ie. hasMany.
+ * @param string|null $association Association name.
+ * @param array|null $assocData Association data.
* @param array &$queryData An array of queryData information containing keys similar to Model::find().
* @param bool $external Whether or not the association query is on an external datasource.
* @return mixed
* String representing a query.
* True, when $external is false and association $type is 'hasOne' or 'belongsTo'.
*/
- public function generateAssociationQuery(Model $model, $LinkModel, $type, $association, $assocData, &$queryData, $external)
- {
+ public function generateAssociationQuery(
+ Model $model,
+ ?Model $LinkModel,
+ ?string $type,
+ ?string $association,
+ ?array $assocData,
+ array &$queryData,
+ bool $external,
+ ): mixed {
$assocData = $this->_scrubQueryData($assocData);
$queryData = $this->_scrubQueryData($queryData);
@@ -2010,7 +2097,7 @@ public function generateAssociationQuery(Model $model, $LinkModel, $type, $assoc
$joinFields = [];
$joinAssoc = null;
- if (isset($assocData['with']) && !empty($assocData['with'])) {
+ if (!empty($assocData['with'])) {
$joinKeys = [$assocData['foreignKey'], $assocData['associationForeignKey']];
[$with, $joinFields] = $model->joinModel($assocData['with'], $joinKeys);
@@ -2061,11 +2148,17 @@ public function generateAssociationQuery(Model $model, $LinkModel, $type, $assoc
* @param Model $LinkModel Linked model object.
* @param string $association Association name.
* @param array $assocData Association data.
- * @param string $association2 HABTM association name.
+ * @param string|null $association2 HABTM association name.
* @return array Conditions array defining the constraint between $Model and $LinkModel.
*/
- public function getConstraint($type, Model $model, Model $LinkModel, $association, $assocData, $association2 = null)
- {
+ public function getConstraint(
+ string $type,
+ Model $model,
+ Model $LinkModel,
+ string $association,
+ array $assocData,
+ ?string $association2 = null,
+ ): array {
$assocData += ['external' => false];
if (empty($assocData['foreignKey'])) {
@@ -2117,7 +2210,7 @@ public function getConstraint($type, Model $model, Model $LinkModel, $associatio
* @see DboSource::renderJoinStatement()
* @see DboSource::buildStatement()
*/
- public function buildJoinStatement($join)
+ public function buildJoinStatement(array $join): string
{
$data = array_merge([
'type' => null,
@@ -2142,12 +2235,24 @@ public function buildJoinStatement($join)
/**
* Builds and generates an SQL statement from an array. Handles final clean-up before conversion.
*
- * @param array $query An array defining an SQL query.
+ * @param array{
+ * conditions?: array,
+ * fields?: array|null,
+ * table?: string|null,
+ * alias?: string|null,
+ * order?: string|null,
+ * limit?: string|null,
+ * joins?: array,
+ * group?: string|null,
+ * offset?: string|null,
+ * having?: string|null,
+ * lock?: string|null
+ * } $query An array defining an SQL query.
* @param Model $model The model object which initiated the query.
* @return string An executable SQL statement.
* @see DboSource::renderStatement()
*/
- public function buildStatement($query, Model $model)
+ public function buildStatement(array $query, Model $model): string
{
$query = array_merge($this->_queryDefaults, $query);
@@ -2163,7 +2268,7 @@ public function buildStatement($query, Model $model)
return $this->renderStatement('select', [
'conditions' => $this->conditions($query['conditions'], true, true, $model),
'fields' => implode(', ', $query['fields']),
- 'table' => $query['table'],
+ 'table' => (string)$query['table'],
'alias' => $this->alias . $this->name($query['alias']),
'order' => $this->order($query['order'], 'ASC', $model),
'limit' => $this->limit($query['limit'], $query['offset']),
@@ -2180,9 +2285,9 @@ public function buildStatement($query, Model $model)
* @param array $data The data to generate a join statement for.
* @return string
*/
- public function renderJoinStatement($data)
+ public function renderJoinStatement(array $data): string
{
- //Fixed deprecation notice in PHP8.1 - fallback to empty string
+ // Fixed deprecation notice in PHP8.1 - fallback to empty string
if (strtoupper($data['type'] ?? '') === 'CROSS' || empty($data['conditions'])) {
return "{$data['type']} JOIN {$data['table']} {$data['alias']}";
}
@@ -2194,19 +2299,56 @@ public function renderJoinStatement($data)
* Renders a final SQL statement by putting together the component parts in the correct order
*
* @param string $type type of query being run. e.g select, create, update, delete, schema, alter.
- * @param array $data Array of data to insert into the query.
+ * @param array{
+ * fields: string|null,
+ * table: string|null,
+ * alias: string|null,
+ * joins?: string|null,
+ * conditions?: string|null,
+ * group?: string|null,
+ * having?: string|null,
+ * order?: string|null,
+ * limit?: string|null,
+ * lock?: string|null
+ * }|array{
+ * fields: string|null,
+ * table: string|null,
+ * values?: string|null
+ * }|array{
+ * fields: string|null,
+ * table: string|null,
+ * alias: string|null,
+ * joins?: string|null,
+ * conditions?: string|null
+ * }|array{
+ * table: string|null,
+ * columns?: mixed,
+ * indexes?: mixed,
+ * tableParameters?: mixed
+ * } $data Array of data to insert into the query.
* @return string|null Rendered SQL expression to be run, otherwise null.
*/
- public function renderStatement($type, $data)
+ public function renderStatement(string $type, array $data): ?string
{
- extract($data);
+ $fields = $data['fields'] ?? '';
+ $table = $data['table'] ?? '';
+ $alias = $data['alias'] ?? '';
+ $joins = $data['joins'] ?? '';
+ $conditions = $data['conditions'] ?? '';
+ $group = $data['group'] ?? '';
+ $having = $data['having'] ?? '';
+ $order = $data['order'] ?? '';
+ $limit = $data['limit'] ?? '';
+ $lock = $data['lock'] ?? '';
+ $values = $data['values'] ?? '';
+ $columns = $data['columns'] ?? [];
+ $indexes = $data['indexes'] ?? [];
+ $tableParameters = $data['tableParameters'] ?? [];
+
$aliases = null;
switch (strtolower($type)) {
case 'select':
- $having = !empty($having) ? " $having" : '';
- $lock = !empty($lock) ? " $lock" : '';
-
return trim("SELECT {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$group}{$having} {$order} {$limit}{$lock}");
case 'create':
return "INSERT INTO {$table} ({$fields}) VALUES ({$values})";
@@ -2238,17 +2380,21 @@ public function renderStatement($type, $data)
case 'alter':
return null;
}
+
+ return null;
}
/**
* Merges a mixed set of string/array conditions.
*
- * @param mixed $query The query to merge conditions for.
- * @param mixed $assoc The association names.
- * @return array
+ * @param array|string|null $query The query to merge conditions for.
+ * @param array|string|null $assoc The association names.
+ * @return array|string|null
*/
- protected function _mergeConditions($query, $assoc)
- {
+ protected function _mergeConditions(
+ array|string|null $query,
+ array|string|null $assoc,
+ ): array|string|null {
if (empty($assoc)) {
return $query;
}
@@ -2276,13 +2422,17 @@ protected function _mergeConditions($query, $assoc)
* For databases that do not support aliases in UPDATE queries.
*
* @param Model $model The model to update.
- * @param array $fields The fields to update
- * @param array $values The values fo the fields.
+ * @param array|null $fields The fields to update
+ * @param array|null $values The values fo the fields.
* @param mixed $conditions The conditions for the update. When non-empty $values will not be quoted.
* @return bool Success
*/
- public function update(Model $model, $fields = [], $values = null, $conditions = null)
- {
+ public function update(
+ Model $model,
+ ?array $fields = [],
+ ?array $values = null,
+ mixed $conditions = null,
+ ): bool {
if (!$values) {
$combined = $fields;
} else {
@@ -2318,8 +2468,12 @@ public function update(Model $model, $fields = [], $values = null, $conditions =
* @param bool $alias Include the model alias in the field name
* @return array Fields and values, quoted and prepared
*/
- protected function _prepareUpdateFields(Model $model, $fields, $quoteValues = true, $alias = false)
- {
+ protected function _prepareUpdateFields(
+ Model $model,
+ array $fields,
+ bool $quoteValues = true,
+ bool $alias = false,
+ ): array {
$quotedAlias = $this->startQuote . $model->alias . $this->endQuote;
$schema = $model->schema();
@@ -2370,8 +2524,10 @@ protected function _prepareUpdateFields(Model $model, $fields, $quoteValues = tr
* @param mixed $conditions The conditions to use. If empty the model's primary key will be used.
* @return bool Success
*/
- public function delete(Model $model, $conditions = null)
- {
+ public function delete(
+ Model $model,
+ mixed $conditions = null,
+ ): bool {
$alias = $joins = null;
$table = $this->fullTableName($model);
$conditions = $this->_matchRecords($model, $conditions);
@@ -2395,14 +2551,16 @@ public function delete(Model $model, $conditions = null)
*
* @param Model $model The model to find matching records for.
* @param mixed $conditions The conditions to match against.
- * @return array|false List of record IDs
+ * @return string|false List of record IDs
*/
- protected function _matchRecords(Model $model, $conditions = null)
- {
+ protected function _matchRecords(
+ Model $model,
+ mixed $conditions = null,
+ ): string|false {
if ($conditions === true) {
- $conditions = $this->conditions(true);
+ $_conditions = $this->conditions(true);
} elseif ($conditions === null) {
- $conditions = $this->conditions($this->defaultConditions($model, $conditions, false), true, true, $model);
+ $_conditions = $this->conditions($this->defaultConditions($model, $conditions, false), true, true, $model);
} else {
$noJoin = true;
foreach ($conditions as $field => $value) {
@@ -2433,12 +2591,12 @@ protected function _matchRecords(Model $model, $conditions = null)
return false;
}
- $conditions = $this->conditions([
+ $_conditions = $this->conditions([
$model->primaryKey => Hash::extract($idList, "{n}.{$model->alias}.{$model->primaryKey}"),
]);
}
- return $conditions;
+ return $_conditions;
}
/**
@@ -2447,7 +2605,7 @@ protected function _matchRecords(Model $model, $conditions = null)
* @param Model $model The model to get joins for.2
* @return array
*/
- protected function _getJoins(Model $model)
+ protected function _getJoins(Model $model): array
{
$join = [];
$joins = array_merge($model->getAssociated('hasOne'), $model->getAssociated('belongsTo'));
@@ -2486,11 +2644,14 @@ protected function _getJoins(Model $model)
*
* @param Model $model The model to get a calculated field for.
* @param string $func Lowercase name of SQL function, i.e. 'count' or 'max'
- * @param array $params Function parameters (any values must be quoted manually)
+ * @param array|string $params Function parameters (any values must be quoted manually)
* @return string An SQL calculation function
*/
- public function calculate(Model $model, $func, $params = []): string
- {
+ public function calculate(
+ Model $model,
+ string $func,
+ array|string $params = [],
+ ): string {
$params = (array)$params;
switch (strtolower($func)) {
@@ -2530,10 +2691,11 @@ public function calculate(Model $model, $func, $params = []): string
* primary key, where applicable.
*
* @param Model|string $table A string or model class representing the table to be truncated
- * @return bool SQL TRUNCATE TABLE statement, false if not applicable.
+ * @return PDOStatement|bool|null SQL TRUNCATE TABLE statement, false if not applicable.
*/
- public function truncate(Model|string $table)
- {
+ public function truncate(
+ Model|string $table,
+ ): PDOStatement|bool|null {
return $this->execute('TRUNCATE TABLE ' . $this->fullTableName($table));
}
@@ -2554,7 +2716,7 @@ public function nestedTransactionSupported(): bool
* (i.e. if the database/model does not support transactions,
* or a transaction has not started).
*/
- public function begin()
+ public function begin(): bool
{
if ($this->_transactionStarted) {
if ($this->nestedTransactionSupported()) {
@@ -2570,8 +2732,9 @@ public function begin()
$this->took = $this->numRows = $this->affected = null;
$this->logQuery('BEGIN');
}
+ $this->_transactionStarted = $this->_connection->beginTransaction();
- return $this->_transactionStarted = $this->_connection->beginTransaction();
+ return $this->_transactionStarted;
}
/**
@@ -2579,7 +2742,7 @@ public function begin()
*
* @return bool
*/
- protected function _beginNested()
+ protected function _beginNested(): bool
{
$query = 'SAVEPOINT LEVEL' . ++$this->_transactionNesting;
if ($this->fullDebug) {
@@ -2598,7 +2761,7 @@ protected function _beginNested()
* (i.e. if the database/model does not support transactions,
* or a transaction has not started).
*/
- public function commit()
+ public function commit(): bool
{
if (!$this->_transactionStarted) {
return false;
@@ -2628,7 +2791,7 @@ public function commit()
*
* @return bool
*/
- protected function _commitNested()
+ protected function _commitNested(): bool
{
$query = 'RELEASE SAVEPOINT LEVEL' . $this->_transactionNesting--;
if ($this->fullDebug) {
@@ -2647,7 +2810,7 @@ protected function _commitNested()
* (i.e. if the database/model does not support transactions,
* or a transaction has not started).
*/
- public function rollback()
+ public function rollback(): bool
{
if (!$this->_transactionStarted) {
return false;
@@ -2677,7 +2840,7 @@ public function rollback()
*
* @return bool
*/
- protected function _rollbackNested()
+ protected function _rollbackNested(): bool
{
$query = 'ROLLBACK TO SAVEPOINT LEVEL' . $this->_transactionNesting--;
if ($this->fullDebug) {
@@ -2695,7 +2858,7 @@ protected function _rollbackNested()
* @param mixed $source The source to get an id for.
* @return mixed
*/
- public function lastInsertId($source = null)
+ public function lastInsertId(mixed $source = null): mixed
{
return $this->_connection->lastInsertId();
}
@@ -2706,16 +2869,19 @@ public function lastInsertId($source = null)
* were provided either null or false will be returned based on what was input.
*
* @param Model $model The model to get conditions for.
- * @param array|string|bool $conditions Array of conditions, conditions string, null or false. If an array of conditions,
+ * @param array|string|bool|null $conditions Array of conditions, conditions string, bool, null or false. If an array of conditions,
* or string conditions those conditions will be returned. With other values the model's existence will be checked.
* If the model doesn't exist a null or false will be returned depending on the input value.
* @param bool $useAlias Use model aliases rather than table names when generating conditions
- * @return mixed Either null, false, $conditions or an array of default conditions to use.
+ * @return array|string|bool|null Either null, false, $conditions or an array of default conditions to use.
* @see DboSource::update()
* @see DboSource::conditions()
*/
- public function defaultConditions(Model $model, $conditions, $useAlias = true)
- {
+ public function defaultConditions(
+ Model $model,
+ array|string|bool|null $conditions,
+ bool $useAlias = true,
+ ): array|string|bool|null {
if (!empty($conditions)) {
return $conditions;
}
@@ -2739,11 +2905,12 @@ public function defaultConditions(Model $model, $conditions, $useAlias = true)
*
* @param Model $model The model to get a key for.
* @param string $key The key field.
- * @param string $assoc The association name.
* @return string
*/
- public function resolveKey(Model $model, $key, $assoc = null)
- {
+ public function resolveKey(
+ Model $model,
+ string $key,
+ ): string {
if (str_contains('.', $key)) {
return $this->name($model->alias) . '.' . $this->name($key);
}
@@ -2754,10 +2921,10 @@ public function resolveKey(Model $model, $key, $assoc = null)
/**
* Private helper method to remove query metadata in given data array.
*
- * @param array $data The data to scrub.
+ * @param array|null $data The data to scrub.
* @return array
*/
- protected function _scrubQueryData($data)
+ protected function _scrubQueryData(?array $data): array
{
static $base = null;
if ($base === null) {
@@ -2778,8 +2945,11 @@ protected function _scrubQueryData($data)
* @param array $fields virtual fields to be used on query
* @return array
*/
- protected function _constructVirtualFields(Model $model, $alias, $fields)
- {
+ protected function _constructVirtualFields(
+ Model $model,
+ string $alias,
+ array $fields,
+ ): array {
$virtual = [];
foreach ($fields as $field) {
$virtualField = $this->name($alias . $this->virtualFieldSeparator . $field);
@@ -2799,13 +2969,17 @@ protected function _constructVirtualFields(Model $model, $alias, $fields)
* Generates the fields list of an SQL query.
*
* @param Model $model The model to get fields for.
- * @param string $alias Alias table name
+ * @param string|null $alias Alias table name
* @param mixed $fields The provided list of fields.
* @param bool $quote If false, returns fields array unquoted
* @return array
*/
- public function fields(Model $model, $alias = null, $fields = [], $quote = true)
- {
+ public function fields(
+ Model $model,
+ ?string $alias = null,
+ mixed $fields = [],
+ bool $quote = true,
+ ): array {
if (empty($alias)) {
$alias = $model->alias;
}
@@ -2893,15 +3067,15 @@ public function fields(Model $model, $alias = null, $fields = [], $quote = true)
}
$fields[$i] = $prepend . $fields[$i];
} elseif (preg_match('/\(([\.\w]+)\)/', $fields[$i], $field)) {
- if (isset($field[1])) {
- if (!str_contains($field[1], '.')) {
- $field[1] = $this->name($alias . '.' . $field[1]);
- } else {
- $field[0] = explode('.', $field[1]);
- if (!Hash::numeric($field[0])) {
- $field[0] = implode('.', array_map([&$this, 'name'], $field[0]));
- $fields[$i] = preg_replace('/\(' . $field[1] . '\)/', '(' . $field[0] . ')', $fields[$i], 1);
- }
+ if (!str_contains($field[1], '.')) {
+ $field[1] = $this->name($alias . '.' . $field[1]);
+ } else {
+ $field[0] = explode('.', $field[1]);
+ if (!Hash::numeric($field[0])) {
+ /** @var array $_field */
+ $_field = array_map([&$this, 'name'], $field[0]);
+ $field[0] = implode('.', $_field);
+ $fields[$i] = preg_replace('/\(' . preg_quote($field[1], '/') . '\)/', '(' . $field[0] . ')', $fields[$i], 1);
}
}
}
@@ -2926,12 +3100,16 @@ public function fields(Model $model, $alias = null, $fields = [], $quote = true)
* @param mixed $conditions Array or string of conditions, or any value.
* @param bool $quoteValues If true, values should be quoted
* @param bool $where If true, "WHERE " will be prepended to the return value
- * @param Model $model A reference to the Model instance making the query
- * @return string SQL fragment
+ * @param Model|null $model A reference to the Model instance making the query
+ * @return string|false SQL fragment
*/
- public function conditions($conditions, $quoteValues = true, $where = true, ?Model $model = null)
- {
- $clause = $out = '';
+ public function conditions(
+ mixed $conditions,
+ bool $quoteValues = true,
+ bool $where = true,
+ ?Model $model = null,
+ ): string|false {
+ $clause = '';
if ($where) {
$clause = ' WHERE ';
@@ -2969,15 +3147,18 @@ public function conditions($conditions, $quoteValues = true, $where = true, ?Mod
/**
* Creates a WHERE clause by parsing given conditions array. Used by DboSource::conditions().
*
- * @param array $conditions Array or string of conditions
+ * @param array $conditions Array or string of conditions
* @param bool $quoteValues If true, values should be quoted
- * @param Model $model A reference to the Model instance making the query
- * @return string SQL fragment
- */
- public function conditionKeysToString($conditions, $quoteValues = true, ?Model $model = null)
- {
+ * @param Model|null $model A reference to the Model instance making the query
+ * @return array SQL fragment
+ */
+ public function conditionKeysToString(
+ array $conditions,
+ bool $quoteValues = true,
+ ?Model $model = null,
+ ): array {
$out = [];
- $data = $columnType = null;
+ $data = $columnType = $valueInsert = null;
foreach ($conditions as $key => $value) {
$join = ' AND ';
@@ -3034,27 +3215,17 @@ public function conditionKeysToString($conditions, $quoteValues = true, ?Model $
}
}
} elseif (is_array($value) && !empty($value) && !$valueInsert) {
- $keys = array_keys($value);
- if ($keys === array_values($keys)) {
- if (count($value) === 1 && !preg_match('/\s+(?:NOT|IN|\!=)$/', $key)) {
- $data = $this->_quoteFields($key) . ' = (';
- if ($quoteValues) {
- if ($model !== null) {
- $columnType = $model->getColumnType($key);
- }
- $data .= implode(', ', $this->value($value, $columnType));
+ if (count($value) === 1 && !preg_match('/\s+(?:NOT|IN|\!=)$/', $key)) {
+ $data = $this->_quoteFields($key) . ' = (';
+ if ($quoteValues) {
+ if ($model !== null) {
+ $columnType = $model->getColumnType($key);
}
- $data .= ')';
- } else {
- $data = $this->_parseKey($key, $value, $model);
+ $data .= implode(', ', $this->value($value, $columnType));
}
+ $data .= ')';
} else {
- $ret = $this->conditionKeysToString($value, $quoteValues, $model);
- if (count($ret) > 1) {
- $data = '(' . implode(') AND (', $ret) . ')';
- } elseif (isset($ret[0])) {
- $data = $ret[0];
- }
+ $data = $this->_parseKey($key, $value, $model);
}
} elseif (is_numeric($key) && !empty($value)) {
$data = $this->_quoteFields($value);
@@ -3078,11 +3249,14 @@ public function conditionKeysToString($conditions, $quoteValues = true, ?Model $
*
* @param string $key An SQL key snippet containing a field and optional SQL operator
* @param mixed $value The value(s) to be inserted in the string
- * @param Model $model Model object initiating the query
+ * @param Model|null $model Model object initiating the query
* @return string
*/
- protected function _parseKey($key, $value, ?Model $model = null)
- {
+ protected function _parseKey(
+ string $key,
+ mixed $value,
+ ?Model $model = null,
+ ): string {
$operatorMatch = '/^(((' . implode(')|(', $this->_sqlOps);
$operatorMatch .= ')\\x20?)|<[>=]?(?![^>]+>)\\x20?|[>=!]{1,3}(?!<)\\x20?)/is';
$bound = (str_contains($key, '?') || (is_array($value) && str_contains($key, ':')));
@@ -3188,7 +3362,7 @@ protected function _parseKey($key, $value, ?Model $model = null)
* @param string $conditions The conditions to quote.
* @return string or false if no match
*/
- protected function _quoteFields($conditions)
+ protected function _quoteFields(string $conditions): string
{
$start = $end = null;
$original = $conditions;
@@ -3203,7 +3377,7 @@ protected function _quoteFields($conditions)
// Remove quotes and requote all the Model.field names.
$conditions = str_replace([$start, $end], '', $conditions);
$conditions = preg_replace_callback(
- '/(?:[\'\"][^\'\"\\\]*(?:\\\.[^\'\"\\\]*)*[\'\"])|([a-z0-9_][a-z0-9\\-_]*\\.[a-z0-9_][a-z0-9_\\-]*[a-z0-9_])|([a-z0-9_][a-z0-9_\\-]*)(?=->)/i',
+ '/[\'\"][^\'\"\\\]*(?:\\\.[^\'\"\\\]*)*[\'\"]|([a-z0-9_][a-z0-9\\-_]*\\.[a-z0-9_][a-z0-9_\\-]*[a-z0-9_])|([a-z0-9_][a-z0-9_\\-]*)(?=->)/i',
[&$this, '_quoteMatchedField'],
$conditions,
);
@@ -3223,10 +3397,10 @@ protected function _quoteFields($conditions)
/**
* Auxiliary function to quote matches `Model.fields` from a preg_replace_callback call
*
- * @param string $match matched string
+ * @param array $match matched string
* @return string quoted string
*/
- protected function _quoteMatchedField($match)
+ protected function _quoteMatchedField(array $match): string
{
if (is_numeric($match[0])) {
return $match[0];
@@ -3238,12 +3412,14 @@ protected function _quoteMatchedField($match)
/**
* Returns a limit statement in the correct format for the particular database.
*
- * @param int $limit Limit of results returned
- * @param int|null $offset Offset from which to start results
+ * @param array|string|int|null $limit Limit of results returned
+ * @param array|string|int|null $offset Offset from which to start results
* @return string|null SQL limit/offset statement
*/
- public function limit($limit, $offset = null)
- {
+ public function limit(
+ array|string|int|null $limit,
+ array|string|int|null $offset = null,
+ ): ?string {
if ($limit) {
$rt = ' LIMIT';
@@ -3251,6 +3427,7 @@ public function limit($limit, $offset = null)
// The sprintf %u format behavior is undefined for values outside int range, but must remain
// consistent with previous PHP versions for query generation
set_error_handler(function () {
+ return true;
}, E_WARNING);
if ($offset) {
$rt .= sprintf(' %u,', $offset);
@@ -3268,13 +3445,16 @@ public function limit($limit, $offset = null)
/**
* Returns an ORDER BY clause as a string.
*
- * @param array|string $keys Field reference, as a key (i.e. Post.title)
+ * @param stdClass|array|string|null $keys Field reference, as a key (i.e. Post.title)
* @param string $direction Direction (ASC or DESC)
- * @param Model $model Model reference (used to look for virtual field)
+ * @param Model|null $model Model reference (used to look for virtual field)
* @return string ORDER BY clause
*/
- public function order($keys, $direction = 'ASC', ?Model $model = null)
- {
+ public function order(
+ stdClass|array|string|null $keys,
+ string $direction = 'ASC',
+ ?Model $model = null,
+ ): string {
if (!is_array($keys)) {
$keys = [$keys];
}
@@ -3331,7 +3511,7 @@ public function order($keys, $direction = 'ASC', ?Model $model = null)
}
if (strpos($key, '.')) {
- $key = preg_replace_callback('/([a-zA-Z0-9_-]{1,})\\.([a-zA-Z0-9_-]{1,})/', [&$this, '_quoteMatchedField'], $key);
+ $key = preg_replace_callback('/([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+)/', [&$this, '_quoteMatchedField'], $key);
}
if (!preg_match('/\s/', $key) && !str_contains($key, '.')) {
@@ -3353,12 +3533,14 @@ public function order($keys, $direction = 'ASC', ?Model $model = null)
/**
* Create a GROUP BY SQL clause.
*
- * @param array|string $fields Group By fields
- * @param Model $model The model to get group by fields for.
- * @return string Group By clause or null.
+ * @param array|string|null $fields Group By fields
+ * @param Model|null $model The model to get group by fields for.
+ * @return string|null Group By clause or null.
*/
- public function group($fields, ?Model $model = null)
- {
+ public function group(
+ array|string|null $fields,
+ ?Model $model = null,
+ ): ?string {
if (empty($fields)) {
return null;
}
@@ -3385,11 +3567,14 @@ public function group($fields, ?Model $model = null)
*
* @param mixed $fields Array or string of conditions
* @param bool $quoteValues If true, values should be quoted
- * @param Model $model A reference to the Model instance making the query
+ * @param Model|null $model A reference to the Model instance making the query
* @return string|null HAVING clause or null
*/
- public function having($fields, $quoteValues = true, ?Model $model = null)
- {
+ public function having(
+ mixed $fields,
+ bool $quoteValues = true,
+ ?Model $model = null,
+ ): ?string {
if (!$fields) {
return null;
}
@@ -3405,7 +3590,7 @@ public function having($fields, $quoteValues = true, ?Model $model = null)
* @param mixed $mode Lock mode
* @return string|null FOR UPDATE clause or null
*/
- public function getLockingHint($mode)
+ public function getLockingHint(mixed $mode): ?string
{
if ($mode !== true) {
return null;
@@ -3419,7 +3604,7 @@ public function getLockingHint($mode)
*
* @return void
*/
- public function close()
+ public function close(): void
{
$this->disconnect();
}
@@ -3428,10 +3613,10 @@ public function close()
* Checks if the specified table contains any record matching specified SQL
*
* @param Model $model Model to search
- * @param string $sql SQL WHERE clause (condition only, not the "WHERE" part)
+ * @param array|string $sql SQL WHERE clause (condition only, not the "WHERE" part)
* @return bool True if the table has a matching record, else false
*/
- public function hasAny(Model $model, $sql)
+ public function hasAny(Model $model, array|string $sql): bool
{
$sql = $this->conditions($sql);
$table = $this->fullTableName($model);
@@ -3454,7 +3639,7 @@ public function hasAny(Model $model, $sql)
* @param string $real Real database-layer column type (i.e. "varchar(255)")
* @return string|int|null An integer or string representing the length of the column, or null for unknown length.
*/
- public function length($real)
+ public function length(string $real): string|int|null
{
preg_match('/([\w\s]+)(?:\((.+?)\))?(\sunsigned)?/i', $real, $result);
$types = [
@@ -3496,7 +3681,7 @@ public function length($real)
* @param bool $quote Whether or not the field should be cast to a string.
* @return string|bool Converted boolean value
*/
- public function boolean($data, $quote = false)
+ public function boolean(mixed $data, bool $quote = false): string|bool
{
if ($quote) {
return !empty($data) ? '1' : '0';
@@ -3519,7 +3704,9 @@ public function insertMulti(Model|string $table, array $fields, array $values):
{
$table = $this->fullTableName($table);
$holder = implode(',', array_fill(0, count($fields), '?'));
- $fields = implode(', ', array_map([&$this, 'name'], $fields));
+ /** @var array $_field */
+ $_field = array_map([&$this, 'name'], $fields);
+ $fields = implode(', ', $_field);
$pdoMap = [
'integer' => PDO::PARAM_INT,
@@ -3580,7 +3767,7 @@ public function resetSequence(string $table, string $column): bool
* @param Model|string $model Name of model to inspect
* @return array Fields in table. Keys are column and unique
*/
- public function index($model)
+ public function index(Model|string $model): array
{
return [];
}
@@ -3589,17 +3776,14 @@ public function index($model)
* Generate a database-native schema for the given Schema object
*
* @param CakeSchema $schema An instance of a subclass of CakeSchema
- * @param string $tableName Optional. If specified only the table name given will be generated.
+ * @param string|null $tableName Optional. If specified only the table name given will be generated.
* Otherwise, all tables defined in the schema are generated.
* @return string
*/
- public function createSchema($schema, $tableName = null)
- {
- if (!$schema instanceof CakeSchema) {
- trigger_error(__d('cake_dev', 'Invalid schema object'), E_USER_WARNING);
-
- return null;
- }
+ public function createSchema(
+ CakeSchema $schema,
+ ?string $tableName = null,
+ ): string {
$out = '';
foreach ($schema->tables as $curTable => $columns) {
@@ -3656,10 +3840,10 @@ public function createSchema($schema, $tableName = null)
* Generate an alter syntax from CakeSchema::compare()
*
* @param mixed $compare The comparison data.
- * @param string $table The table name.
- * @return bool
+ * @param string|null $table The table name.
+ * @return string|false
*/
- public function alterSchema($compare, $table = null)
+ public function alterSchema(mixed $compare, ?string $table = null): string|false
{
return false;
}
@@ -3668,12 +3852,14 @@ public function alterSchema($compare, $table = null)
* Generate a "drop table" statement for the given Schema object
*
* @param CakeSchema $schema An instance of a subclass of CakeSchema
- * @param string $table Optional. If specified only the table name given will be generated.
+ * @param string|null $table Optional. If specified only the table name given will be generated.
* Otherwise, all tables defined in the schema are generated.
* @return string
*/
- public function dropSchema(CakeSchema $schema, $table = null)
- {
+ public function dropSchema(
+ CakeSchema $schema,
+ ?string $table = null,
+ ): string {
$out = '';
if ($table && array_key_exists($table, $schema->tables)) {
@@ -3695,7 +3881,7 @@ public function dropSchema(CakeSchema $schema, $table = null)
* @param Model|string $table Name of the table to drop
* @return string Drop table SQL statement
*/
- protected function _dropTable($table): string
+ protected function _dropTable(Model|string $table): string
{
return 'DROP TABLE ' . $this->fullTableName($table) . ';';
}
@@ -3705,12 +3891,12 @@ protected function _dropTable($table): string
*
* @param array $column An array structured like the following: array('name' => 'value', 'type' => 'value'[, options]),
* where options can be 'default', 'length', or 'key'.
- * @return string
+ * @return string|null
*/
- public function buildColumn($column)
+ public function buildColumn(array $column): ?string
{
- $name = $type = null;
- extract(array_merge(['null' => true], $column));
+ $name = $column['name'] ?? null;
+ $type = $column['type'] ?? null;
if (empty($name) || empty($type)) {
trigger_error(__d('cake_dev', 'Column name or type not defined in schema'), E_USER_WARNING);
@@ -3778,8 +3964,11 @@ public function buildColumn($column)
* @param string $position The position type to use. 'beforeDefault' or 'afterDefault' are common
* @return string a built column with the field parameters added.
*/
- protected function _buildFieldParameters($columnString, $columnData, $position)
- {
+ protected function _buildFieldParameters(
+ string $columnString,
+ array $columnData,
+ string $position,
+ ): string {
foreach ($this->fieldParameters as $paramName => $value) {
if (isset($columnData[$paramName]) && $value['position'] == $position) {
if (isset($value['options']) && !in_array($columnData[$paramName], $value['options'], true)) {
@@ -3821,7 +4010,9 @@ public function buildIndex(array $indexes, ?string $table = null): array
$name = $this->startQuote . $name . $this->endQuote;
}
if (is_array($value['column'])) {
- $out .= 'KEY ' . $name . ' (' . implode(', ', array_map([&$this, 'name'], $value['column'])) . ')';
+ /** @var array $_column */
+ $_column = array_map([&$this, 'name'], $value['column']);
+ $out .= 'KEY ' . $name . ' (' . implode(', ', $_column) . ')';
} else {
$out .= 'KEY ' . $name . ' (' . $this->name($value['column']) . ')';
}
@@ -3837,7 +4028,7 @@ public function buildIndex(array $indexes, ?string $table = null): array
* @param string $name The table name to read.
* @return array
*/
- public function readTableParameters($name)
+ public function readTableParameters(string $name): array
{
$parameters = [];
if (method_exists($this, 'listDetailedSources')) {
@@ -3856,11 +4047,13 @@ public function readTableParameters($name)
* Format parameters for create table
*
* @param array $parameters The parameters to create SQL for.
- * @param string $table The table name.
+ * @param string|null $table The table name.
* @return array
*/
- public function buildTableParameters($parameters, $table = null)
- {
+ public function buildTableParameters(
+ array $parameters,
+ ?string $table = null,
+ ): array {
$result = [];
foreach ($parameters as $name => $value) {
if (isset($this->tableParameters[$name])) {
@@ -3877,19 +4070,19 @@ public function buildTableParameters($parameters, $table = null)
/**
* Guesses the data type of an array
*
- * @param string $value The value to introspect for type data.
+ * @param mixed $value The value to introspect for type data.
* @return string
*/
- public function introspectType($value)
+ public function introspectType(mixed $value): string
{
if (!is_array($value)) {
if (is_bool($value)) {
return 'boolean';
}
- if (is_float($value) && (float)$value === $value) {
+ if (is_float($value)) {
return 'float';
}
- if (is_int($value) && (int)$value === $value) {
+ if (is_int($value)) {
return 'integer';
}
if (is_string($value) && strlen($value) > 255) {
@@ -3903,12 +4096,12 @@ public function introspectType($value)
$containsInt = $containsString = false;
foreach ($value as $valElement) {
$valElement = trim($valElement);
- if (!is_float($valElement) && !preg_match('/^[\d]+\.[\d]+$/', $valElement)) {
+ if (!preg_match('/^\d+\.\d+$/', $valElement)) {
$isAllFloat = false;
} else {
continue;
}
- if (!is_int($valElement) && !preg_match('/^[\d]+$/', $valElement)) {
+ if (!preg_match('/^\d+$/', $valElement)) {
$isAllInt = false;
} else {
$containsInt = true;
@@ -3936,7 +4129,7 @@ public function introspectType($value)
*
* @return void
*/
- public function flushQueryCache()
+ public function flushQueryCache(): void
{
$this->_queryCache = [];
}
@@ -3949,8 +4142,11 @@ public function flushQueryCache()
* @param array $params query params bound as values
* @return void
*/
- protected function _writeQueryCache($sql, $data, $params = [])
- {
+ protected function _writeQueryCache(
+ string $sql,
+ mixed $data,
+ array $params = [],
+ ): void {
if (preg_match('/^\s*select/i', $sql)) {
$this->_queryCache[$sql][serialize($params)] = $data;
}
@@ -3961,9 +4157,9 @@ protected function _writeQueryCache($sql, $data, $params = [])
*
* @param string $sql SQL query
* @param array $params query params bound as values
- * @return mixed results for query if it is cached, false otherwise
+ * @return array|false results for query if it is cached, false otherwise
*/
- public function getQueryCache($sql, $params = [])
+ public function getQueryCache(string $sql, array $params = []): array|false
{
if (isset($this->_queryCache[$sql]) && preg_match('/^\s*select/i', $sql)) {
$serialized = serialize($params);
diff --git a/src/Model/Datasource/Session/CacheSession.php b/src/Model/Datasource/Session/CacheSession.php
index d8ea6ec095..afca0e3d43 100644
--- a/src/Model/Datasource/Session/CacheSession.php
+++ b/src/Model/Datasource/Session/CacheSession.php
@@ -34,7 +34,7 @@ class CacheSession implements CakeSessionHandlerInterface
*
* @return bool Success
*/
- public function open()
+ public function open(): bool
{
return true;
}
@@ -44,7 +44,7 @@ public function open()
*
* @return bool Success
*/
- public function close()
+ public function close(): bool
{
return true;
}
@@ -53,9 +53,9 @@ public function close()
* Method used to read from a database session.
*
* @param string $id The key of the value to read
- * @return mixed The value of the key or false if it does not exist
+ * @return string|false The value of the key or false if it does not exist
*/
- public function read($id)
+ public function read(string $id): string|false
{
$data = Cache::read($id, Configure::read('Session.handler.config'));
@@ -69,34 +69,34 @@ public function read($id)
/**
* Helper function called on write for database sessions.
*
- * @param int $id ID that uniquely identifies session in database
+ * @param string $id ID that uniquely identifies session in database
* @param mixed $data The value of the data to be saved.
* @return bool True for successful write, false otherwise.
*/
- public function write($id, $data)
+ public function write(string $id, mixed $data): bool
{
- return (bool)Cache::write($id, $data, Configure::read('Session.handler.config'));
+ return Cache::write($id, $data, Configure::read('Session.handler.config'));
}
/**
* Method called on the destruction of a database session.
*
- * @param int $id ID that uniquely identifies session in cache
+ * @param string $id ID that uniquely identifies session in cache
* @return bool True for successful delete, false otherwise.
*/
- public function destroy($id)
+ public function destroy(string $id): bool
{
- return (bool)Cache::delete($id, Configure::read('Session.handler.config'));
+ return Cache::delete($id, Configure::read('Session.handler.config'));
}
/**
* Helper function called on gc for cache sessions.
*
- * @param int $expires Timestamp (defaults to current time)
+ * @param int|null $expires Timestamp (defaults to current time)
* @return bool Success
*/
- public function gc($expires = null)
+ public function gc(?int $expires = null): bool
{
- return (bool)Cache::gc(Configure::read('Session.handler.config'), $expires);
+ return Cache::gc(Configure::read('Session.handler.config'), $expires);
}
}
diff --git a/src/Model/Datasource/Session/CakeSessionHandlerInterface.php b/src/Model/Datasource/Session/CakeSessionHandlerInterface.php
index e023030b1d..f04abc0d07 100644
--- a/src/Model/Datasource/Session/CakeSessionHandlerInterface.php
+++ b/src/Model/Datasource/Session/CakeSessionHandlerInterface.php
@@ -29,14 +29,14 @@ interface CakeSessionHandlerInterface
*
* @return bool Success
*/
- public function open();
+ public function open(): bool;
/**
* Method called on close of a session.
*
* @return bool Success
*/
- public function close();
+ public function close(): bool;
/**
* Method used to read from a session.
@@ -44,31 +44,31 @@ public function close();
* @param string $id The key of the value to read
* @return mixed The value of the key or false if it does not exist
*/
- public function read($id);
+ public function read(string $id): mixed;
/**
* Helper function called on write for sessions.
*
- * @param int $id ID that uniquely identifies session in database
+ * @param string $id ID that uniquely identifies session in database
* @param mixed $data The value of the data to be saved.
* @return bool True for successful write, false otherwise.
*/
- public function write($id, $data);
+ public function write(string $id, mixed $data): bool;
/**
* Method called on the destruction of a session.
*
- * @param int $id ID that uniquely identifies session in database
- * @return bool True for successful delete, false otherwise.
+ * @param string $id ID that uniquely identifies session in database
+ * @return int|bool True for successful delete, false otherwise.
*/
- public function destroy($id);
+ public function destroy(string $id): int|bool;
/**
* Run the Garbage collection on the session storage. This method should vacuum all
* expired or dead sessions.
*
- * @param int $expires Timestamp (defaults to current time)
+ * @param int|null $expires Timestamp (defaults to current time)
* @return bool Success
*/
- public function gc($expires = null);
+ public function gc(?int $expires = null): bool;
}
diff --git a/src/Model/Datasource/Session/DatabaseSession.php b/src/Model/Datasource/Session/DatabaseSession.php
index baaf82aff6..75cb41fc9d 100644
--- a/src/Model/Datasource/Session/DatabaseSession.php
+++ b/src/Model/Datasource/Session/DatabaseSession.php
@@ -73,7 +73,7 @@ public function __construct()
*
* @return bool Success
*/
- public function open()
+ public function open(): bool
{
return true;
}
@@ -83,7 +83,7 @@ public function open()
*
* @return bool Success
*/
- public function close()
+ public function close(): bool
{
return true;
}
@@ -91,10 +91,10 @@ public function close()
/**
* Method used to read from a database session.
*
- * @param string|int $id The key of the value to read
- * @return mixed The value of the key or false if it does not exist
+ * @param string $id The key of the value to read
+ * @return string The value of the key or false if it does not exist
*/
- public function read($id)
+ public function read(string $id): string
{
$row = $this->_model->find('first', [
'conditions' => [$this->_model->alias . '.' . $this->_model->primaryKey => $id],
@@ -117,11 +117,11 @@ public function read($id)
* Will retry, once, if the save triggers a PDOException which
* can happen if a race condition is encountered
*
- * @param int $id ID that uniquely identifies session in database
+ * @param string $id ID that uniquely identifies session in database
* @param mixed $data The value of the data to be saved.
* @return bool True for successful write, false otherwise.
*/
- public function write($id, $data)
+ public function write(string $id, mixed $data): bool
{
if (!$id) {
return false;
@@ -145,10 +145,10 @@ public function write($id, $data)
/**
* Method called on the destruction of a database session.
*
- * @param int $id ID that uniquely identifies session in database
+ * @param string $id ID that uniquely identifies session in database
* @return bool True for successful delete, false otherwise.
*/
- public function destroy($id)
+ public function destroy(string $id): bool
{
return (bool)$this->_model->delete($id);
}
@@ -156,10 +156,10 @@ public function destroy($id)
/**
* Helper function called on gc for database sessions.
*
- * @param int $expires Timestamp (defaults to current time)
+ * @param int|null $expires Timestamp (defaults to current time)
* @return bool Success
*/
- public function gc($expires = null)
+ public function gc(?int $expires = null): bool
{
if (!$expires) {
$expires = time();
diff --git a/src/Model/Datasource/SessionHandlerAdapter.php b/src/Model/Datasource/SessionHandlerAdapter.php
index 2b911630ea..264e7cac96 100644
--- a/src/Model/Datasource/SessionHandlerAdapter.php
+++ b/src/Model/Datasource/SessionHandlerAdapter.php
@@ -32,7 +32,7 @@ public function __construct(
*
* @return bool Success
*/
- public function close()
+ public function close(): bool
{
return $this->cakeSessionHandler->close();
}
@@ -43,7 +43,7 @@ public function close()
* @param string $id The session ID
* @return bool Success
*/
- public function destroy(string $id)
+ public function destroy(string $id): bool
{
return $this->cakeSessionHandler->destroy($id);
}
@@ -54,7 +54,7 @@ public function destroy(string $id)
* @param int $max_lifetime Session max lifetime in seconds
* @return int|bool Number of deleted sessions or success status
*/
- public function gc(int $max_lifetime)
+ public function gc(int $max_lifetime): int|bool
{
return $this->cakeSessionHandler->gc($max_lifetime);
}
@@ -66,7 +66,7 @@ public function gc(int $max_lifetime)
* @param string $name The session name
* @return bool Success
*/
- public function open(string $path, string $name)
+ public function open(string $path, string $name): bool
{
//Cake interface ignores these parameters.
return $this->cakeSessionHandler->open();
@@ -78,7 +78,7 @@ public function open(string $path, string $name)
* @param string $id The session ID
* @return string|false The session data or false on failure
*/
- public function read(string $id)
+ public function read(string $id): string|false
{
return $this->cakeSessionHandler->read($id);
}
@@ -90,7 +90,7 @@ public function read(string $id)
* @param string $data The session data
* @return bool Success
*/
- public function write(string $id, string $data)
+ public function write(string $id, string $data): bool
{
return $this->cakeSessionHandler->write($id, $data);
}
diff --git a/src/Model/I18nModel.php b/src/Model/I18nModel.php
index e7b5b918d8..0f4c91dbcf 100644
--- a/src/Model/I18nModel.php
+++ b/src/Model/I18nModel.php
@@ -31,21 +31,21 @@ class I18nModel extends AppModel
/**
* Model name
*
- * @var string
+ * @var string|null
*/
public ?string $name = 'I18nModel';
/**
* Table name
*
- * @var string
+ * @var string|bool|null
*/
public string|bool|null $useTable = 'i18n';
/**
* Display field
*
- * @var string
+ * @var string|bool|null
*/
public string|bool|null $displayField = 'field';
}
diff --git a/src/Model/Model.php b/src/Model/Model.php
index 017e988cf2..0fcfd2c94d 100644
--- a/src/Model/Model.php
+++ b/src/Model/Model.php
@@ -32,6 +32,7 @@
use Cake\Event\CakeEventListener;
use Cake\Event\CakeEventManager;
use Cake\Model\Datasource\DataSource;
+use Cake\Model\Datasource\DboSource;
use Cake\Utility\CakeText;
use Cake\Utility\ClassRegistry;
use Cake\Utility\Hash;
@@ -53,6 +54,7 @@
*
* @package Cake.Model
* @link https://book.cakephp.org/2.0/en/models.html
+ * @property Model $VerifyParent
*/
#[AllowDynamicProperties]
class Model extends CakeObject implements CakeEventListener
@@ -71,7 +73,7 @@ class Model extends CakeObject implements CakeEventListener
/**
* Custom database table name, or null/false if no table association is desired.
*
- * @var string|false|null
+ * @var string|bool|null
* @link https://book.cakephp.org/2.0/en/models/model-attributes.html#usetable
*/
public string|bool|null $useTable = null;
@@ -81,7 +83,7 @@ class Model extends CakeObject implements CakeEventListener
*
* This field is also used in `find('list')` when called with no extra parameters in the fields list
*
- * @var string|false|null
+ * @var string|bool|null
* @link https://book.cakephp.org/2.0/en/models/model-attributes.html#displayfield
*/
public string|bool|null $displayField = null;
@@ -90,17 +92,17 @@ class Model extends CakeObject implements CakeEventListener
* Value of the primary key ID of the record that this model is currently pointing to.
* Automatically set after database insertions.
*
- * @var mixed|false
+ * @var mixed
*/
- public $id = false;
+ public mixed $id = false;
/**
* Container for the data that this model gets from persistent storage (usually, a database).
*
- * @var array|false
+ * @var array|false|null
* @link https://book.cakephp.org/2.0/en/models/model-attributes.html#data
*/
- public $data = [];
+ public array|false|null $data = [];
/**
* Holds physical schema/database name for this model. Automatically set during Model creation.
@@ -129,7 +131,7 @@ class Model extends CakeObject implements CakeEventListener
*
* @var array|null
*/
- protected $_schema = null;
+ protected ?array $_schema = null;
/**
* List of validation rules. It must be an array with the field name as key and using
@@ -583,8 +585,6 @@ class Model extends CakeObject implements CakeEventListener
*/
protected array $_associations = ['belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany'];
-// @codingStandardsIgnoreStart
-
/**
* Holds model associations temporarily to allow for dynamic (un)binding.
*
@@ -611,7 +611,7 @@ class Model extends CakeObject implements CakeEventListener
*
* @var array
*/
- public $__backContainableAssociation = [];
+ public array $__backContainableAssociation = [];
/**
* Safe update mode
@@ -621,8 +621,6 @@ class Model extends CakeObject implements CakeEventListener
*/
public bool $__safeUpdateMode = false;
-// @codingStandardsIgnoreEnd
-
/**
* If true, afterFind will be passed consistent formatted $results in case of $primary is false.
* The format will be such as the following.
@@ -682,6 +680,11 @@ class Model extends CakeObject implements CakeEventListener
*/
protected ?ModelValidator $_validator = null;
+ /**
+ * @var array|string|null
+ */
+ public array|string|null $locale = null;
+
/**
* Constructor. Binds the model's database table to the object.
*
@@ -710,23 +713,41 @@ class Model extends CakeObject implements CakeEventListener
* Would create a model attached to the posts table on connection2. Dynamic model creation is useful
* when you want a model object that contains no associations or attached behaviors.
*
- * @param array|string|int|bool $id Set this ID for this model on startup,
+ * @param array{
+ * id?: mixed,
+ * table?: string|bool|null,
+ * ds?: string,
+ * name?: string|null,
+ * alias?: string|null,
+ * plugin?: string|null
+ * }|string|int|false|null $id Set this ID for this model on startup,
* can also be an array of options, see above.
- * @param string|false $table Name of database table to use.
- * @param string $ds DataSource connection name.
- */
- public function __construct($id = false, $table = null, $ds = null)
- {
+ * @param string|false|null $table Name of database table to use.
+ * @param string|null $ds DataSource connection name.
+ */
+ public function __construct(
+ array|string|int|false|null $id = false,
+ string|false|null $table = null,
+ ?string $ds = null,
+ ) {
parent::__construct();
if (is_array($id)) {
- extract(array_merge(
- [
- 'id' => $this->id, 'table' => $this->useTable, 'ds' => $this->useDbConfig,
- 'name' => $this->name, 'alias' => $this->alias, 'plugin' => $this->plugin,
- ],
- $id,
- ));
+ $config = array_merge([
+ 'id' => $this->id,
+ 'table' => $this->useTable,
+ 'ds' => $this->useDbConfig,
+ 'name' => $this->name,
+ 'alias' => $this->alias,
+ 'plugin' => $this->plugin,
+ ], $id);
+
+ $id = $config['id'];
+ $table = $config['table'];
+ $ds = $config['ds'];
+ $name = $config['name'];
+ $alias = $config['alias'];
+ $plugin = $config['plugin'];
}
if ($this->plugin === null) {
@@ -808,7 +829,7 @@ public function __construct($id = false, $table = null, $ds = null)
* Returns a list of all events that will fire in the model during it's lifecycle.
* You can override this function to add your own listener callbacks
*
- * @return array
+ * @return array
*/
public function implementedEvents(): array
{
@@ -831,7 +852,7 @@ public function implementedEvents(): array
*
* @return CakeEventManager
*/
- public function getEventManager()
+ public function getEventManager(): CakeEventManager
{
if (empty($this->_eventManager)) {
$this->_eventManager = new CakeEventManager();
@@ -850,14 +871,19 @@ public function getEventManager()
* @param array $params Parameters for the method.
* @return mixed Whatever is returned by called method
*/
- public function __call($method, $params)
+ public function __call(string $method, array $params): mixed
{
$result = $this->Behaviors->dispatchMethod($this, $method, $params);
if ($result !== ['unhandled']) {
return $result;
}
- return $this->getDataSource()->query($method, $params, $this);
+ $db = $this->getDataSource();
+ if (method_exists($db, 'query')) {
+ return $db->query($method, $params, $this);
+ }
+
+ return null;
}
/**
@@ -866,17 +892,18 @@ public function __call($method, $params)
* @param string $name variable tested for existence in class
* @return bool true if the variable exists (if is a not loaded model association it will be created), false otherwise
*/
- public function __isset($name)
+ public function __isset(string $name): bool
{
- $className = false;
-
+ $className = null;
+ $assocKey = null;
foreach ($this->_associations as $type) {
- if (isset($name, $this->{$type}[$name])) {
- $className = empty($this->{$type}[$name]['className']) ? $name : $this->{$type}[$name]['className'];
+ if (isset($this->{$type}[$name])) {
+ $className = empty($this->{$type}[$name]['className'])
+ ? $name : $this->{$type}[$name]['className'];
break;
- } elseif (isset($name, $this->__backAssociation[$type][$name])) {
- $className = empty($this->__backAssociation[$type][$name]['className']) ?
- $name : $this->__backAssociation[$type][$name]['className'];
+ } elseif (isset($this->__backAssociation[$type][$name])) {
+ $className = empty($this->__backAssociation[$type][$name]['className'])
+ ? $name : $this->__backAssociation[$type][$name]['className'];
break;
} elseif ($type === 'hasAndBelongsToMany') {
foreach ($this->{$type} as $k => $relation) {
@@ -889,7 +916,7 @@ public function __isset($name)
$className = $name;
}
} else {
- [$plugin, $class] = pluginSplit($relation['with']);
+ [, $class] = pluginSplit($relation['with']);
if ($class === $name) {
$className = $relation['with'];
}
@@ -936,7 +963,7 @@ public function __isset($name)
* @param string $name variable requested for it's value or reference
* @return mixed value of requested variable if it is set
*/
- public function __get($name)
+ public function __get(string $name): mixed
{
if ($name === 'displayField') {
return $this->displayField = $this->hasField(['title', 'name', $this->primaryKey]);
@@ -944,16 +971,19 @@ public function __get($name)
if ($name === 'tablePrefix') {
$this->setDataSource();
- if (property_exists($this, 'tablePrefix') && !empty($this->tablePrefix)) {
- return $this->tablePrefix;
+
+ if (empty($this->tablePrefix)) {
+ $this->tablePrefix = null;
}
- return $this->tablePrefix = null;
+ return $this->tablePrefix;
}
if (isset($this->{$name})) {
return $this->{$name};
}
+
+ return null;
}
/**
@@ -975,7 +1005,7 @@ public function __get($name)
* @return bool Success
* @link https://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#creating-and-destroying-associations-on-the-fly
*/
- public function bindModel($params, $reset = true)
+ public function bindModel(array $params, bool $reset = true): bool
{
foreach ($params as $assoc => $model) {
if ($reset === true && !isset($this->__backAssociation[$assoc])) {
@@ -1027,7 +1057,7 @@ public function bindModel($params, $reset = true)
* @return bool Success
* @link https://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#creating-and-destroying-associations-on-the-fly
*/
- public function unbindModel($params, $reset = true)
+ public function unbindModel(array $params, bool $reset = true): bool
{
foreach ($params as $assoc => $models) {
if ($reset === true && !isset($this->__backAssociation[$assoc])) {
@@ -1051,7 +1081,7 @@ public function unbindModel($params, $reset = true)
*
* @return void
*/
- protected function _createLinks()
+ protected function _createLinks(): void
{
foreach ($this->_associations as $type) {
$association =& $this->{$type};
@@ -1068,8 +1098,6 @@ protected function _createLinks()
if (!empty($association)) {
foreach ($association as $assoc => $value) {
- $plugin = null;
-
if (is_numeric($assoc)) {
unset($association[$assoc]);
$assoc = $value;
@@ -1092,9 +1120,9 @@ protected function _createLinks()
/**
* Protected helper method to create associated models of a given class.
*
- * @param string $assoc Association name
- * @param string $className Class name
- * @param string $plugin name of the plugin where $className is located
+ * @param string|null $assoc Association name
+ * @param string|null $className Class name
+ * @param string|null $plugin name of the plugin where $className is located
* examples: public $hasMany = array('Assoc' => array('className' => 'ModelName'));
* usage: $this->Assoc->modelMethods();
*
@@ -1102,8 +1130,11 @@ protected function _createLinks()
* usage: $this->ModelName->modelMethods();
* @return void
*/
- protected function _constructLinkedModel($assoc, $className = null, $plugin = null)
- {
+ protected function _constructLinkedModel(
+ ?string $assoc,
+ ?string $className = null,
+ ?string $plugin = null,
+ ): void {
if (empty($className)) {
$className = $assoc;
}
@@ -1113,15 +1144,19 @@ protected function _constructLinkedModel($assoc, $className = null, $plugin = nu
$plugin .= '.';
}
- $model = ['class' => $plugin . $className, 'alias' => $assoc];
- $this->{$assoc} = ClassRegistry::init($model);
+ /** @var Model $model */
+ $model = ClassRegistry::init([
+ 'class' => $plugin . $className,
+ 'alias' => $assoc,
+ ]);
+ $this->{$assoc} = $model;
if ($plugin) {
- ClassRegistry::addObject($plugin . $className, $this->{$assoc});
+ ClassRegistry::addObject($plugin . $className, $model);
}
if ($assoc) {
- $this->tableToModel[$this->{$assoc}->table] = $assoc;
+ $this->tableToModel[$model->table] = $assoc;
}
}
}
@@ -1133,50 +1168,36 @@ protected function _constructLinkedModel($assoc, $className = null, $plugin = nu
* @param string $assocKey Association key.
* @return void
*/
- protected function _generateAssociation($type, $assocKey)
+ protected function _generateAssociation(string $type, string $assocKey): void
{
$class = $assocKey;
$dynamicWith = false;
$assoc =& $this->{$type}[$assocKey];
foreach ($this->_associationKeys[$type] as $key) {
- if (!isset($assoc[$key]) || $assoc[$key] === null) {
- $data = '';
-
- switch ($key) {
- case 'fields':
- $data = '';
- break;
-
- case 'foreignKey':
- $data = ($type === 'belongsTo' ? Inflector::underscore($assocKey) : Inflector::singularize($this->table)) . '_id';
- break;
-
- case 'associationForeignKey':
- $data = Inflector::singularize($this->{$class}->table) . '_id';
- break;
-
- case 'with':
- $data = Inflector::camelize(Inflector::singularize($assoc['joinTable']));
+ if (!isset($assoc[$key])) {
+ $assoc[$key] = match ($key) {
+ 'foreignKey' => ($type === 'belongsTo' ? Inflector::underscore($assocKey) : Inflector::singularize($this->table)) . '_id',
+ 'associationForeignKey' => (function () use ($class) {
+ $table = $this->{$class}->table;
+
+ return Inflector::singularize($table) . '_id';
+ })(),
+ 'with' => (function () use (&$dynamicWith, $assoc) {
$dynamicWith = true;
- break;
- case 'joinTable':
+ return Inflector::camelize(Inflector::singularize($assoc['joinTable']));
+ })(),
+ 'joinTable' => (function () use ($class) {
$tables = [$this->table, $this->{$class}->table];
sort($tables);
- $data = $tables[0] . '_' . $tables[1];
- break;
-
- case 'className':
- $data = $class;
- break;
- case 'unique':
- $data = true;
- break;
- }
-
- $assoc[$key] = $data;
+ return $tables[0] . '_' . $tables[1];
+ })(),
+ 'className' => $class,
+ 'unique' => true,
+ default => '',
+ };
}
if ($dynamicWith) {
@@ -1192,28 +1213,26 @@ protected function _generateAssociation($type, $assocKey)
* @throws MissingTableException when database table $tableName is not found on data source
* @return void
*/
- public function setSource($tableName)
+ public function setSource(string $tableName): void
{
$this->setDataSource($this->useDbConfig);
$db = ConnectionManager::getDataSource($this->useDbConfig);
- if (method_exists($db, 'listSources')) {
- $restore = $db->cacheSources;
- $db->cacheSources = ($restore && $this->cacheSources);
- $sources = $db->listSources();
- $db->cacheSources = $restore;
-
- if (is_array($sources) && !in_array(strtolower($this->tablePrefix . $tableName), array_map('strtolower', $sources))) {
- throw new MissingTableException([
- 'table' => $this->tablePrefix . $tableName,
- 'class' => $this->alias,
- 'ds' => $this->useDbConfig,
- ]);
- }
+ $restore = $db->cacheSources;
+ $db->cacheSources = ($restore && $this->cacheSources);
+ $sources = $db->listSources();
+ $db->cacheSources = $restore;
- if ($sources) {
- $this->_schema = null;
- }
+ if (is_array($sources) && !in_array(strtolower($this->tablePrefix . $tableName), array_map('strtolower', $sources))) {
+ throw new MissingTableException([
+ 'table' => $this->tablePrefix . $tableName,
+ 'class' => $this->alias,
+ 'ds' => $this->useDbConfig,
+ ]);
+ }
+
+ if ($sources) {
+ $this->_schema = null;
}
$this->table = $this->useTable = $tableName;
@@ -1230,12 +1249,12 @@ public function setSource($tableName)
* (Alternative indata: two strings, which are mangled to
* a one-item, two-dimensional array using $one for a key and $two as its value.)
*
- * @param SimpleXmlElement|DomNode|array|string $one Array or string of data
- * @param string|false $two Value string for the alternative indata method
+ * @param mixed $one Array or string of data
+ * @param string|false|null $two Value string for the alternative indata method
* @return array|null Data with all of $one's keys and values, otherwise null.
* @link https://book.cakephp.org/2.0/en/models/saving-your-data.html
*/
- public function set($one, $two = null)
+ public function set(mixed $one, string|false|null $two = null): ?array
{
if (!$one) {
return null;
@@ -1291,7 +1310,7 @@ public function set($one, $two = null)
* @param array $data Data.
* @return array
*/
- protected function _setAliasData($data)
+ protected function _setAliasData(array $data): array
{
$models = array_keys($this->getAssociated());
$schema = array_keys((array)$this->schema());
@@ -1312,7 +1331,7 @@ protected function _setAliasData($data)
* @param array $xml XML as array
* @return array
*/
- protected function _normalizeXmlData(array $xml)
+ protected function _normalizeXmlData(array $xml): array
{
$return = [];
foreach ($xml as $key => $value) {
@@ -1335,7 +1354,7 @@ protected function _normalizeXmlData(array $xml)
* @param object|array $data An array or object to be deconstructed into a field
* @return mixed The resulting data that should be assigned to a field
*/
- public function deconstruct($field, $data)
+ public function deconstruct(string $field, object|array $data): mixed
{
if (!is_array($data)) {
return $data;
@@ -1359,7 +1378,6 @@ public function deconstruct($field, $data)
}
if (
- isset($data['hour']) &&
isset($data['meridian']) &&
!empty($data['hour']) &&
$data['hour'] != 12 &&
@@ -1398,13 +1416,11 @@ public function deconstruct($field, $data)
}
}
- if (!isset($data[$val]) || isset($data[$val]) && (empty($data[$val]) || str_starts_with($data[$val], '-'))) {
+ if (empty($data[$val]) || str_starts_with($data[$val], '-')) {
return null;
}
- if (isset($data[$val]) && !empty($data[$val])) {
- $date[$key] = $data[$val];
- }
+ $date[$key] = $data[$val];
}
}
@@ -1429,14 +1445,12 @@ public function deconstruct($field, $data)
* @param string|bool $field Set to true to reload schema, or a string to return a specific field
* @return array|null Array of table metadata
*/
- public function schema($field = false)
+ public function schema(string|bool $field = false): ?array
{
if ($this->useTable !== false && (!is_array($this->_schema) || $field === true)) {
$db = $this->getDataSource();
$db->cacheSources = ($this->cacheSources && $db->cacheSources);
- if (method_exists($db, 'describe')) {
- $this->_schema = $db->describe($this);
- }
+ $this->_schema = $db->describe($this);
}
if (!is_string($field)) {
@@ -1451,31 +1465,28 @@ public function schema($field = false)
*
* @return array Field types indexed by field name
*/
- public function getColumnTypes()
+ public function getColumnTypes(): array
{
$columns = $this->schema();
if (empty($columns)) {
trigger_error(__d('cake_dev', '(Model::getColumnTypes) Unable to build model field data. If you are using a model without a database table, try implementing schema()'), E_USER_WARNING);
}
- $cols = [];
- foreach ($columns as $field => $values) {
- $cols[$field] = $values['type'];
- }
-
- return $cols;
+ return array_map(function ($values) {
+ return $values['type'];
+ }, $columns);
}
/**
* Returns the column type of a column in the model.
*
- * @param string $column The name of the model column
- * @return string Column type
+ * @param string|null $column The name of the model column
+ * @return string|null Column type
*/
- public function getColumnType($column)
+ public function getColumnType(?string $column): ?string
{
$cols = $this->schema();
- if (isset($cols[$column]) && isset($cols[$column]['type'])) {
+ if (isset($cols[$column]['type'])) {
return $cols[$column]['type'];
}
@@ -1504,13 +1515,13 @@ public function getColumnType($column)
/**
* Returns true if the supplied field exists in the model's database table.
*
- * @param array|string $name Name of field to look for, or an array of names
+ * @param array|string|null $name Name of field to look for, or an array of names
* @param bool $checkVirtual checks if the field is declared as virtual
* @return mixed If $name is a string, returns a boolean indicating whether the field exists.
* If $name is an array of field names, returns the first field that exists,
* or false if none exist.
*/
- public function hasField($name, $checkVirtual = false)
+ public function hasField(array|string|null $name, bool $checkVirtual = false): mixed
{
if (is_array($name)) {
foreach ($name as $n) {
@@ -1544,7 +1555,7 @@ public function hasField($name, $checkVirtual = false)
* @param string $method The method to be called.
* @return bool True on method being callable.
*/
- public function hasMethod($method)
+ public function hasMethod(string $method): bool
{
if (method_exists($this, $method)) {
return true;
@@ -1556,10 +1567,10 @@ public function hasMethod($method)
/**
* Returns true if the supplied field is a model Virtual Field
*
- * @param string $field Name of field to look for
+ * @param mixed $field Name of field to look for
* @return bool indicating whether the field exists as a model virtual field.
*/
- public function isVirtualField($field)
+ public function isVirtualField(mixed $field): bool
{
if (empty($this->virtualFields) || !is_string($field)) {
return false;
@@ -1582,12 +1593,12 @@ public function isVirtualField($field)
/**
* Returns the expression for a model virtual field
*
- * @param string $field Name of field to look for
+ * @param string|null $field Name of field to look for
* @return mixed If $field is string expression bound to virtual field $field
* If $field is null, returns an array of all model virtual fields
* or false if none $field exist.
*/
- public function getVirtualField($field = null)
+ public function getVirtualField(?string $field = null): mixed
{
if (!$field) {
return empty($this->virtualFields) ? false : $this->virtualFields;
@@ -1609,13 +1620,13 @@ public function getVirtualField($field = null)
* for those fields that are not defined in $data, and clearing previous validation errors.
* Especially helpful for saving data in loops.
*
- * @param array|bool $data Optional data array to assign to the model after it is created. If null or false,
+ * @param array|bool|null $data Optional data array to assign to the model after it is created. If null or false,
* schema data defaults are not merged.
* @param bool $filterKey If true, overwrites any primary key input with an empty value
- * @return array The current Model::data; after merging $data and/or defaults from database
+ * @return array|null The current Model::data; after merging $data and/or defaults from database
* @link https://book.cakephp.org/2.0/en/models/saving-your-data.html#model-create-array-data-array
*/
- public function create($data = [], $filterKey = false)
+ public function create(array|bool|null $data = [], bool $filterKey = false): ?array
{
$defaults = [];
$this->id = false;
@@ -1647,7 +1658,7 @@ public function create($data = [], $filterKey = false)
* @return bool Always true upon success
* @see Model::create()
*/
- public function clear()
+ public function clear(): bool
{
$this->create(false);
@@ -1658,12 +1669,12 @@ public function clear()
* Returns a list of fields from the database, and sets the current model
* data (Model::$data) with the record found.
*
- * @param array|string $fields String of single field name, or an array of field names.
- * @param string|int $id The ID of the record to read
+ * @param array|string|null $fields String of single field name, or an array of field names.
+ * @param string|int|bool|null $id The ID of the record to read
* @return array|false Array of database fields, or false if not found
* @link https://book.cakephp.org/2.0/en/models/retrieving-your-data.html#model-read
*/
- public function read($fields = null, $id = null)
+ public function read(array|string|null $fields = null, string|int|bool|null $id = null): array|false
{
$this->validationErrors = [];
@@ -1694,13 +1705,16 @@ public function read($fields = null, $id = null)
* of the first record in the supplied order.
*
* @param string $name The name of the field to get.
- * @param array $conditions SQL conditions (defaults to NULL).
- * @param array|string $order SQL ORDER BY fragment.
+ * @param array|bool|null $conditions SQL conditions (defaults to NULL).
+ * @param array|string|null $order SQL ORDER BY fragment.
* @return string|false Field content, or false if not found.
* @link https://book.cakephp.org/2.0/en/models/retrieving-your-data.html#model-field
*/
- public function field($name, $conditions = null, $order = null)
- {
+ public function field(
+ string $name,
+ array|bool|null $conditions = null,
+ array|string|null $order = null,
+ ): string|false {
if ($conditions === null && !in_array($this->id, [false, null], true)) {
$conditions = [$this->alias . '.' . $this->primaryKey => $this->id];
}
@@ -1750,7 +1764,7 @@ public function field($name, $conditions = null, $order = null)
* @see Model::save()
* @link https://book.cakephp.org/2.0/en/models/saving-your-data.html#model-savefield-string-fieldname-string-fieldvalue-validate-false
*/
- public function saveField($name, $value, $validate = false)
+ public function saveField(string $name, mixed $value, array|bool $validate = false): array|bool
{
$id = $this->id;
$this->create(false);
@@ -1768,7 +1782,7 @@ public function saveField($name, $value, $validate = false)
* default, validation occurs before save. Passthrough method to _doSave() with
* transaction handling.
*
- * @param array $data Data to save.
+ * @param array|null $data Data to save.
* @param array|bool $validate Either a boolean, or an array.
* If a boolean, indicates whether or not to validate before saving.
* If an array, can have following keys:
@@ -1788,11 +1802,16 @@ public function saveField($name, $value, $validate = false)
* @triggers Model.afterSave $this, array($created, $options)
* @link https://book.cakephp.org/2.0/en/models/saving-your-data.html
*/
- public function save($data = null, $validate = true, $fieldList = [])
- {
+ public function save(
+ array|null $data = null,
+ array|bool $validate = true,
+ array $fieldList = [],
+ ): mixed {
$defaults = [
- 'validate' => true, 'fieldList' => [],
- 'callbacks' => true, 'counterCache' => true,
+ 'validate' => true,
+ 'fieldList' => [],
+ 'callbacks' => true,
+ 'counterCache' => true,
'atomic' => true,
];
@@ -1831,7 +1850,7 @@ public function save($data = null, $validate = true, $fieldList = [])
* Saves model data (based on white-list, if supplied) to the database. By
* default, validation occurs before save.
*
- * @param array $data Data to save.
+ * @param array|null $data Data to save.
* @param array $options can have following keys:
*
* - validate: Set to true/false to enable or disable validation.
@@ -1839,11 +1858,11 @@ public function save($data = null, $validate = true, $fieldList = [])
* - callbacks: Set to false to disable callbacks. Using 'before' or 'after'
* will enable only those callbacks.
* - `counterCache`: Boolean to control updating of counter caches (if any)
- * @return mixed On success Model::$data if its not empty or true, false on failure
+ * @return array|bool On success Model::$data if its not empty or true, false on failure
* @throws PDOException
* @link https://book.cakephp.org/2.0/en/models/saving-your-data.html
*/
- protected function _doSave($data = null, $options = [])
+ protected function _doSave(?array $data = null, array $options = []): array|bool
{
$_whitelist = $this->whitelist;
$fields = [];
@@ -2008,14 +2027,14 @@ protected function _doSave($data = null, $options = [])
}
}
- if ($success && !empty($joined)) {
+ if ($success && !empty($joined) && $db instanceof DboSource) {
$this->_saveMulti($joined, $this->id, $db);
}
if (!$success) {
$this->whitelist = $_whitelist;
- return $success;
+ return false;
}
if ($count > 0) {
@@ -2047,7 +2066,7 @@ protected function _doSave($data = null, $options = [])
* @param string $field the field to check
* @return bool
*/
- protected function _isUUIDField($field)
+ protected function _isUUIDField(string $field): bool
{
$field = $this->schema($field);
@@ -2059,10 +2078,10 @@ protected function _isUUIDField($field)
*
* @param array $joined Data to save
* @param string|int $id ID of record in this model
- * @param DataSource $db Datasource instance.
+ * @param DboSource $db Datasource instance.
* @return void
*/
- protected function _saveMulti($joined, $id, $db)
+ protected function _saveMulti(array $joined, string|int $id, DboSource $db): void
{
foreach ($joined as $assoc => $data) {
if (!isset($this->hasAndBelongsToMany[$assoc])) {
@@ -2117,7 +2136,7 @@ protected function _saveMulti($joined, $id, $db)
}
$newData[] = $row;
- } elseif (isset($row[$join]) && isset($row[$join][$habtm['associationForeignKey']])) {
+ } elseif (isset($row[$join][$habtm['associationForeignKey']])) {
if (!empty($row[$join][$model->primaryKey])) {
$newJoins[] = $row[$join][$habtm['associationForeignKey']];
}
@@ -2127,6 +2146,7 @@ protected function _saveMulti($joined, $id, $db)
}
$keepExisting = $habtm['unique'] === 'keepExisting';
+ $associationForeignKey = null;
if ($habtm['unique']) {
$conditions = [
$join . '.' . $habtm['foreignKey'] => $id,
@@ -2156,13 +2176,13 @@ protected function _saveMulti($joined, $id, $db)
}
if (!empty($newData)) {
- foreach ($newData as $data) {
- $data[$habtm['foreignKey']] = $id;
- if (empty($data[$model->primaryKey])) {
+ foreach ($newData as $_data) {
+ $_data[$habtm['foreignKey']] = $id;
+ if (empty($_data[$model->primaryKey])) {
$model->create();
}
- $model->save($data, ['atomic' => false]);
+ $model->save($_data, ['atomic' => false]);
}
}
@@ -2196,7 +2216,7 @@ protected function _saveMulti($joined, $id, $db)
* 'counterScope' defined get updated
* @return void
*/
- public function updateCounterCache($keys = [], $created = false)
+ public function updateCounterCache(array $keys = [], bool $created = false): void
{
if (empty($keys) && isset($this->data[$this->alias])) {
$keys = $this->data[$this->alias];
@@ -2255,7 +2275,7 @@ public function updateCounterCache($keys = [], $created = false)
$conditions[$fkQuoted] = $keys[$foreignKey];
if ($recursive === 0) {
- $conditions = array_merge($conditions, (array)$conditions);
+ $conditions = array_merge($conditions, $conditions);
}
$count = (int)$this->find('count', compact('conditions', 'recursive'));
@@ -2275,7 +2295,7 @@ public function updateCounterCache($keys = [], $created = false)
* @return array Returns updated foreign key values, along with an 'old' key containing the old
* values, or empty if no foreign keys are updated.
*/
- protected function _prepareUpdateFields($data)
+ protected function _prepareUpdateFields(array $data): array
{
$foreignKeys = [];
foreach ($this->belongsTo as $assoc => $info) {
@@ -2334,7 +2354,7 @@ protected function _prepareUpdateFields($data)
* @link https://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveassociated-array-data-null-array-options-array
* @link https://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveall-array-data-null-array-options-array
*/
- public function saveAll($data = [], $options = [])
+ public function saveAll(array $data = [], array $options = []): mixed
{
$options += ['validate' => 'first'];
if (Hash::numeric(array_keys($data))) {
@@ -2366,15 +2386,15 @@ public function saveAll($data = [], $options = [])
* - `callbacks`: See Model::save()
* - `counterCache`: See Model::save()
*
- * @param array $data Record data to save. This should be a numerically-indexed array
+ * @param array|null $data Record data to save. This should be a numerically-indexed array
* @param array $options Options to use when saving record data, See $options above.
- * @return mixed If atomic: True on success, or false on failure.
+ * @return array|bool If atomic: True on success, or false on failure.
* Otherwise: array similar to the $data array passed, but values are set to true/false
* depending on whether each record saved successfully.
* @throws PDOException
* @link https://book.cakephp.org/2.0/en/models/saving-your-data.html#model-savemany-array-data-null-array-options-array
*/
- public function saveMany($data = null, $options = [])
+ public function saveMany(?array $data = null, array $options = []): array|bool
{
if (empty($data)) {
$data = $this->data;
@@ -2392,6 +2412,7 @@ public function saveMany($data = null, $options = [])
return !empty($result);
}
+ $validates = null;
if ($options['validate'] === 'first') {
$validates = $this->validateMany($data, $options);
if ((!$validates && $options['atomic']) || (!$options['atomic'] && in_array(false, $validates, true))) {
@@ -2401,6 +2422,7 @@ public function saveMany($data = null, $options = [])
}
$transactionBegun = false;
+ $db = null;
if ($options['atomic']) {
$db = $this->getDataSource();
$transactionBegun = $db->begin();
@@ -2409,17 +2431,14 @@ public function saveMany($data = null, $options = [])
try {
$return = [];
foreach ($data as $key => $record) {
- $validates = $this->create(null) !== null;
- $saved = false;
- if ($validates) {
- if ($options['deep']) {
- $saved = $this->saveAssociated($record, ['atomic' => false] + $options);
- } else {
- $saved = (bool)$this->save($record, ['atomic' => false] + $options);
- }
+ $this->create(null);
+ if ($options['deep']) {
+ $saved = $this->saveAssociated($record, ['atomic' => false] + $options);
+ } else {
+ $saved = (bool)$this->save($record, ['atomic' => false] + $options);
}
- $validates = ($validates && ($saved === true || (is_array($saved) && !in_array(false, Hash::flatten($saved), true))));
+ $validates = ($saved === true || (is_array($saved) && !in_array(false, Hash::flatten($saved), true)));
if (!$validates) {
$validationErrors[$key] = $this->validationErrors;
}
@@ -2477,7 +2496,7 @@ public function saveMany($data = null, $options = [])
* Otherwise: array similar to the $data array passed, but values are set to true/false
* depending on whether each record validated successfully.
*/
- public function validateMany(&$data, $options = [])
+ public function validateMany(array &$data, array $options = []): array|bool
{
return $this->validator()->validateMany($data, $options);
}
@@ -2503,7 +2522,7 @@ public function validateMany(&$data, $options = [])
* - `callbacks`: See Model::save()
* - `counterCache`: See Model::save()
*
- * @param array $data Record data to save. This should be an array indexed by association name.
+ * @param array|null $data Record data to save. This should be an array indexed by association name.
* @param array $options Options to use when saving record data, See $options above.
* @return mixed If atomic: True on success, or false on failure.
* Otherwise: array similar to the $data array passed, but values are set to true/false
@@ -2511,7 +2530,7 @@ public function validateMany(&$data, $options = [])
* @throws PDOException
* @link https://book.cakephp.org/2.0/en/models/saving-your-data.html#model-saveassociated-array-data-null-array-options-array
*/
- public function saveAssociated($data = null, $options = [])
+ public function saveAssociated(?array $data = null, array $options = []): mixed
{
if (empty($data)) {
$data = $this->data;
@@ -2539,6 +2558,7 @@ public function saveAssociated($data = null, $options = [])
}
$transactionBegun = false;
+ $db = null;
if ($options['atomic']) {
$db = $this->getDataSource();
$transactionBegun = $db->begin();
@@ -2558,7 +2578,6 @@ public function saveAssociated($data = null, $options = [])
$model = $this->{$association};
$validates = $model->create(null) !== null;
- $saved = false;
if ($validates) {
if ($options['deep']) {
$saved = $model->saveAssociated($values, ['atomic' => false] + $options);
@@ -2613,7 +2632,6 @@ public function saveAssociated($data = null, $options = [])
$validates = $model->create(null) !== null;
$saved = false;
-
if ($validates) {
$options = $model->_addToWhiteList($key, $options);
if ($options['deep']) {
@@ -2632,7 +2650,7 @@ public function saveAssociated($data = null, $options = [])
break;
case 'hasMany':
foreach ($values as $i => $value) {
- if (isset($values[$i][$association])) {
+ if (isset($value[$association])) {
$values[$i][$association][$key] = $this->id;
} else {
$values[$i] = array_merge([$key => $this->id], $value, [$key => $this->id]);
@@ -2689,7 +2707,7 @@ public function saveAssociated($data = null, $options = [])
* @param array $options Options list
* @return array options
*/
- protected function _addToWhiteList($key, $options)
+ protected function _addToWhiteList(string $key, array $options): array
{
if (empty($options['fieldList']) && $this->whitelist && !in_array($key, $this->whitelist)) {
$options['fieldList'][$this->alias] = $this->whitelist;
@@ -2730,7 +2748,7 @@ protected function _addToWhiteList($key, $options)
* Otherwise: array similar to the $data array passed, but values are set to true/false
* depending on whether each record validated successfully.
*/
- public function validateAssociated(&$data, $options = [])
+ public function validateAssociated(array &$data, array $options = []): array|bool
{
return $this->validator()->validateAssociated($data, $options);
}
@@ -2744,7 +2762,7 @@ public function validateAssociated(&$data, $options = [])
* @return bool True on success, false on failure
* @link https://book.cakephp.org/2.0/en/models/saving-your-data.html#model-updateall-array-fields-mixed-conditions
*/
- public function updateAll($fields, $conditions = true)
+ public function updateAll(array $fields, mixed $conditions = true): bool
{
return $this->getDataSource()->update($this, $fields, null, $conditions);
}
@@ -2752,14 +2770,14 @@ public function updateAll($fields, $conditions = true)
/**
* Removes record for given ID. If no ID is given, the current ID is used. Returns true on success.
*
- * @param string|int $id ID of record to delete
+ * @param string|int|null $id ID of record to delete
* @param bool $cascade Set to true to delete records that depend on this record
* @return bool True on success
* @triggers Model.beforeDelete $this, array($cascade)
* @triggers Model.afterDelete $this
* @link https://book.cakephp.org/2.0/en/models/deleting-data.html
*/
- public function delete($id = null, $cascade = true)
+ public function delete(string|int|null $id = null, bool $cascade = true): bool
{
if (!empty($id)) {
$this->id = $id;
@@ -2820,7 +2838,7 @@ public function delete($id = null, $cascade = true)
* @param bool $cascade Set to true to delete records that depend on this record
* @return void
*/
- protected function _deleteDependent($id, $cascade)
+ protected function _deleteDependent(string $id, bool $cascade): void
{
if ($cascade !== true) {
return;
@@ -2875,7 +2893,7 @@ protected function _deleteDependent($id, $cascade)
* @param string $id ID of record that was deleted
* @return void
*/
- protected function _deleteLinks($id)
+ protected function _deleteLinks(string $id): void
{
foreach ($this->hasAndBelongsToMany as $data) {
[, $joinModel] = pluginSplit($data['with']);
@@ -2904,8 +2922,11 @@ protected function _deleteLinks($id)
* @param array $relationshipConfig The relationship config defined on the primary model
* @return array
*/
- protected function _getConditionsForDeletingLinks(Model $model, $id, array $relationshipConfig)
- {
+ protected function _getConditionsForDeletingLinks(
+ Model $model,
+ mixed $id,
+ array $relationshipConfig,
+ ): array {
return [$model->escapeField($relationshipConfig['foreignKey']) => $id];
}
@@ -2918,8 +2939,11 @@ protected function _getConditionsForDeletingLinks(Model $model, $id, array $rela
* @return bool True on success, false on failure
* @link https://book.cakephp.org/2.0/en/models/deleting-data.html#deleteall
*/
- public function deleteAll($conditions, $cascade = true, $callbacks = false)
- {
+ public function deleteAll(
+ mixed $conditions,
+ bool $cascade = true,
+ bool $callbacks = false,
+ ): bool {
if (empty($conditions)) {
return false;
}
@@ -2930,11 +2954,17 @@ public function deleteAll($conditions, $cascade = true, $callbacks = false)
return $db->delete($this, $conditions);
}
- $ids = $this->find('all', array_merge([
- 'fields' => "{$this->alias}.{$this->primaryKey}",
- 'order' => false,
- 'group' => "{$this->alias}.{$this->primaryKey}",
- 'recursive' => 0], compact('conditions')),);
+ $ids = $this->find(
+ 'all',
+ array_merge([
+ 'fields' => "{$this->alias}.{$this->primaryKey}",
+ 'order' => false,
+ 'group' => "{$this->alias}.{$this->primaryKey}",
+ 'recursive' => 0,
+ ], [
+ 'conditions' => $conditions,
+ ]),
+ );
if ($ids === false || $ids === null) {
return false;
@@ -2973,7 +3003,7 @@ public function deleteAll($conditions, $cascade = true, $callbacks = false)
* @param string $type Association type.
* @return array
*/
- protected function _collectForeignKeys($type = 'belongsTo')
+ protected function _collectForeignKeys(string $type = 'belongsTo'): array
{
$result = [];
@@ -2993,10 +3023,10 @@ protected function _collectForeignKeys($type = 'belongsTo')
* and then performs a `Model::find('count')` on the currently configured datasource
* to ascertain the existence of the record in persistent storage.
*
- * @param string|int $id ID of record to check for existence
+ * @param string|int|bool|null $id ID of record to check for existence
* @return bool True if such a record exists
*/
- public function exists($id = null)
+ public function exists(string|int|bool|null $id = null): bool
{
if ($id === null) {
$id = $this->getID();
@@ -3022,10 +3052,10 @@ public function exists($id = null)
/**
* Returns true if a record that meets given conditions exists.
*
- * @param array $conditions SQL conditions array
+ * @param array|null $conditions SQL conditions array
* @return bool True if such a record exists
*/
- public function hasAny($conditions = null)
+ public function hasAny(?array $conditions = null): bool
{
return (bool)$this->find('count', ['conditions' => $conditions, 'recursive' => -1]);
}
@@ -3093,13 +3123,15 @@ public function hasAny($conditions = null)
*
* Note: find(count) has its own return values.
*
- * @param string $type Type of find operation (all / first / count / neighbors / list / threaded)
- * @param array $query Option fields (conditions / fields / joins / limit / offset / order / page / group / callbacks)
- * @return array|int|null Array of records, int if the type is count, or Null on failure.
+ * @param string|null $type Type of find operation (all / first / count / neighbors / list / threaded)
+ * @param array|null $query Option fields (conditions / fields / joins / limit / offset / order / page / group / callbacks)
+ * @return array|int|false|null Array of records, int if the type is count, or Null on failure.
* @link https://book.cakephp.org/2.0/en/models/retrieving-your-data.html
*/
- public function find(string $type = 'first', array $query = [])
- {
+ public function find(
+ ?string $type = 'first',
+ ?array $query = [],
+ ): array|int|false|null {
$this->findQueryType = $type;
$this->id = $this->getID();
@@ -3175,7 +3207,7 @@ public function buildQuery(string $type = 'first', array $query = []): ?array
'conditions' => null, 'fields' => null, 'joins' => [], 'limit' => null,
'offset' => null, 'order' => null, 'page' => 1, 'group' => null, 'callbacks' => true,
],
- (array)$query,
+ $query,
);
if ($this->findMethods[$type] === true) {
@@ -3224,7 +3256,7 @@ public function buildQuery(string $type = 'first', array $query = []): ?array
* @return array
* @see Model::find()
*/
- protected function _findAll($state, $query, $results = [])
+ protected function _findAll(string $state, array $query, array $results = []): array
{
if ($state === 'before') {
return $query;
@@ -3238,11 +3270,11 @@ protected function _findAll($state, $query, $results = [])
*
* @param string $state Either "before" or "after"
* @param array $query Query.
- * @param array $results Results.
+ * @param array|false $results Results.
* @return array
* @see Model::find()
*/
- protected function _findFirst($state, $query, $results = [])
+ protected function _findFirst(string $state, array $query, array|false $results = []): array
{
if ($state === 'before') {
$query['limit'] = 1;
@@ -3263,10 +3295,10 @@ protected function _findFirst($state, $query, $results = [])
* @param string $state Either "before" or "after"
* @param array $query Query.
* @param array $results Results.
- * @return int|false The number of records found, or false
+ * @return array|int|false The number of records found, or false
* @see Model::find()
*/
- protected function _findCount($state, $query, $results = [])
+ protected function _findCount(string $state, array $query, array $results = []): array|int|false
{
if ($state === 'before') {
if (!empty($query['type']) && isset($this->findMethods[$query['type']]) && $query['type'] !== 'count') {
@@ -3319,7 +3351,7 @@ protected function _findCount($state, $query, $results = [])
* @return array Key/value pairs of primary keys/display field values of all records found
* @see Model::find()
*/
- protected function _findList($state, $query, $results = [])
+ protected function _findList(string $state, array $query, array $results = []): array
{
if ($state === 'before') {
if (empty($query['fields'])) {
@@ -3356,7 +3388,7 @@ protected function _findList($state, $query, $results = [])
}
}
- if (!isset($query['recursive']) || $query['recursive'] === null) {
+ if (!isset($query['recursive'])) {
$query['recursive'] = -1;
}
[$query['list']['keyPath'], $query['list']['valuePath'], $query['list']['groupPath']] = $list;
@@ -3380,12 +3412,13 @@ protected function _findList($state, $query, $results = [])
* @param array $results Results.
* @return array
*/
- protected function _findNeighbors($state, $query, $results = [])
+ protected function _findNeighbors(string $state, array $query, array $results = []): array
{
- extract($query);
+ $field = $query['field'] ?? null;
+ $value = $query['value'] ?? null;
+ $conditions = $query['conditions'] ?? [];
if ($state === 'before') {
- $conditions = (array)$conditions;
if (isset($field) && isset($value)) {
if (!str_contains($field, '.')) {
$field = $this->alias . '.' . $field;
@@ -3395,7 +3428,7 @@ protected function _findNeighbors($state, $query, $results = [])
$value = $this->id;
}
- $query['conditions'] = array_merge($conditions, [$field . ' <' => $value]);
+ $query['conditions'] = array_merge((array)$conditions, [$field . ' <' => $value]);
$query['order'] = $field . ' DESC';
$query['limit'] = 1;
$query['field'] = $field;
@@ -3443,8 +3476,11 @@ protected function _findNeighbors($state, $query, $results = [])
* @param array $results Results.
* @return array Threaded results
*/
- protected function _findThreaded($state, $query, $results = [])
- {
+ protected function _findThreaded(
+ string $state,
+ array $query,
+ array $results = [],
+ ): array {
if ($state === 'before') {
return $query;
}
@@ -3465,11 +3501,13 @@ protected function _findThreaded($state, $query, $results = [])
*
* @param array $results Results to filter
* @param bool $primary If this is the primary model results (results from model where the find operation was performed)
- * @return array Set of filtered results
+ * @return array|false Set of filtered results
* @triggers Model.afterFind $this, array($results, $primary)
*/
- protected function _filterResults($results, $primary = true)
- {
+ protected function _filterResults(
+ array|false $results,
+ bool $primary = true,
+ ): array|false {
$event = new CakeEvent('Model.afterFind', $this, [$results, $primary]);
$event->modParams = 0;
$this->getEventManager()->dispatch($event);
@@ -3484,7 +3522,7 @@ protected function _filterResults($results, $primary = true)
*
* @return bool Success
*/
- public function resetAssociations()
+ public function resetAssociations(): bool
{
if (!empty($this->__backAssociation)) {
foreach ($this->_associations as $type) {
@@ -3520,8 +3558,11 @@ public function resetAssociations()
* @param mixed ...$args
* @return bool False if any records matching any fields are found
*/
- public function isUnique($fields, $or = true, ...$args): bool
- {
+ public function isUnique(
+ array|string $fields,
+ array|bool $or = true,
+ mixed ...$args,
+ ): bool {
if (is_array($or)) {
$isRule = (
array_key_exists('rule', $or) &&
@@ -3587,7 +3628,7 @@ public function isUnique($fields, $or = true, ...$args): bool
* @return mixed Resultset array or boolean indicating success / failure depending on the query executed
* @link https://book.cakephp.org/2.0/en/models/retrieving-your-data.html#model-query
*/
- public function query(...$params)
+ public function query(mixed ...$params): mixed
{
// use $this->cacheQueries as default when argument not explicitly given already
if (count($params) === 1 || count($params) === 2 && !is_bool($params[1])) {
@@ -3607,7 +3648,7 @@ public function query(...$params)
* @param array $options An optional array of custom options to be made available in the beforeValidate callback
* @return bool True if there are no errors
*/
- public function validates($options = [])
+ public function validates(array $options = []): bool
{
return $this->validator()->validates($options);
}
@@ -3618,10 +3659,10 @@ public function validates($options = [])
* Additionally it populates the validationErrors property of the model with the same array.
*
* @param array|string $options An optional array of custom options to be made available in the beforeValidate callback
- * @return array|bool Array of invalid fields and their error messages
+ * @return array|false Array of invalid fields and their error messages
* @see Model::validates()
*/
- public function invalidFields($options = [])
+ public function invalidFields(array|string $options = []): array|false
{
return $this->validator()->errors($options);
}
@@ -3635,7 +3676,7 @@ public function invalidFields($options = [])
* be returned. If no validation key is provided, defaults to true.
* @return void
*/
- public function invalidate($field, $value = true)
+ public function invalidate(string $field, mixed $value = true): void
{
$this->validator()->invalidate($field, $value);
}
@@ -3646,7 +3687,7 @@ public function invalidate($field, $value = true)
* @param string $field Returns true if the input string ends in "_id"
* @return bool True if the field is a foreign key listed in the belongsTo array.
*/
- public function isForeignKey($field)
+ public function isForeignKey(string $field): bool
{
$foreignKeys = [];
if (!empty($this->belongsTo)) {
@@ -3662,11 +3703,11 @@ public function isForeignKey($field)
* Escapes the field name and prepends the model name. Escaping is done according to the
* current database driver's rules.
*
- * @param string $field Field to escape (e.g: id)
- * @param string $alias Alias for the model (e.g: Post)
- * @return string The name of the escaped field for this Model (i.e. id becomes `Post`.`id`).
+ * @param string|null $field Field to escape (e.g: id)
+ * @param string|null $alias Alias for the model (e.g: Post)
+ * @return string|null The name of the escaped field for this Model (i.e. id becomes `Post`.`id`).
*/
- public function escapeField($field = null, $alias = null)
+ public function escapeField(?string $field = null, ?string $alias = null): ?string
{
if (empty($alias)) {
$alias = $this->alias;
@@ -3677,6 +3718,10 @@ public function escapeField($field = null, $alias = null)
}
$db = $this->getDataSource();
+ if (!method_exists($db, 'name')) {
+ return null;
+ }
+
if (str_starts_with($field, $db->name($alias) . '.')) {
return $field;
}
@@ -3690,7 +3735,7 @@ public function escapeField($field = null, $alias = null)
* @param int $list Index on which the composed ID is located
* @return mixed The ID of the current record, false if no ID
*/
- public function getID($list = 0)
+ public function getID(int $list = 0): mixed
{
if (empty($this->id) || (is_array($this->id) && isset($this->id[0]) && empty($this->id[0]))) {
return false;
@@ -3714,9 +3759,9 @@ public function getID($list = 0)
/**
* Returns the ID of the last record this model inserted.
*
- * @return mixed Last inserted ID
+ * @return string|int|null Last inserted ID
*/
- public function getLastInsertID()
+ public function getLastInsertID(): string|int|null
{
return $this->getInsertID();
}
@@ -3724,9 +3769,9 @@ public function getLastInsertID()
/**
* Returns the ID of the last record this model inserted.
*
- * @return mixed Last inserted ID
+ * @return string|int|null Last inserted ID
*/
- public function getInsertID()
+ public function getInsertID(): string|int|null
{
return $this->_insertID;
}
@@ -3734,10 +3779,10 @@ public function getInsertID()
/**
* Sets the ID of the last record this model inserted
*
- * @param string|int $id Last inserted ID
+ * @param string|int|null $id Last inserted ID
* @return void
*/
- public function setInsertID($id)
+ public function setInsertID(string|int|null $id): void
{
$this->_insertID = $id;
}
@@ -3745,9 +3790,9 @@ public function setInsertID($id)
/**
* Returns the number of rows returned from the last query.
*
- * @return int Number of rows
+ * @return int|false Number of rows
*/
- public function getNumRows()
+ public function getNumRows(): int|false
{
return $this->getDataSource()->lastNumRows();
}
@@ -3755,9 +3800,9 @@ public function getNumRows()
/**
* Returns the number of rows affected by the last query.
*
- * @return int Number of rows
+ * @return int|false Number of rows
*/
- public function getAffectedRows()
+ public function getAffectedRows(): int|false
{
return $this->getDataSource()->lastAffected();
}
@@ -3765,11 +3810,11 @@ public function getAffectedRows()
/**
* Sets the DataSource to which this model is bound.
*
- * @param string $dataSource The name of the DataSource, as defined in app/Config/database.php
+ * @param string|null $dataSource The name of the DataSource, as defined in app/Config/database.php
* @return void
* @throws MissingConnectionException
*/
- public function setDataSource($dataSource = null)
+ public function setDataSource(?string $dataSource = null): void
{
$oldConfig = $this->useDbConfig;
@@ -3816,7 +3861,7 @@ public function getDataSource(): DataSource
*
* @return array
*/
- public function associations()
+ public function associations(): array
{
return $this->_associations;
}
@@ -3824,10 +3869,10 @@ public function associations()
/**
* Gets all the models with which this model is associated.
*
- * @param string $type Only result associations of this type
+ * @param string|null $type Only result associations of this type
* @return array|null Associations
*/
- public function getAssociated($type = null)
+ public function getAssociated(?string $type = null): ?array
{
if (!$type) {
$associated = [];
@@ -3876,16 +3921,18 @@ public function getAssociated($type = null)
* Gets the name and fields to be used by a join model. This allows specifying join fields
* in the association definition.
*
- * @param array|string $assoc The model to be joined
+ * @param mixed $assoc The model to be joined
* @param array $keys Any join keys which must be merged with the keys queried
* @return array
*/
- public function joinModel(array|string $assoc, array $keys = []): array
+ public function joinModel(mixed $assoc, array $keys = []): array
{
if (is_string($assoc)) {
[, $assoc] = pluginSplit($assoc);
+ $schema = $this->{$assoc}->schema();
+ $schemaKeys = array_keys($schema ?: []);
- return [$assoc, array_keys($this->{$assoc}->schema())];
+ return [$assoc, $schemaKeys];
}
if (is_array($assoc)) {
@@ -3907,11 +3954,11 @@ public function joinModel(array|string $assoc, array $keys = []): array
* call, otherwise return the (modified) query data.
*
* @param array $query Data used to execute this query, i.e. conditions, order, etc.
- * @return mixed true if the operation should continue, false if it should abort; or, modified
+ * @return array|bool|null true if the operation should continue, false if it should abort; or, modified
* $query to continue with new $query
* @link https://book.cakephp.org/2.0/en/models/callback-methods.html#beforefind
*/
- public function beforeFind($query)
+ public function beforeFind(array $query): array|bool|null
{
return true;
}
@@ -3925,7 +3972,7 @@ public function beforeFind($query)
* @return mixed Result of the find operation
* @link https://book.cakephp.org/2.0/en/models/callback-methods.html#afterfind
*/
- public function afterFind($results, bool $primary = false)
+ public function afterFind(mixed $results, bool $primary = false): mixed
{
return $results;
}
@@ -3965,7 +4012,7 @@ public function afterSave(bool $created, array $options = []): ?bool
* @return bool|null True if the operation should continue, false if it should abort
* @link https://book.cakephp.org/2.0/en/models/callback-methods.html#beforedelete
*/
- public function beforeDelete($cascade = true): ?bool
+ public function beforeDelete(bool $cascade = true): ?bool
{
return true;
}
@@ -3986,7 +4033,7 @@ public function afterDelete(): ?bool
* validation rules can be defined in $validate.
*
* @param array $options Options passed from Model::save().
- * @return bool True if validate operation should continue, false to abort
+ * @return bool|null True if validate operation should continue, false to abort
* @link https://book.cakephp.org/2.0/en/models/callback-methods.html#beforevalidate
* @see Model::save()
*/
diff --git a/src/Model/ModelBehavior.php b/src/Model/ModelBehavior.php
index 6bd53724c4..c9a384b9f5 100644
--- a/src/Model/ModelBehavior.php
+++ b/src/Model/ModelBehavior.php
@@ -95,7 +95,7 @@ class ModelBehavior extends CakeObject
* @param array $config Configuration settings for $model
* @return void
*/
- public function setup(Model $model, $config = [])
+ public function setup(Model $model, array $config = []): void
{
}
@@ -121,10 +121,10 @@ public function cleanup(Model $model)
*
* @param Model $model Model using this behavior
* @param array $query Data used to execute this query, i.e. conditions, order, etc.
- * @return array|bool False or null will abort the operation. You can return an array to replace the
+ * @return array|bool|null False or null will abort the operation. You can return an array to replace the
* $query that will be eventually run.
*/
- public function beforeFind(Model $model, $query)
+ public function beforeFind(Model $model, array $query): array|bool|null
{
return true;
}
@@ -137,8 +137,9 @@ public function beforeFind(Model $model, $query)
* @param bool $primary Whether this model is being queried directly (vs. being queried as an association)
* @return mixed|void An array value will replace the value of $results - any other value will be ignored.
*/
- public function afterFind(Model $model, $results, $primary = false)
+ public function afterFind(Model $model, mixed $results, bool $primary = false): mixed
{
+ return null;
}
/**
diff --git a/src/Model/ModelValidator.php b/src/Model/ModelValidator.php
index c80bf79a74..af58aec8f1 100644
--- a/src/Model/ModelValidator.php
+++ b/src/Model/ModelValidator.php
@@ -150,7 +150,7 @@ public function validateAssociated(&$data, $options = [])
$validationErrors[$model->alias] = $model->validationErrors;
$return[$model->alias] = false;
}
- $data = $model->data;
+ $data = $model->data ?: [];
if (!empty($options['deep']) && isset($data[$model->alias])) {
$recordData = $data[$model->alias];
unset($data[$model->alias]);
diff --git a/src/Model/Permission.php b/src/Model/Permission.php
index b492c0fdd9..5e69bcb7ce 100644
--- a/src/Model/Permission.php
+++ b/src/Model/Permission.php
@@ -41,7 +41,7 @@ class Permission extends AppModel
/**
* Override default table name
*
- * @var string
+ * @var string|bool|null
*/
public string|bool|null $useTable = 'aros_acos';
@@ -75,13 +75,16 @@ public function __construct()
/**
* Checks if the given $aro has access to action $action in $aco
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
+ * @param Model|array|string $aro ARO The requesting object identifier.
+ * @param Model|array|string $aco ACO The controlled object identifier.
* @param string $action Action (defaults to *)
* @return bool Success (true if ARO has access to action in ACO, false otherwise)
*/
- public function check($aro, $aco, $action = '*')
- {
+ public function check(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ string $action = '*',
+ ): bool {
if (!$aro || !$aco) {
return false;
}
@@ -183,15 +186,19 @@ public function check($aro, $aco, $action = '*')
/**
* Allow $aro to have access to action $actions in $aco
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
- * @param string $actions Action (defaults to *) Invalid permissions will result in an exception
+ * @param Model|array|string $aro ARO The requesting object identifier.
+ * @param Model|array|string $aco ACO The controlled object identifier.
+ * @param array|string $actions Action (defaults to *) Invalid permissions will result in an exception
* @param int $value Value to indicate access type (1 to give access, -1 to deny, 0 to inherit)
* @return bool Success
* @throws AclException on Invalid permission key.
*/
- public function allow($aro, $aco, $actions = '*', $value = 1)
- {
+ public function allow(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ array|string $actions = '*',
+ int $value = 1,
+ ): bool {
$perms = $this->getAclLink($aro, $aco);
$permKeys = $this->getAcoKeys($this->schema());
$save = [];
@@ -236,12 +243,14 @@ public function allow($aro, $aco, $actions = '*', $value = 1)
/**
* Get an array of access-control links between the given Aro and Aco
*
- * @param string $aro ARO The requesting object identifier.
- * @param string $aco ACO The controlled object identifier.
- * @return array Indexed array with: 'aro', 'aco' and 'link'
+ * @param Model|array|string $aro ARO The requesting object identifier.
+ * @param Model|array|string $aco ACO The controlled object identifier.
+ * @return array|false Indexed array with: 'aro', 'aco' and 'link'
*/
- public function getAclLink($aro, $aco)
- {
+ public function getAclLink(
+ Model|array|string|null $aro,
+ Model|array|string|null $aco,
+ ): array|false {
$obj = [];
$obj['Aro'] = $this->Aro->node($aro);
$obj['Aco'] = $this->Aco->node($aco);
@@ -270,7 +279,7 @@ public function getAclLink($aro, $aco)
* @param array $keys Permission schema
* @return array permission keys
*/
- public function getAcoKeys($keys)
+ public function getAcoKeys($keys): array
{
$newKeys = [];
$keys = array_keys($keys);
diff --git a/src/Model/Validator/CakeValidationRule.php b/src/Model/Validator/CakeValidationRule.php
index 6f5290b7ef..dfe8a5a086 100644
--- a/src/Model/Validator/CakeValidationRule.php
+++ b/src/Model/Validator/CakeValidationRule.php
@@ -34,86 +34,86 @@ class CakeValidationRule
/**
* Whether the field passed this validation rule
*
- * @var mixed
+ * @var string|bool
*/
- protected $_valid = true;
+ protected string|bool $_valid = true;
/**
* Holds whether the record being validated exists in datasource or not
*
* @var bool
*/
- protected $_recordExists = false;
+ protected bool $_recordExists = false;
/**
* Validation method
*
* @var mixed
*/
- protected $_rule = null;
+ protected mixed $_rule = null;
/**
* Validation method arguments
*
* @var array
*/
- protected $_ruleParams = [];
+ protected array $_ruleParams = [];
/**
* Holds passed in options
*
* @var array
*/
- protected $_passedOptions = [];
+ protected array $_passedOptions = [];
/**
* The 'rule' key
*
* @var mixed
*/
- public $rule = 'blank';
+ public mixed $rule = 'blank';
/**
* The 'required' key
*
- * @var mixed
+ * @var string|bool|null
*/
- public $required = null;
+ public string|bool|null $required = null;
/**
* The 'allowEmpty' key
*
- * @var bool
+ * @var bool|null
*/
- public $allowEmpty = null;
+ public ?bool $allowEmpty = null;
/**
* The 'on' key
*
- * @var string
+ * @var string|null
*/
- public $on = null;
+ public ?string $on = null;
/**
* The 'last' key
*
* @var bool
*/
- public $last = true;
+ public bool $last = true;
/**
* The 'message' key
*
- * @var string
+ * @var array|string|null
*/
- public $message = null;
+ public array|string|null $message = null;
/**
* Constructor
*
- * @param array $validator [optional] The validator properties
+ * @param array|string|null $validator [optional] The validator properties
*/
- public function __construct($validator = [])
+ public function __construct(array|string|null $validator = [])
{
$this->_addValidatorProps($validator);
}
@@ -123,7 +123,7 @@ public function __construct($validator = [])
*
* @return bool
*/
- public function isValid()
+ public function isValid(): bool
{
if (!$this->_valid || (is_string($this->_valid) && !empty($this->_valid))) {
return false;
@@ -137,7 +137,7 @@ public function isValid()
*
* @return bool
*/
- public function isEmptyAllowed()
+ public function isEmptyAllowed(): bool
{
return $this->skip() || $this->allowEmpty === true;
}
@@ -145,9 +145,9 @@ public function isEmptyAllowed()
/**
* Checks if the field is required according to the `required` property
*
- * @return bool
+ * @return bool|null
*/
- public function isRequired()
+ public function isRequired(): ?bool
{
if (in_array($this->required, ['create', 'update'], true)) {
if ($this->required === 'create' && !$this->isUpdate() || $this->required === 'update' && $this->isUpdate()) {
@@ -163,11 +163,11 @@ public function isRequired()
/**
* Checks whether the field failed the `field should be present` validation
*
- * @param string $field Field name
+ * @param string|null $field Field name
* @param array &$data Data to check rule against
* @return bool
*/
- public function checkRequired($field, &$data)
+ public function checkRequired(?string $field, array $data): bool
{
return (!array_key_exists($field, $data) && $this->isRequired() === true) ||
(
@@ -179,11 +179,11 @@ public function checkRequired($field, &$data)
/**
* Checks if the allowEmpty key applies
*
- * @param string $field Field name
+ * @param string|null $field Field name
* @param array &$data data to check rule against
* @return bool
*/
- public function checkEmpty($field, &$data)
+ public function checkEmpty(?string $field, array $data): bool
{
if (empty($data[$field]) && $data[$field] != '0' && $this->allowEmpty === true) {
return true;
@@ -197,7 +197,7 @@ public function checkEmpty($field, &$data)
*
* @return bool True if the ValidationRule can be skipped
*/
- public function skip()
+ public function skip(): bool
{
if (!empty($this->on)) {
if ($this->on === 'create' && $this->isUpdate() || $this->on === 'update' && !$this->isUpdate()) {
@@ -214,17 +214,17 @@ public function skip()
*
* @return bool
*/
- public function isLast()
+ public function isLast(): bool
{
- return (bool)$this->last;
+ return $this->last;
}
/**
* Gets the validation error message
*
- * @return string
+ * @return string|bool
*/
- public function getValidationResult()
+ public function getValidationResult(): string|bool
{
return $this->_valid;
}
@@ -234,7 +234,7 @@ public function getValidationResult()
*
* @return array
*/
- protected function _getPropertiesArray()
+ protected function _getPropertiesArray(): array
{
$rule = $this->rule;
if (!is_string($rule)) {
@@ -259,27 +259,28 @@ protected function _getPropertiesArray()
* If called with no parameters it will return whether this rule
* is configured for update operations or not.
*
- * @param bool $exists Boolean to indicate if records exists
+ * @param bool|null $exists Boolean to indicate if records exists
* @return bool
*/
- public function isUpdate($exists = null)
+ public function isUpdate(?bool $exists = null): bool
{
if ($exists === null) {
return $this->_recordExists;
}
+ $this->_recordExists = $exists;
- return $this->_recordExists = $exists;
+ return $this->_recordExists;
}
/**
* Dispatches the validation rule to the given validator method
*
* @param string $field Field name
- * @param array &$data Data array
- * @param array &$methods Methods list
+ * @param array $data Data array
+ * @param array $methods Methods list
* @return bool True if the rule could be dispatched, false otherwise
*/
- public function process($field, &$data, &$methods)
+ public function process(string $field, array $data, array $methods): bool
{
$this->_valid = true;
$this->_parseRule($field, $data);
@@ -293,7 +294,7 @@ public function process($field, &$data, &$methods)
} elseif (class_exists(Validation::class) && method_exists(Validation::class, $this->_rule)) {
$this->_valid = call_user_func_array([Validation::class, $this->_rule], $this->_ruleParams);
} elseif (is_string($validator['rule'])) {
- $this->_valid = preg_match($this->_rule, $data[$field]);
+ $this->_valid = (bool)preg_match($this->_rule, $data[$field]);
} else {
trigger_error(__d('cake_dev', 'Could not find validation handler %s for %s', $this->_rule, $field), E_USER_WARNING);
@@ -309,7 +310,7 @@ public function process($field, &$data, &$methods)
*
* @return void
*/
- public function reset()
+ public function reset(): void
{
$this->_valid = true;
$this->_recordExists = false;
@@ -321,7 +322,7 @@ public function reset()
* @param string|int $key Array index
* @return array|null
*/
- public function getOptions($key)
+ public function getOptions(string|int $key): ?array
{
if (!isset($this->_passedOptions[$key])) {
return null;
@@ -333,21 +334,19 @@ public function getOptions($key)
/**
* Sets the rule properties from the rule entry in validate
*
- * @param array $validator [optional]
+ * @param array|string|null $validator [optional]
* @return void
*/
- protected function _addValidatorProps($validator = [])
+ protected function _addValidatorProps(array|string|null $validator = []): void
{
if (!is_array($validator)) {
$validator = ['rule' => $validator];
}
foreach ($validator as $key => $value) {
- if (isset($value) || !empty($value)) {
- if (in_array($key, ['rule', 'required', 'allowEmpty', 'on', 'message', 'last'])) {
- $this->{$key} = $validator[$key];
- } else {
- $this->_passedOptions[$key] = $value;
- }
+ if (in_array($key, ['rule', 'required', 'allowEmpty', 'on', 'message', 'last'])) {
+ $this->{$key} = $value;
+ } else {
+ $this->_passedOptions[$key] = $value;
}
}
}
@@ -356,10 +355,10 @@ protected function _addValidatorProps($validator = [])
* Parses the rule and sets the rule and ruleParams
*
* @param string $field Field name
- * @param array &$data Data array
+ * @param array $data Data array
* @return void
*/
- protected function _parseRule($field, &$data)
+ protected function _parseRule(string $field, array $data): void
{
if (is_array($this->rule)) {
$this->_rule = $this->rule[0];
diff --git a/src/Model/Validator/CakeValidationSet.php b/src/Model/Validator/CakeValidationSet.php
index dbab20bb07..3668a9bf74 100644
--- a/src/Model/Validator/CakeValidationSet.php
+++ b/src/Model/Validator/CakeValidationSet.php
@@ -40,54 +40,54 @@ class CakeValidationSet implements ArrayAccess, IteratorAggregate, Countable
*
* @var array
*/
- protected $_rules = [];
+ protected array $_rules = [];
/**
* List of methods available for validation
*
* @var array
*/
- protected $_methods = [];
+ protected array $_methods = [];
/**
* I18n domain for validation messages.
*
- * @var string
+ * @var string|null
*/
- protected $_validationDomain = null;
+ protected ?string $_validationDomain = null;
/**
* Whether the validation is stopped
*
* @var bool
*/
- public $isStopped = false;
+ public bool $isStopped = false;
/**
* Holds the fieldname
*
- * @var string
+ * @var string|null
*/
- public $field = null;
+ public ?string $field = null;
/**
* Holds the original ruleSet
*
* @var array
*/
- public $ruleSet = [];
+ public array $ruleSet = [];
/**
* Constructor
*
* @param string $fieldName The fieldname.
- * @param array $ruleSet Rules set.
+ * @param array|string $ruleSet Rules set.
*/
- public function __construct($fieldName, $ruleSet)
+ public function __construct(string $fieldName, array|string $ruleSet)
{
$this->field = $fieldName;
- if (!is_array($ruleSet) || (is_array($ruleSet) && isset($ruleSet['rule']))) {
+ if (!is_array($ruleSet) || isset($ruleSet['rule'])) {
$ruleSet = [$ruleSet];
}
@@ -103,7 +103,7 @@ public function __construct($fieldName, $ruleSet)
* @param array &$methods Methods list
* @return void
*/
- public function setMethods(&$methods)
+ public function setMethods(array &$methods): void
{
$this->_methods =& $methods;
}
@@ -111,10 +111,10 @@ public function setMethods(&$methods)
/**
* Sets the I18n domain for validation messages.
*
- * @param string $validationDomain The validation domain to be used.
+ * @param string|null $validationDomain The validation domain to be used.
* @return void
*/
- public function setValidationDomain($validationDomain)
+ public function setValidationDomain(?string $validationDomain): void
{
$this->_validationDomain = $validationDomain;
}
@@ -127,7 +127,7 @@ public function setValidationDomain($validationDomain)
* @param bool $isUpdate Is record being updated or created
* @return array list of validation errors for this field
*/
- public function validate($data, $isUpdate = false)
+ public function validate(array $data, bool $isUpdate = false): array
{
$this->reset();
$errors = [];
@@ -161,7 +161,7 @@ public function validate($data, $isUpdate = false)
*
* @return void
*/
- public function reset()
+ public function reset(): void
{
foreach ($this->getRules() as $rule) {
$rule->reset();
@@ -188,7 +188,7 @@ public function getRule(string $name): ?CakeValidationRule
*
* @return array
*/
- public function getRules()
+ public function getRules(): array
{
return $this->_rules;
}
@@ -208,7 +208,7 @@ public function getRules()
* @param CakeValidationRule|array $rule The validation rule to be set
* @return self
*/
- public function setRule($name, $rule)
+ public function setRule(string $name, CakeValidationRule|array $rule): self
{
if (!($rule instanceof CakeValidationRule)) {
$rule = new CakeValidationRule($rule);
@@ -232,7 +232,7 @@ public function setRule($name, $rule)
* @param string $name The name under which the rule should be unset
* @return self
*/
- public function removeRule($name)
+ public function removeRule(string $name): self
{
unset($this->_rules[$name]);
@@ -255,7 +255,7 @@ public function removeRule($name)
* @param bool $mergeVars [optional] If true, merges vars instead of replace. Defaults to true.
* @return self
*/
- public function setRules($rules = [], $mergeVars = true)
+ public function setRules(array $rules = [], bool $mergeVars = true): self
{
if ($mergeVars === false) {
$this->_rules = [];
@@ -270,11 +270,11 @@ public function setRules($rules = [], $mergeVars = true)
/**
* Fetches the correct error message for a failed validation
*
- * @param string $name the name of the rule as it was configured
+ * @param string|int|null $name the name of the rule as it was configured
* @param CakeValidationRule $rule the object containing validation information
- * @return string
+ * @return string|null
*/
- protected function _processValidationResponse($name, $rule)
+ protected function _processValidationResponse(string|int|null $name, CakeValidationRule $rule): ?string
{
$message = $rule->getValidationResult();
if (is_string($message)) {
@@ -314,10 +314,10 @@ protected function _processValidationResponse($name, $rule)
/**
* Applies translations to validator arguments.
*
- * @param array $args The args to translate
- * @return array Translated args.
+ * @param array|string|null $args The args to translate
+ * @return array|null Translated args.
*/
- protected function _translateArgs($args)
+ protected function _translateArgs(array|string|null $args): ?array
{
foreach ((array)$args as $k => $arg) {
if (is_string($arg)) {
@@ -331,23 +331,23 @@ protected function _translateArgs($args)
/**
* Returns whether an index exists in the rule set
*
- * @param mixed $index name of the rule
+ * @param mixed $offset name of the rule
* @return bool
*/
- public function offsetExists(mixed $index): bool
+ public function offsetExists(mixed $offset): bool
{
- return isset($this->_rules[$index]);
+ return isset($this->_rules[$offset]);
}
/**
* Returns a rule object by its index
*
- * @param mixed $index name of the rule
+ * @param mixed $offset name of the rule
* @return CakeValidationRule
*/
- public function offsetGet(mixed $index): mixed
+ public function offsetGet(mixed $offset): CakeValidationRule
{
- return $this->_rules[$index];
+ return $this->_rules[$offset];
}
/**
@@ -356,25 +356,25 @@ public function offsetGet(mixed $index): mixed
* This is a wrapper for ArrayAccess. Use setRule() directly for
* chainable access.
*
- * @param string $index Name of the rule.
- * @param CakeValidationRule|array $rule Rule to add to $index.
+ * @param string $offset Name of the rule.
+ * @param CakeValidationRule|array $value Rule to add to $index.
* @return void
* @see http://www.php.net/manual/en/arrayobject.offsetset.php
*/
- public function offsetSet(mixed $index, mixed $rule): void
+ public function offsetSet(mixed $offset, mixed $value): void
{
- $this->setRule($index, $rule);
+ $this->setRule($offset, $value);
}
/**
* Unsets a validation rule
*
- * @param string $index name of the rule
+ * @param string $offset name of the rule
* @return void
*/
- public function offsetUnset(mixed $index): void
+ public function offsetUnset(mixed $offset): void
{
- unset($this->_rules[$index]);
+ unset($this->_rules[$offset]);
}
/**
diff --git a/src/Network/CakeRequest.php b/src/Network/CakeRequest.php
index 5eb5b1c6a9..c70008d2f7 100644
--- a/src/Network/CakeRequest.php
+++ b/src/Network/CakeRequest.php
@@ -80,9 +80,9 @@ class CakeRequest implements ArrayAccess
/**
* Base URL path.
*
- * @var string
+ * @var string|false
*/
- public string|bool $base = false;
+ public string|false $base = false;
/**
* webroot path segment for the request.
@@ -94,7 +94,7 @@ class CakeRequest implements ArrayAccess
/**
* The full address to the current request
*
- * @var string
+ * @var string|null
*/
public ?string $here = null;
@@ -139,10 +139,10 @@ class CakeRequest implements ArrayAccess
/**
* Constructor
*
- * @param string $url Trimmed URL string to use. Should not contain the application base path.
+ * @param string|null $url Trimmed URL string to use. Should not contain the application base path.
* @param bool $parseEnvironment Set to false to not auto parse the environment. ie. GET, POST and FILES.
*/
- public function __construct($url = null, $parseEnvironment = true)
+ public function __construct(?string $url = null, bool $parseEnvironment = true)
{
$this->_base();
if (empty($url)) {
@@ -174,7 +174,7 @@ public function __construct($url = null, $parseEnvironment = true)
*
* @return void
*/
- protected function _processPost()
+ protected function _processPost(): void
{
if ($_POST) {
$this->data = $_POST;
@@ -192,8 +192,7 @@ protected function _processPost()
$override = $this->data['_method'];
}
- $isArray = is_array($this->data);
- if ($isArray && isset($this->data['_method'])) {
+ if (isset($this->data['_method'])) {
if (!empty($_SERVER)) {
$_SERVER['REQUEST_METHOD'] = $this->data['_method'];
} else {
@@ -207,7 +206,7 @@ protected function _processPost()
$this->data = [];
}
- if ($isArray && isset($this->data['data'])) {
+ if (isset($this->data['data'])) {
$data = $this->data['data'];
if (count($this->data) <= 1) {
$this->data = $data;
@@ -223,7 +222,7 @@ protected function _processPost()
*
* @return void
*/
- protected function _processGet()
+ protected function _processGet(): void
{
$query = $_GET;
@@ -248,7 +247,7 @@ protected function _processGet()
*
* @return string URI The CakePHP request path that is being accessed.
*/
- protected function _url()
+ protected function _url(): string
{
$uri = '';
if (!empty($_SERVER['PATH_INFO'])) {
@@ -305,19 +304,20 @@ protected function _url()
*
* @return string Base URL
*/
- protected function _base()
+ protected function _base(): string
{
- $dir = $webroot = null;
$config = Configure::read('App');
- extract($config);
- if (!isset($base)) {
- $base = $this->base;
- }
+ $base = $config['base'] ?? $this->base;
+ $baseUrl = $config['baseUrl'] ?? null;
+ $dir = $config['dir'] ?? null;
+ $webroot = $config['webroot'] ?? null;
+
if ($base !== false) {
$this->webroot = $base . '/';
+ $this->base = $base;
- return $this->base = $base;
+ return $this->base;
}
if (empty($baseUrl)) {
@@ -336,19 +336,20 @@ protected function _base()
$base = dirname($base);
}
- if ($base === DS || $base === '.') {
+ if ($base === DIRECTORY_SEPARATOR || $base === '.') {
$base = '';
}
$base = implode('/', array_map('rawurlencode', explode('/', $base)));
$this->webroot = $base . '/';
+ $this->base = $base;
- return $this->base = $base;
+ return $this->base;
}
$file = '/' . basename($baseUrl);
$base = dirname($baseUrl);
- if ($base === DS || $base === '.') {
+ if ($base === DIRECTORY_SEPARATOR || $base === '.') {
$base = '';
}
$this->webroot = $base . '/';
@@ -364,8 +365,9 @@ protected function _base()
$this->webroot .= $webroot . '/';
}
}
+ $this->base = $base . $file;
- return $this->base = $base . $file;
+ return $this->base;
}
/**
@@ -373,13 +375,11 @@ protected function _base()
*
* @return void
*/
- protected function _processFiles()
+ protected function _processFiles(): void
{
- if (isset($_FILES) && is_array($_FILES)) {
- foreach ($_FILES as $name => $data) {
- if ($name !== 'data') {
- $this->params['form'][$name] = $data;
- }
+ foreach ($_FILES as $name => $data) {
+ if ($name !== 'data') {
+ $this->params['form'][$name] = $data;
}
}
@@ -394,12 +394,12 @@ protected function _processFiles()
* Recursively walks the FILES array restructuring the data
* into something sane and useable.
*
- * @param string $path The dot separated path to insert $data into.
+ * @param string|null $path The dot separated path to insert $data into.
* @param array $data The data to traverse/insert.
* @param string $field The terminal field name, which is the top level key in $_FILES.
* @return void
*/
- protected function _processFileData($path, $data, $field)
+ protected function _processFileData(?string $path, array $data, string $field): void
{
foreach ($data as $key => $fields) {
$newPath = $key;
@@ -418,9 +418,9 @@ protected function _processFileData($path, $data, $field)
/**
* Get the content type used in this request.
*
- * @return string
+ * @return string|null
*/
- public function contentType()
+ public function contentType(): ?string
{
$type = env('CONTENT_TYPE');
if ($type) {
@@ -435,12 +435,12 @@ public function contentType()
*
* @param bool $safe Use safe = false when you think the user might manipulate their HTTP_CLIENT_IP
* header. Setting $safe = false will also look at HTTP_X_FORWARDED_FOR
- * @return string The client IP.
+ * @return string|false The client IP.
*/
- public function clientIp($safe = true)
+ public function clientIp(bool $safe = true): string|false
{
if (!$safe && env('HTTP_X_FORWARDED_FOR')) {
- $ipaddr = preg_replace('/(?:,.*)/', '', env('HTTP_X_FORWARDED_FOR'));
+ $ipaddr = preg_replace('/,.*/', '', env('HTTP_X_FORWARDED_FOR'));
} elseif (!$safe && env('HTTP_CLIENT_IP')) {
$ipaddr = env('HTTP_CLIENT_IP');
} else {
@@ -485,10 +485,10 @@ public function referer(bool $local = false): string
*
* @param string $name The method called
* @param array $params Array of parameters for the method call
- * @return mixed
+ * @return bool
* @throws CakeException when an invalid method is called.
*/
- public function __call(string $name, $params)
+ public function __call(string $name, array $params): bool
{
if (str_starts_with($name, 'is')) {
$type = strtolower(substr($name, 2));
@@ -507,7 +507,7 @@ public function __call(string $name, $params)
* @param string $name The property being accessed.
* @return mixed Either the value of the parameter or null.
*/
- public function __get(string $name)
+ public function __get(string $name): mixed
{
return $this->params[$name] ?? null;
}
@@ -533,9 +533,9 @@ public function __isset(string $name): bool
*
* @param array|string $type The type of request you want to check. If an array
* this method will return true if the request matches any type.
- * @return bool Whether or not the request is the type you are checking.
+ * @return mixed|bool Whether or not the request is the type you are checking.
*/
- public function is($type)
+ public function is(array|string $type): mixed
{
if (is_array($type)) {
foreach ($type as $_type) {
@@ -576,7 +576,7 @@ public function is($type)
* @param array $detect Detector options array.
* @return bool Whether or not the request is the type you are checking.
*/
- protected function _extensionDetector($detect)
+ protected function _extensionDetector(array $detect): bool
{
if (is_string($detect['extension'])) {
$detect['extension'] = [$detect['extension']];
@@ -640,10 +640,10 @@ protected function _paramDetector(array $detect): bool
if (isset($detect['value'])) {
$value = $detect['value'];
- return isset($this->params[$key]) ? $this->params[$key] == $value : false;
+ return isset($this->params[$key]) && $this->params[$key] == $value;
}
if (isset($detect['options'])) {
- return isset($this->params[$key]) ? in_array($this->params[$key], $detect['options']) : false;
+ return isset($this->params[$key]) && in_array($this->params[$key], $detect['options']);
}
return false;
@@ -759,9 +759,9 @@ public function addDetector(string $name, array $options): void
* @param array $params Array of parameters to merge in
* @return self
*/
- public function addParams($params)
+ public function addParams(array $params): self
{
- $this->params = array_merge($this->params, (array)$params);
+ $this->params = array_merge($this->params, $params);
return $this;
}
@@ -807,9 +807,9 @@ public function here(bool $base = true): string
* Read an HTTP header from the Request information.
*
* @param string $name Name of the header you want.
- * @return mixed Either false on no header being set or the value of the header.
+ * @return string|false Either false on no header being set or the value of the header.
*/
- public static function header(string $name)
+ public static function header(string $name): string|false
{
$httpName = 'HTTP_' . strtoupper(str_replace('-', '_', $name));
@@ -896,10 +896,10 @@ public function subdomains(int $tldLength = 1): array
* by the client.
*
* @param string|null $type The content type to check for. Leave null to get all types a client accepts.
- * @return mixed Either an array of all the types the client accepts or a boolean if they accept the
+ * @return array|bool Either an array of all the types the client accepts or a boolean if they accept the
* provided type.
*/
- public function accepts(?string $type = null)
+ public function accepts(?string $type = null): array|bool
{
$raw = $this->parseAccept();
$accept = [];
@@ -939,9 +939,9 @@ public function parseAccept(): array
* ``` CakeRequest::acceptLanguage('es-es'); ```
*
* @param string|null $language The language to test.
- * @return mixed If a $language is provided, a boolean. Otherwise the array of accepted languages.
+ * @return array|bool If a $language is provided, a boolean. Otherwise the array of accepted languages.
*/
- public static function acceptLanguage(?string $language = null)
+ public static function acceptLanguage(?string $language = null): array|bool
{
$raw = static::_parseAcceptWithQualifier(static::header('Accept-Language'));
$accept = [];
@@ -1009,7 +1009,7 @@ protected static function _parseAcceptWithQualifier(string $header): array
* @param string $name Query string variable name
* @return mixed The value being read
*/
- public function query(string $name)
+ public function query(string $name): mixed
{
return Hash::get($this->query, $name);
}
@@ -1035,7 +1035,7 @@ public function query(string $name)
* @param mixed ...$args
* @return self|mixed Either the value being read, or $this so you can chain consecutive writes.
*/
- public function data(string $name, ...$args)
+ public function data(string $name, mixed ...$args): mixed
{
if (count($args) === 1) {
$this->data = Hash::insert($this->data, $name, $args[0]);
@@ -1054,7 +1054,7 @@ public function data(string $name, ...$args)
* @return mixed The value of the provided parameter. Will
* return false if the parameter doesn't exist or is falsey.
*/
- public function param(string $name, ...$args)
+ public function param(string $name, mixed ...$args): mixed
{
if (count($args) === 1) {
$this->params = Hash::insert($this->params, $name, $args[0]);
@@ -1088,7 +1088,7 @@ public function param(string $name, ...$args)
* @param mixed ...$args
* @return mixed The decoded/processed request data.
*/
- public function input(?callable $callback = null, ...$args)
+ public function input(?callable $callback = null, mixed ...$args): mixed
{
$input = $this->_readInput();
if ($callback !== null) {
@@ -1184,18 +1184,18 @@ protected function _readInput(): string
/**
* Array access read implementation
*
- * @param mixed $name Name of the key being accessed.
+ * @param mixed $offset Name of the key being accessed.
* @return mixed
*/
- public function offsetGet(mixed $name): mixed
+ public function offsetGet(mixed $offset): mixed
{
- if (isset($this->params[$name])) {
- return $this->params[$name];
+ if (isset($this->params[$offset])) {
+ return $this->params[$offset];
}
- if ($name === 'url') {
+ if ($offset === 'url') {
return $this->query;
}
- if ($name === 'data') {
+ if ($offset === 'data') {
return $this->data;
}
@@ -1205,38 +1205,38 @@ public function offsetGet(mixed $name): mixed
/**
* Array access write implementation
*
- * @param mixed $name Name of the key being written
+ * @param mixed $offset Name of the key being written
* @param mixed $value The value being written.
* @return void
*/
- public function offsetSet(mixed $name, mixed $value): void
+ public function offsetSet(mixed $offset, mixed $value): void
{
- $this->params[$name] = $value;
+ $this->params[$offset] = $value;
}
/**
* Array access isset() implementation
*
- * @param mixed $name thing to check.
+ * @param mixed $offset thing to check.
* @return bool
*/
- public function offsetExists(mixed $name): bool
+ public function offsetExists(mixed $offset): bool
{
- if ($name === 'url' || $name === 'data') {
+ if ($offset === 'url' || $offset === 'data') {
return true;
}
- return isset($this->params[$name]);
+ return isset($this->params[$offset]);
}
/**
* Array access unset() implementation
*
- * @param string $name Name to unset.
+ * @param string $offset Name to unset.
* @return void
*/
- public function offsetUnset(mixed $name): void
+ public function offsetUnset(mixed $offset): void
{
- unset($this->params[$name]);
+ unset($this->params[$offset]);
}
}
diff --git a/src/Network/CakeResponse.php b/src/Network/CakeResponse.php
index 81ac86e405..c7c03f14fa 100644
--- a/src/Network/CakeResponse.php
+++ b/src/Network/CakeResponse.php
@@ -23,6 +23,7 @@
use Cake\Error\NotFoundException;
use Cake\Utility\File;
use DateTime;
+use DateTimeInterface;
use DateTimeZone;
/**
@@ -40,7 +41,7 @@ class CakeResponse
*
* @var array
*/
- protected $_statusCodes = [
+ protected array $_statusCodes = [
100 => 'Continue',
101 => 'Switching Protocols',
200 => 'OK',
@@ -89,7 +90,7 @@ class CakeResponse
*
* @var array
*/
- protected $_mimeTypes = [
+ protected array $_mimeTypes = [
'html' => ['text/html', '*/*'],
'json' => 'application/json',
'xml' => ['application/xml', 'text/xml'],
@@ -325,14 +326,14 @@ class CakeResponse
*
* @var string
*/
- protected $_protocol = 'HTTP/1.1';
+ protected string $_protocol = 'HTTP/1.1';
/**
* Status code to send to the client
*
* @var int
*/
- protected $_status = 200;
+ protected int $_status = 200;
/**
* Content type to send. This can be an 'extension' that will be transformed using the $_mimetypes array
@@ -340,42 +341,42 @@ class CakeResponse
*
* @var string
*/
- protected $_contentType = 'text/html';
+ protected string $_contentType = 'text/html';
/**
* Buffer list of headers
*
* @var array
*/
- protected $_headers = [];
+ protected array $_headers = [];
/**
* Buffer string for response message
*
- * @var string
+ * @var CakeRequest|array|string|bool|null
*/
- protected $_body = null;
+ protected CakeRequest|array|string|bool|null $_body = null;
/**
* File object for file to be read out as response
*
- * @var File
+ * @var File|null
*/
- protected $_file = null;
+ protected ?File $_file = null;
/**
* File range. Used for requesting ranges of files.
*
- * @var array
+ * @var array|null
*/
- protected $_fileRange = null;
+ protected ?array $_fileRange = null;
/**
* The charset the response body is encoded with
*
* @var string
*/
- protected $_charset = 'UTF-8';
+ protected string $_charset = 'UTF-8';
/**
* Holds all the cache directives that will be converted
@@ -383,14 +384,14 @@ class CakeResponse
*
* @var array
*/
- protected $_cacheDirectives = [];
+ protected array $_cacheDirectives = [];
/**
* Holds cookies to be sent to the client
*
* @var array
*/
- protected $_cookies = [];
+ protected array $_cookies = [];
/**
* Constructor
@@ -428,7 +429,7 @@ public function __construct(array $options = [])
*
* @return void
*/
- public function send()
+ public function send(): void
{
if (isset($this->_headers['Location']) && $this->_status === 200) {
$this->statusCode(302);
@@ -460,7 +461,7 @@ public function send()
*
* @return void
*/
- protected function _setCookies()
+ protected function _setCookies(): void
{
foreach ($this->_cookies as $name => $c) {
setcookie(
@@ -477,7 +478,7 @@ protected function _setCookies()
*
* @return void
*/
- protected function _setContentType()
+ protected function _setContentType(): void
{
if (in_array($this->_status, [304, 204])) {
return;
@@ -506,7 +507,7 @@ protected function _setContentType()
*
* @return void
*/
- protected function _setContent()
+ protected function _setContent(): void
{
if (in_array($this->_status, [304, 204])) {
$this->body('');
@@ -519,7 +520,7 @@ protected function _setContent()
*
* @return void
*/
- protected function _setContentLength()
+ protected function _setContentLength(): void
{
$shouldSetLength = !isset($this->_headers['Content-Length']) && !in_array($this->_status, range(301, 307));
if (isset($this->_headers['Content-Length']) && $this->_headers['Content-Length'] === false) {
@@ -543,12 +544,12 @@ protected function _setContentLength()
* Will skip sending headers if headers have already been sent.
*
* @param string $name the header name
- * @param string $value the header value
+ * @param string|null $value the header value
* @return void
*/
- protected function _sendHeader($name, $value = null)
+ protected function _sendHeader(string $name, ?string $value = null): void
{
- if (headers_sent($filename, $linenum)) {
+ if (headers_sent()) {
return;
}
if ($value === null) {
@@ -561,10 +562,10 @@ protected function _sendHeader($name, $value = null)
/**
* Sends a content string to the client.
*
- * @param string $content string to send as response body
+ * @param string|null $content string to send as response body
* @return void
*/
- protected function _sendContent($content)
+ protected function _sendContent(?string $content): void
{
echo $content;
}
@@ -590,13 +591,13 @@ protected function _sendContent($content)
* e.g `header('WWW-Authenticate: Negotiate'); header('WWW-Authenticate: Not-Negotiate');`
* will have the same effect as only doing `header('WWW-Authenticate: Not-Negotiate');`
*
- * @param array|string $header An array of header strings or a single header string
+ * @param array|string|null $header An array of header strings or a single header string
* - an associative array of "header name" => "header value" is also accepted
* - an array of string headers is also accepted
- * @param array|string $value The header value(s)
+ * @param array|string|int|null $value The header value(s)
* @return array list of headers to be sent
*/
- public function header($header = null, $value = null)
+ public function header(array|string|null $header = null, array|string|int|null $value = null): array
{
if ($header === null) {
return $this->_headers;
@@ -624,7 +625,7 @@ public function header($header = null, $value = null)
* @return string|null When setting the location null will be returned. When reading the location
* a string of the current location header value (if any) will be returned.
*/
- public function location($url = null)
+ public function location(?string $url = null): ?string
{
if ($url === null) {
$headers = $this->header();
@@ -640,27 +641,28 @@ public function location($url = null)
* Buffers the response message to be sent
* if $content is null the current buffer is returned
*
- * @param string $content the string message to be sent
- * @return string current message buffer if $content param is passed as null
+ * @param CakeRequest|array|string|bool|null $content the string message to be sent
+ * @return CakeRequest|array|string|bool|null current message buffer if $content param is passed as null
*/
- public function body($content = null)
+ public function body(CakeRequest|array|string|bool|null $content = null): CakeRequest|array|string|bool|null
{
if ($content === null) {
return $this->_body;
}
+ $this->_body = $content;
- return $this->_body = $content;
+ return $this->_body;
}
/**
* Sets the HTTP status code to be sent
* if $code is null the current code is returned
*
- * @param int $code the HTTP status code
+ * @param int|null $code the HTTP status code
* @return int current status code
* @throws CakeException When an unknown status code is reached.
*/
- public function statusCode($code = null)
+ public function statusCode(?int $code = null): int
{
if ($code === null) {
return $this->_status;
@@ -668,14 +670,15 @@ public function statusCode($code = null)
if (!isset($this->_statusCodes[$code])) {
throw new CakeException(__d('cake_dev', 'Unknown status code'));
}
+ $this->_status = $code;
- return $this->_status = $code;
+ return $this->_status;
}
/**
* Queries & sets valid HTTP response codes & messages.
*
- * @param array|int $code If $code is an integer, then the corresponding code/message is
+ * @param array|int|null $code If $code is an integer, then the corresponding code/message is
* returned if it exists, null if it does not exist. If $code is an array, then the
* keys are used as codes and the values as messages to add to the default HTTP
* codes. The codes must be integers greater than 99 and less than 1000. Keep in
@@ -698,12 +701,12 @@ public function statusCode($code = null)
* )); // throws an exception due to invalid codes
*
* For more on HTTP status codes see: http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1
- * @return array|true|null associative array of the HTTP codes as keys, and the message
+ * @return array|bool|null associative array of the HTTP codes as keys, and the message
* strings as values, or null of the given $code does not exist. `true` if `$code` is
* an array of valid codes.
* @throws CakeException If an attempt is made to add an invalid status code
*/
- public function httpCodes($code = null)
+ public function httpCodes(array|int|null $code = null): array|bool|null
{
if (empty($code)) {
return $this->_statusCodes;
@@ -750,7 +753,7 @@ public function httpCodes($code = null)
* @param array|string|null $contentType Content type key.
* @return string|false current content type or false if supplied an invalid content type
*/
- public function type($contentType = null)
+ public function type(array|string|null $contentType = null): string|false
{
if ($contentType === null) {
return $this->_contentType;
@@ -769,8 +772,9 @@ public function type($contentType = null)
if (!str_contains($contentType, '/')) {
return false;
}
+ $this->_contentType = $contentType;
- return $this->_contentType = $contentType;
+ return $this->_contentType;
}
/**
@@ -778,10 +782,10 @@ public function type($contentType = null)
*
* 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
+ * @param string|null $alias the content type alias to map
+ * @return array|string|false string mapped mime type or false if $alias is not mapped
*/
- public function getMimeType($alias)
+ public function getMimeType(?string $alias): array|string|false
{
return $this->_mimeTypes[$alias] ?? false;
}
@@ -792,9 +796,9 @@ public function getMimeType($alias)
* e.g `mapType('application/pdf'); // returns 'pdf'`
*
* @param array|string $ctype Either a string content type to map, or an array of types.
- * @return mixed Aliases for the types provided.
+ * @return array|string|null Aliases for the types provided.
*/
- public function mapType($ctype)
+ public function mapType(array|string $ctype): array|string|null
{
if (is_array($ctype)) {
return array_map([$this, 'mapType'], $ctype);
@@ -813,16 +817,17 @@ public function mapType($ctype)
* Sets the response charset
* if $charset is null the current charset is returned
*
- * @param string $charset Character set string.
+ * @param string|null $charset Character set string.
* @return string current charset
*/
- public function charset($charset = null)
+ public function charset(?string $charset = null): string
{
if ($charset === null) {
return $this->_charset;
}
+ $this->_charset = $charset;
- return $this->_charset = $charset;
+ return $this->_charset;
}
/**
@@ -830,7 +835,7 @@ public function charset($charset = null)
*
* @return void
*/
- public function disableCache()
+ public function disableCache(): void
{
$this->header([
'Expires' => 'Mon, 26 Jul 1997 05:00:00 GMT',
@@ -842,11 +847,11 @@ public function disableCache()
/**
* Sets the correct headers to instruct the client to cache the response.
*
- * @param string|int $since a valid time since the response text has not been modified
+ * @param string|int|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
*/
- public function cache($since, $time = '+1 day')
+ public function cache(string|int|null $since, string|int $time = '+1 day'): void
{
if (!is_int($time)) {
$time = strtotime($time);
@@ -865,13 +870,13 @@ public function cache($since, $time = '+1 day')
* This method controls the `public` or `private` directive in the Cache-Control
* header
*
- * @param bool $public If set to true, the Cache-Control header will be set as public
+ * @param bool|null $public If set to true, the Cache-Control header will be set as public
* if set to false, the response will be set to private
* if no value is provided, it will return whether the response is sharable or not
- * @param int $time time in seconds after which the response should no longer be considered fresh
- * @return bool
+ * @param int|null $time time in seconds after which the response should no longer be considered fresh
+ * @return bool|null
*/
- public function sharable($public = null, $time = null)
+ public function sharable(?bool $public = null, ?int $time = null): ?bool
{
if ($public === null) {
$public = array_key_exists('public', $this->_cacheDirectives);
@@ -880,9 +885,8 @@ public function sharable($public = null, $time = null)
if (!$public && !$private && !$noCache) {
return null;
}
- $sharable = $public || !($private || $noCache);
- return $sharable;
+ return $public || !($private || $noCache);
}
if ($public) {
$this->_cacheDirectives['public'] = true;
@@ -897,7 +901,7 @@ public function sharable($public = null, $time = null)
$this->_setCacheControl();
}
- return (bool)$public;
+ return $public;
}
/**
@@ -906,10 +910,10 @@ public function sharable($public = null, $time = null)
* a good candidate to be fetched from a shared cache (like in a proxy server).
* If called with no parameters, this function will return the current max-age value if any
*
- * @param int $seconds if null, the method will return the current s-maxage value
- * @return int
+ * @param int|null $seconds if null, the method will return the current s-maxage value
+ * @return int|null
*/
- public function sharedMaxAge($seconds = null)
+ public function sharedMaxAge(?int $seconds = null): ?int
{
if ($seconds !== null) {
$this->_cacheDirectives['s-maxage'] = $seconds;
@@ -925,10 +929,10 @@ public function sharedMaxAge($seconds = null)
* a good candidate to be fetched from the local (client) cache.
* If called with no parameters, this function will return the current max-age value if any
*
- * @param int $seconds if null, the method will return the current max-age value
- * @return int
+ * @param int|null $seconds if null, the method will return the current max-age value
+ * @return int|null
*/
- public function maxAge($seconds = null)
+ public function maxAge(?int $seconds = null): ?int
{
if ($seconds !== null) {
$this->_cacheDirectives['max-age'] = $seconds;
@@ -945,11 +949,11 @@ public function maxAge($seconds = null)
* with the origin.
* If called with no parameters, this function will return whether must-revalidate is present.
*
- * @param bool $enable If null returns whether directive is set, if boolean
+ * @param bool|null $enable If null returns whether directive is set, if boolean
* sets or unsets directive.
* @return bool
*/
- public function mustRevalidate($enable = null)
+ public function mustRevalidate(?bool $enable = null): bool
{
if ($enable !== null) {
if ($enable) {
@@ -969,7 +973,7 @@ public function mustRevalidate($enable = null)
*
* @return void
*/
- protected function _setCacheControl()
+ protected function _setCacheControl(): void
{
$control = '';
foreach ($this->_cacheDirectives as $key => $val) {
@@ -990,10 +994,10 @@ 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 DateTime|string $time Valid time string or DateTime object.
- * @return string
+ * @param DateTimeInterface|string|int|null $time Valid time string or DateTime object.
+ * @return string|null
*/
- public function expires($time = null)
+ public function expires(DateTimeInterface|string|int|null $time = null): ?string
{
if ($time !== null) {
$date = $this->_getUTCDate($time);
@@ -1013,10 +1017,10 @@ public function expires($time = null)
* `$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 DateTime|string $time Valid time string or DateTime object.
- * @return string
+ * @param DateTimeInterface|string|int|null $time Valid time string or DateTime object.
+ * @return string|null
*/
- public function modified($time = null)
+ public function modified(DateTimeInterface|string|int|null $time = null): ?string
{
if ($time !== null) {
$date = $this->_getUTCDate($time);
@@ -1033,7 +1037,7 @@ public function modified($time = null)
*
* @return void
*/
- public function notModified()
+ public function notModified(): void
{
$this->statusCode(304);
$this->body('');
@@ -1057,11 +1061,11 @@ public function notModified()
* parameters are passed, then an array with the current Vary header
* value is returned
*
- * @param array|string $cacheVariances a single Vary string or an array
+ * @param array|string|null $cacheVariances a single Vary string or an array
* containing the list for variances.
- * @return array
+ * @return array|null
*/
- public function vary($cacheVariances = null)
+ public function vary(array|string|null $cacheVariances = null): ?array
{
if ($cacheVariances !== null) {
$cacheVariances = (array)$cacheVariances;
@@ -1090,12 +1094,12 @@ public function vary($cacheVariances = null)
*
* If no parameters are passed, current Etag header is returned.
*
- * @param string $tag Tag to set.
+ * @param string|null $tag Tag to set.
* @param bool $weak whether the response is semantically the same as
* other with the same hash or not
- * @return string
+ * @return string|null
*/
- public function etag($tag = null, $weak = false)
+ public function etag(?string $tag = null, bool $weak = false): ?string
{
if ($tag !== null) {
$this->_headers['Etag'] = sprintf('%s"%s"', $weak ? 'W/' : null, $tag);
@@ -1108,19 +1112,22 @@ public function etag($tag = null, $weak = false)
* Returns a DateTime object initialized at the $time param and using UTC
* as timezone
*
- * @param DateTime|string|int $time Valid time string or unix timestamp or DateTime object.
- * @return DateTime
+ * @param DateTimeInterface|string|int|null $time Valid time string or unix timestamp or DateTime object.
+ * @return DateTimeInterface
*/
- protected function _getUTCDate($time = null)
+ protected function _getUTCDate(DateTimeInterface|string|int|null $time = null): DateTimeInterface
{
- 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'));
+
+ if (method_exists($result, 'setTimezone')) {
+ $result->setTimeZone(new DateTimeZone('UTC'));
+ }
return $result;
}
@@ -1131,7 +1138,7 @@ protected function _getUTCDate($time = null)
*
* @return bool false if client does not accept compressed responses or no handler is available, true otherwise
*/
- public function compress()
+ public function compress(): bool
{
$compressionEnabled = ini_get('zlib.output_compression') !== '1' &&
extension_loaded('zlib') &&
@@ -1145,7 +1152,7 @@ public function compress()
*
* @return bool
*/
- public function outputCompressed()
+ public function outputCompressed(): bool
{
return str_contains(env('HTTP_ACCEPT_ENCODING'), 'gzip')
&& (ini_get('zlib.output_compression') === '1' || in_array('ob_gzhandler', ob_list_handlers()));
@@ -1157,7 +1164,7 @@ public function outputCompressed()
* @param string $filename the name of the file as the browser will download the response
* @return void
*/
- public function download($filename)
+ public function download(string $filename): void
{
$this->header('Content-Disposition', 'attachment; filename="' . $filename . '"');
}
@@ -1166,10 +1173,10 @@ public function download($filename)
* Sets the protocol to be used when sending the response. Defaults to HTTP/1.1
* If called with no arguments, it will return the current configured protocol
*
- * @param string $protocol Protocol to be used for sending response.
+ * @param string|null $protocol Protocol to be used for sending response.
* @return string protocol currently set
*/
- public function protocol($protocol = null)
+ public function protocol(?string $protocol = null): string
{
if ($protocol !== null) {
$this->_protocol = $protocol;
@@ -1182,13 +1189,13 @@ public function protocol($protocol = null)
* Sets the Content-Length header for the response
* If called with no arguments returns the last Content-Length set
*
- * @param int $bytes Number of bytes
+ * @param int|false|null $bytes Number of bytes
* @return int|null
*/
- public function length($bytes = null)
+ public function length(int|false|null $bytes = null): ?int
{
if ($bytes !== null) {
- $this->_headers['Content-Length'] = $bytes;
+ $this->_headers['Content-Length'] = $bytes > 0 ? $bytes : null;
}
return $this->_headers['Content-Length'] ?? null;
@@ -1207,7 +1214,7 @@ public function length($bytes = null)
* @param CakeRequest $request Request object
* @return bool whether the response was marked as not modified or not.
*/
- public function checkNotModified(CakeRequest $request)
+ public function checkNotModified(CakeRequest $request): bool
{
$ifNoneMatchHeader = $request->header('If-None-Match');
$etags = [];
@@ -1243,7 +1250,7 @@ public function checkNotModified(CakeRequest $request)
*
* @return string
*/
- public function __toString()
+ public function __toString(): string
{
return (string)$this->_body;
}
@@ -1283,11 +1290,11 @@ public function __toString()
*
* `$this->cookie((array) $options)`
*
- * @param array|string $options Either null to get all cookies, string for a specific cookie
+ * @param array|string|null $options Either null to get all cookies, string for a specific cookie
* or array to set cookie.
* @return mixed
*/
- public function cookie($options = null)
+ public function cookie(array|string|null $options = null): mixed
{
if ($options === null) {
return $this->_cookies;
@@ -1313,6 +1320,8 @@ public function cookie($options = null)
$options += $defaults;
$this->_cookies[$options['name']] = $options;
+
+ return null;
}
/**
@@ -1341,8 +1350,12 @@ public function cookie($options = null)
* @param array|string $allowedHeaders List of HTTP headers allowed
* @return void
*/
- public function cors(CakeRequest $request, $allowedDomains, $allowedMethods = [], $allowedHeaders = [])
- {
+ public function cors(
+ CakeRequest $request,
+ array|string $allowedDomains,
+ array|string $allowedMethods = [],
+ array|string $allowedHeaders = [],
+ ): void {
$origin = $request->header('Origin');
if (!$origin) {
return;
@@ -1367,7 +1380,7 @@ public function cors(CakeRequest $request, $allowedDomains, $allowedMethods = []
* @param bool $requestIsSSL Whether it's a SSL request.
* @return array
*/
- protected function _normalizeCorsDomains($domains, $requestIsSSL = false)
+ protected function _normalizeCorsDomains(array $domains, bool $requestIsSSL = false): array
{
$result = [];
foreach ($domains as $domain) {
@@ -1400,7 +1413,7 @@ protected function _normalizeCorsDomains($domains, $requestIsSSL = false)
* @return void
* @throws NotFoundException
*/
- public function file($path, $options = [])
+ public function file(string $path, array $options = []): void
{
$options += [
'name' => null,
@@ -1436,7 +1449,7 @@ public function file($path, $options = [])
if ($download) {
$agent = env('HTTP_USER_AGENT');
- if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent)) {
+ if (preg_match('%Opera[/ ]([0-9].[0-9]{1,2})%', $agent)) {
$contentType = 'application/octet-stream';
} elseif (preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) {
$contentType = 'application/force-download';
@@ -1476,7 +1489,7 @@ public function file($path, $options = [])
* @param string $httpRange The range to use.
* @return void
*/
- protected function _fileRange($file, $httpRange)
+ protected function _fileRange(File $file, string $httpRange): void
{
$fileSize = $file->size();
$lastByte = $fileSize - 1;
@@ -1519,16 +1532,16 @@ protected function _fileRange($file, $httpRange)
* Reads out a file, and echos the content to the client.
*
* @param File $file File object
- * @param array $range The range to read out of the file.
+ * @param array{int|false, int|false}|null $range The range to read out of the file.
* @return bool True is whole file is echoed successfully or false if client connection is lost in between
*/
- protected function _sendFile($file, $range)
+ protected function _sendFile(File $file, ?array $range): bool
{
$compress = $this->outputCompressed();
$file->open('rb');
$end = $start = false;
- if ($range && is_array($range)) {
+ if (is_array($range)) {
[$start, $end] = $range;
}
if ($start !== false) {
@@ -1566,7 +1579,7 @@ protected function _sendFile($file, $range)
*
* @return bool
*/
- protected function _isActive()
+ protected function _isActive(): bool
{
return connection_status() === CONNECTION_NORMAL && !connection_aborted();
}
@@ -1576,7 +1589,7 @@ protected function _isActive()
*
* @return bool
*/
- protected function _clearBuffer()
+ protected function _clearBuffer(): bool
{
if (ob_get_length()) {
return ob_end_clean();
@@ -1590,13 +1603,11 @@ protected function _clearBuffer()
*
* @return void
*/
- protected function _flushBuffer()
+ protected function _flushBuffer(): void
{
- //@codingStandardsIgnoreStart
- @flush();
+ @flush(); // phpcs:ignore
if (ob_get_level()) {
- @ob_flush();
+ @ob_flush(); // phpcs:ignore
}
- //@codingStandardsIgnoreEnd
}
}
diff --git a/src/Network/CakeSocket.php b/src/Network/CakeSocket.php
index 31fc4db3f3..515826a610 100644
--- a/src/Network/CakeSocket.php
+++ b/src/Network/CakeSocket.php
@@ -59,12 +59,12 @@ class CakeSocket
*
* @var array
*/
- public $config = [];
+ public array $config = [];
/**
* Reference to socket connection resource
*
- * @var resource
+ * @var resource|null
*/
public $connection = null;
@@ -73,29 +73,28 @@ class CakeSocket
*
* @var bool
*/
- public $connected = false;
+ public bool $connected = false;
/**
* This variable contains an array with the last error number (num) and string (str)
*
* @var array
*/
- public $lastError = [];
+ public array $lastError = [];
/**
* True if the socket stream is encrypted after a CakeSocket::enableCrypto() call
*
* @var bool
*/
- public $encrypted = false;
+ public bool $encrypted = false;
/**
* Contains all the encryption methods available
*
* @var array
*/
- protected $_encryptMethods = [
- // @codingStandardsIgnoreStart
+ protected array $_encryptMethods = [
'sslv2_client' => STREAM_CRYPTO_METHOD_SSLv2_CLIENT,
'sslv3_client' => STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
'sslv23_client' => STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
@@ -104,7 +103,6 @@ class CakeSocket
'sslv3_server' => STREAM_CRYPTO_METHOD_SSLv3_SERVER,
'sslv23_server' => STREAM_CRYPTO_METHOD_SSLv23_SERVER,
'tls_server' => STREAM_CRYPTO_METHOD_TLS_SERVER,
- // @codingStandardsIgnoreEnd
];
/**
@@ -113,7 +111,7 @@ class CakeSocket
*
* @var array
*/
- protected $_connectionErrors = [];
+ protected array $_connectionErrors = [];
/**
* Constructor.
@@ -121,7 +119,7 @@ class CakeSocket
* @param array $config Socket configuration, which will be merged with the base configuration
* @see CakeSocket::$_baseConfig
*/
- public function __construct($config = [])
+ public function __construct(array $config = [])
{
$this->config = array_merge($this->_baseConfig, $config);
@@ -141,7 +139,7 @@ public function __construct($config = [])
* @see https://github.com/php/php-src/commit/10bc5fd4c4c8e1dd57bd911b086e9872a56300a0
* @return void
*/
- protected function _addTlsVersions()
+ protected function _addTlsVersions(): void
{
$conditionalCrypto = [
'tlsv1_1_client' => 'STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT',
@@ -157,7 +155,6 @@ protected function _addTlsVersions()
}
}
- // @codingStandardsIgnoreStart
if (isset($this->_encryptMethods['tlsv1_2_client'])) {
$this->_encryptMethods['tls_client'] = STREAM_CRYPTO_METHOD_TLS_CLIENT | STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT | STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
}
@@ -176,7 +173,6 @@ protected function _addTlsVersions()
STREAM_CRYPTO_METHOD_TLSv1_2_SERVER |
STREAM_CRYPTO_METHOD_TLSv1_3_SERVER;
}
- // @codingStandardsIgnoreEnd
}
/**
@@ -185,7 +181,7 @@ protected function _addTlsVersions()
* @return bool Success
* @throws SocketException
*/
- public function connect()
+ public function connect(): bool
{
if ($this->connection) {
$this->disconnect();
@@ -268,7 +264,7 @@ public function connect()
}
}
- $this->enableCrypto($this->config['cryptoType'], 'client');
+ $this->enableCrypto($this->config['cryptoType']);
}
}
@@ -281,7 +277,7 @@ public function connect()
* @param string $host The host name being connected to.
* @return void
*/
- protected function _setSslContext($host)
+ protected function _setSslContext(string $host): void
{
foreach ($this->config as $key => $value) {
if (!str_starts_with($key, 'ssl_')) {
@@ -323,7 +319,7 @@ protected function _setSslContext($host)
* @param string $message Message.
* @return void
*/
- protected function _connectionErrorHandler($code, $message)
+ protected function _connectionErrorHandler(int $code, string $message): void
{
$this->_connectionErrors[] = $message;
}
@@ -333,7 +329,7 @@ protected function _connectionErrorHandler($code, $message)
*
* @return array|null Null when there is no connection, an array when there is.
*/
- public function context()
+ public function context(): ?array
{
if (!$this->connection) {
return null;
@@ -345,9 +341,9 @@ public function context()
/**
* Gets the host name of the current connection.
*
- * @return string Host name
+ * @return string|false Host name
*/
- public function host()
+ public function host(): string|false
{
if (Validation::ip($this->config['host'])) {
return gethostbyaddr($this->config['host']);
@@ -361,7 +357,7 @@ public function host()
*
* @return string IP address
*/
- public function address()
+ public function address(): string
{
if (Validation::ip($this->config['host'])) {
return $this->config['host'];
@@ -373,9 +369,9 @@ public function address()
/**
* Gets all IP addresses associated with the current connection.
*
- * @return array IP addresses
+ * @return array|false IP addresses
*/
- public function addresses()
+ public function addresses(): array|false
{
if (Validation::ip($this->config['host'])) {
return [$this->config['host']];
@@ -389,7 +385,7 @@ public function addresses()
*
* @return string|null Last error
*/
- public function lastError()
+ public function lastError(): ?string
{
if (!empty($this->lastError)) {
return $this->lastError['num'] . ': ' . $this->lastError['str'];
@@ -405,7 +401,7 @@ public function lastError()
* @param string $errStr Error string
* @return void
*/
- public function setLastError($errNum, $errStr)
+ public function setLastError(?int $errNum, string $errStr): void
{
$this->lastError = ['num' => $errNum, 'str' => $errStr];
}
@@ -414,9 +410,9 @@ public function setLastError($errNum, $errStr)
* Writes data to the socket.
*
* @param string $data The data to write to the socket
- * @return bool Success
+ * @return int|false Success
*/
- public function write($data)
+ public function write(string $data): int|false
{
if (!$this->connected) {
if (!$this->connect()) {
@@ -439,9 +435,9 @@ public function write($data)
* established.
*
* @param int $length Optional buffer length to read; defaults to 1024
- * @return mixed Socket data
+ * @return string|false Socket data
*/
- public function read($length = 1024)
+ public function read(int $length = 1024): string|false
{
if (!$this->connected) {
if (!$this->connect()) {
@@ -469,7 +465,7 @@ public function read($length = 1024)
*
* @return bool Success
*/
- public function disconnect()
+ public function disconnect(): bool
{
if (!is_resource($this->connection)) {
$this->connected = false;
@@ -496,10 +492,10 @@ public function __destruct()
/**
* Resets the state of this Socket instance to it's initial state (before CakeObject::__construct got executed)
*
- * @param array $state Array with key and values to reset
+ * @param array|null $state Array with key and values to reset
* @return bool True on success
*/
- public function reset($state = null)
+ public function reset(?array $state = null): bool
{
if (empty($state)) {
static $initalState = [];
@@ -527,8 +523,11 @@ public function reset($state = null)
* @throws SocketException When attempting to enable SSL/TLS fails.
* @see stream_socket_enable_crypto
*/
- public function enableCrypto($type, $clientOrServer = 'client', $enable = true)
- {
+ public function enableCrypto(
+ string $type,
+ string $clientOrServer = 'client',
+ bool $enable = true,
+ ): bool {
if (!array_key_exists($type . '_' . $clientOrServer, $this->_encryptMethods)) {
throw new InvalidArgumentException(__d('cake_dev', 'Invalid encryption scheme chosen'));
}
diff --git a/src/Network/Email/AbstractTransport.php b/src/Network/Email/AbstractTransport.php
index 1e6f195733..5ecf894aad 100644
--- a/src/Network/Email/AbstractTransport.php
+++ b/src/Network/Email/AbstractTransport.php
@@ -30,7 +30,7 @@ abstract class AbstractTransport
*
* @var array
*/
- protected $_config = [];
+ protected array $_config = [];
/**
* Send mail
@@ -38,15 +38,15 @@ abstract class AbstractTransport
* @param CakeEmail $email CakeEmail instance.
* @return array
*/
- abstract public function send(CakeEmail $email);
+ abstract public function send(CakeEmail $email): array;
/**
* Set the config
*
- * @param array $config Configuration options.
+ * @param array|null $config Configuration options.
* @return array Returns configs
*/
- public function config($config = null)
+ public function config(?array $config = null): array
{
if (is_array($config)) {
$this->_config = $config + $this->_config;
@@ -62,7 +62,7 @@ public function config($config = null)
* @param string $eol End of line string.
* @return string
*/
- protected function _headersToString($headers, $eol = "\r\n")
+ protected function _headersToString(array $headers, string $eol = "\r\n"): string
{
$out = '';
foreach ($headers as $key => $value) {
diff --git a/src/Network/Email/CakeEmail.php b/src/Network/Email/CakeEmail.php
index d918e4a82f..f524232b7a 100644
--- a/src/Network/Email/CakeEmail.php
+++ b/src/Network/Email/CakeEmail.php
@@ -85,35 +85,35 @@ class CakeEmail
*
* @var array
*/
- protected $_to = [];
+ protected array $_to = [];
/**
* The mail which the email is sent from
*
* @var array
*/
- protected $_from = [];
+ protected array $_from = [];
/**
* The sender email
*
* @var array
*/
- protected $_sender = [];
+ protected array $_sender = [];
/**
* The email the recipient will reply to
*
* @var array
*/
- protected $_replyTo = [];
+ protected array $_replyTo = [];
/**
* The read receipt email
*
* @var array
*/
- protected $_readReceipt = [];
+ protected array $_readReceipt = [];
/**
* The mail that will be used in case of any errors like
@@ -123,7 +123,7 @@ class CakeEmail
*
* @var array
*/
- protected $_returnPath = [];
+ protected array $_returnPath = [];
/**
* Carbon Copy
@@ -133,7 +133,7 @@ class CakeEmail
*
* @var array
*/
- protected $_cc = [];
+ protected array $_cc = [];
/**
* Blind Carbon Copy
@@ -143,29 +143,29 @@ class CakeEmail
*
* @var array
*/
- protected $_bcc = [];
+ protected array $_bcc = [];
/**
* Message ID
*
* @var string|bool
*/
- protected $_messageId = true;
+ protected string|bool $_messageId = true;
/**
* Domain for messageId generation.
* Needs to be manually set for CLI mailing as env('HTTP_HOST') is empty
*
- * @var string
+ * @var string|null
*/
- protected $_domain = null;
+ protected ?string $_domain = null;
/**
* The subject of the email
*
* @var string
*/
- protected $_subject = '';
+ protected string $_subject = '';
/**
* Associative array of a user defined headers
@@ -173,120 +173,120 @@ class CakeEmail
*
* @var array
*/
- protected $_headers = [];
+ protected array $_headers = [];
/**
* Layout for the View
*
- * @var string
+ * @var string|false|null
*/
- protected $_layout = 'default';
+ protected string|false|null $_layout = 'default';
/**
* Template for the view
*
- * @var string
+ * @var string|null
*/
- protected $_template = '';
+ protected ?string $_template = '';
/**
* View for render
*
* @var string
*/
- protected $_viewRender = 'View';
+ protected string $_viewRender = 'View';
/**
* Vars to sent to render
*
* @var array
*/
- protected $_viewVars = [];
+ protected array $_viewVars = [];
/**
* Theme for the View
*
- * @var array
+ * @var string|null
*/
- protected $_theme = null;
+ protected ?string $_theme = null;
/**
* Helpers to be used in the render
*
* @var array
*/
- protected $_helpers = ['Html'];
+ protected array $_helpers = ['Html'];
/**
* Text message
*
* @var string
*/
- protected $_textMessage = '';
+ protected string $_textMessage = '';
/**
* Html message
*
* @var string
*/
- protected $_htmlMessage = '';
+ protected string $_htmlMessage = '';
/**
* Final message to send
*
* @var array
*/
- protected $_message = [];
+ protected array $_message = [];
/**
* Available formats to be sent.
*
- * @var array
+ * @var array
*/
- protected $_emailFormatAvailable = ['text', 'html', 'both'];
+ protected array $_emailFormatAvailable = ['text', 'html', 'both'];
/**
* What format should the email be sent in
*
* @var string
*/
- protected $_emailFormat = 'text';
+ protected string $_emailFormat = 'text';
/**
* What method should the email be sent
*
* @var string
*/
- protected $_transportName = 'Mail';
+ protected string $_transportName = 'Mail';
/**
* Instance of transport class
*
- * @var AbstractTransport
+ * @var AbstractTransport|null
*/
- protected $_transportClass = null;
+ protected ?AbstractTransport $_transportClass = null;
/**
* Charset the email body is sent in
*
* @var string
*/
- public $charset = 'utf-8';
+ public string $charset = 'utf-8';
/**
* Charset the email header is sent in
* If null, the $charset property will be used as default
*
- * @var string
+ * @var string|null
*/
- public $headerCharset = null;
+ public ?string $headerCharset = null;
/**
* The application wide charset, used to encode headers and body
*
- * @var string
+ * @var string|null
*/
- protected $_appCharset = null;
+ protected ?string $_appCharset = null;
/**
* List of files that should be attached to the email.
@@ -295,35 +295,35 @@ class CakeEmail
*
* @var array
*/
- protected $_attachments = [];
+ protected array $_attachments = [];
/**
* If set, boundary to use for multipart mime messages
*
- * @var string
+ * @var string|null
*/
- protected $_boundary = null;
+ protected ?string $_boundary = null;
/**
* Configuration to transport
*
- * @var array|string
+ * @var array
*/
- protected $_config = [];
+ protected array $_config = [];
/**
* 8Bit character sets
*
- * @var array
+ * @var array
*/
- protected $_charset8bit = ['UTF-8', 'SHIFT_JIS'];
+ protected array $_charset8bit = ['UTF-8', 'SHIFT_JIS'];
/**
* Define Content-Type charset name
*
- * @var array
+ * @var array
*/
- protected $_contentTypeCharset = [
+ protected array $_contentTypeCharset = [
'ISO-2022-JP-MS' => 'ISO-2022-JP',
];
@@ -333,36 +333,36 @@ class CakeEmail
* If null, filter_var() will be used. Use the emailPattern() method
* to set a custom pattern.'
*
- * @var string
+ * @var string|null
*/
- protected $_emailPattern = self::EMAIL_PATTERN;
+ protected ?string $_emailPattern = self::EMAIL_PATTERN;
/**
* The class name used for email configuration.
*
* @var string
*/
- protected $_configClass = EmailConfig::class;
+ protected string $_configClass = EmailConfig::class;
/**
* An instance of the EmailConfig class can be set here
*
- * @var EmailConfig
+ * @var EmailConfigInterface|null
*/
- protected $_configInstance;
+ protected ?EmailConfigInterface $_configInstance = null;
/**
* Constructor
*
- * @param array|string $config Array of configs, or string to load configs from email.php
+ * @param array|string|null $config Array of configs, or string to load configs from email.php
*/
- public function __construct($config = null)
+ public function __construct(array|string|null $config = null)
{
$this->_appCharset = Configure::read('App.encoding');
if ($this->_appCharset !== null) {
$this->charset = $this->_appCharset;
}
- $this->_domain = preg_replace('/\:\d+$/', '', env('HTTP_HOST'));
+ $this->_domain = preg_replace('/:\d+$/', '', env('HTTP_HOST'));
if (empty($this->_domain)) {
$this->_domain = php_uname('n');
}
@@ -383,13 +383,13 @@ public function __construct($config = null)
/**
* From
*
- * @param array|string $email Null to get, String with email,
+ * @param array|string|null $email Null to get, String with email,
* Array with email as key, name as value or email as value (without name)
- * @param string $name Name
- * @return CakeEmail|array
+ * @param string|null $name Name
+ * @return self|array
* @throws SocketException
*/
- public function from($email = null, $name = null)
+ public function from(array|string|null $email = null, ?string $name = null): self|array
{
if ($email === null) {
return $this->_from;
@@ -401,13 +401,13 @@ public function from($email = null, $name = null)
/**
* Sender
*
- * @param array|string $email Null to get, String with email,
+ * @param array|string|null $email Null to get, String with email,
* Array with email as key, name as value or email as value (without name)
- * @param string $name Name
- * @return CakeEmail|array
+ * @param string|null $name Name
+ * @return self|array
* @throws SocketException
*/
- public function sender($email = null, $name = null)
+ public function sender(array|string|null $email = null, ?string $name = null): self|array
{
if ($email === null) {
return $this->_sender;
@@ -419,13 +419,13 @@ public function sender($email = null, $name = null)
/**
* Reply-To
*
- * @param array|string $email Null to get, String with email,
+ * @param array|string|null $email Null to get, String with email,
* Array with email as key, name as value or email as value (without name)
- * @param string $name Name
- * @return CakeEmail|array
+ * @param string|null $name Name
+ * @return self|array
* @throws SocketException
*/
- public function replyTo($email = null, $name = null)
+ public function replyTo(array|string|null $email = null, ?string $name = null): self|array
{
if ($email === null) {
return $this->_replyTo;
@@ -437,13 +437,13 @@ public function replyTo($email = null, $name = null)
/**
* Read Receipt (Disposition-Notification-To header)
*
- * @param array|string $email Null to get, String with email,
+ * @param array|string|null $email Null to get, String with email,
* Array with email as key, name as value or email as value (without name)
- * @param string $name Name
- * @return CakeEmail|array
+ * @param string|null $name Name
+ * @return self|array
* @throws SocketException
*/
- public function readReceipt($email = null, $name = null)
+ public function readReceipt(array|string|null $email = null, ?string $name = null): self|array
{
if ($email === null) {
return $this->_readReceipt;
@@ -455,13 +455,13 @@ public function readReceipt($email = null, $name = null)
/**
* Return Path
*
- * @param array|string $email Null to get, String with email,
+ * @param array|string|null $email Null to get, String with email,
* Array with email as key, name as value or email as value (without name)
- * @param string $name Name
- * @return CakeEmail|array
+ * @param string|null $name Name
+ * @return self|array
* @throws SocketException
*/
- public function returnPath($email = null, $name = null)
+ public function returnPath(array|string|null $email = null, ?string $name = null): self|array
{
if ($email === null) {
return $this->_returnPath;
@@ -473,12 +473,12 @@ public function returnPath($email = null, $name = null)
/**
* To
*
- * @param array|string $email Null to get, String with email,
+ * @param array|string|null $email Null to get, String with email,
* Array with email as key, name as value or email as value (without name)
- * @param string $name Name
+ * @param string|null $name Name
* @return self|array
*/
- public function to($email = null, $name = null)
+ public function to(array|string|null $email = null, ?string $name = null): self|array
{
if ($email === null) {
return $this->_to;
@@ -490,12 +490,12 @@ public function to($email = null, $name = null)
/**
* Add To
*
- * @param array|string $email Null to get, String with email,
+ * @param array|string|null $email Null to get, String with email,
* Array with email as key, name as value or email as value (without name)
- * @param string $name Name
+ * @param string|null $name Name
* @return self
*/
- public function addTo($email, $name = null)
+ public function addTo(array|string|null $email, ?string $name = null): self
{
return $this->_addEmail('_to', $email, $name);
}
@@ -503,12 +503,12 @@ public function addTo($email, $name = null)
/**
* Cc
*
- * @param array|string $email Null to get, String with email,
+ * @param array|string|null $email Null to get, String with email,
* Array with email as key, name as value or email as value (without name)
- * @param string $name Name
+ * @param string|null $name Name
* @return self|array
*/
- public function cc($email = null, $name = null)
+ public function cc(array|string|null $email = null, ?string $name = null): self|array
{
if ($email === null) {
return $this->_cc;
@@ -520,12 +520,12 @@ public function cc($email = null, $name = null)
/**
* Add Cc
*
- * @param array|string $email Null to get, String with email,
+ * @param array|string|null $email Null to get, String with email,
* Array with email as key, name as value or email as value (without name)
- * @param string $name Name
+ * @param string|null $name Name
* @return self
*/
- public function addCc($email, $name = null)
+ public function addCc(array|string|null $email, ?string $name = null): self
{
return $this->_addEmail('_cc', $email, $name);
}
@@ -533,12 +533,12 @@ public function addCc($email, $name = null)
/**
* Bcc
*
- * @param array|string $email Null to get, String with email,
+ * @param array|string|null $email Null to get, String with email,
* Array with email as key, name as value or email as value (without name)
- * @param string $name Name
+ * @param string|null $name Name
* @return self|array
*/
- public function bcc($email = null, $name = null)
+ public function bcc(array|string|null $email = null, ?string $name = null): self|array
{
if ($email === null) {
return $this->_bcc;
@@ -550,12 +550,12 @@ public function bcc($email = null, $name = null)
/**
* Add Bcc
*
- * @param array|string $email Null to get, String with email,
+ * @param array|string|null $email Null to get, String with email,
* Array with email as key, name as value or email as value (without name)
- * @param string $name Name
+ * @param string|null $name Name
* @return self
*/
- public function addBcc($email, $name = null)
+ public function addBcc(array|string|null $email, ?string $name = null): self
{
return $this->_addEmail('_bcc', $email, $name);
}
@@ -563,10 +563,10 @@ public function addBcc($email, $name = null)
/**
* Charset setter/getter
*
- * @param string $charset Character set.
- * @return string this->charset
+ * @param string|null $charset Character set.
+ * @return string|null this->charset
*/
- public function charset($charset = null)
+ public function charset(?string $charset = null): ?string
{
if ($charset === null) {
return $this->charset;
@@ -582,10 +582,10 @@ public function charset($charset = null)
/**
* HeaderCharset setter/getter
*
- * @param string $charset Character set.
- * @return string this->charset
+ * @param string|null $charset Character set.
+ * @return string|null this->charset
*/
- public function headerCharset($charset = null)
+ public function headerCharset(?string $charset = null): ?string
{
if ($charset === null) {
return $this->headerCharset;
@@ -597,12 +597,12 @@ public function headerCharset($charset = null)
/**
* EmailPattern setter/getter
*
- * @param string|bool|null $regex The pattern to use for email address validation,
+ * @param string|false|null $regex The pattern to use for email address validation,
* null to unset the pattern and make use of filter_var() instead, false or
* nothing to return the current value
- * @return self|string
+ * @return self|string|null
*/
- public function emailPattern($regex = false)
+ public function emailPattern(string|false|null $regex = false): self|string|null
{
if ($regex === false) {
return $this->_emailPattern;
@@ -616,12 +616,12 @@ public function emailPattern($regex = false)
* Set email
*
* @param string $varName Property name
- * @param array|string $email String with email,
+ * @param array|string|null $email String with email,
* Array with email as key, name as value or email as value (without name)
- * @param string $name Name
+ * @param string|null $name Name
* @return self
*/
- protected function _setEmail($varName, $email, $name)
+ protected function _setEmail(string $varName, array|string|null $email, ?string $name): self
{
if (!is_array($email)) {
$this->_validateEmail($email, $varName);
@@ -648,12 +648,12 @@ protected function _setEmail($varName, $email, $name)
/**
* Validate email address
*
- * @param string $email Email address to validate
+ * @param string|null $email Email address to validate
* @param string $context Which property was set
* @return void
* @throws SocketException If email address does not validate
*/
- protected function _validateEmail($email, $context)
+ protected function _validateEmail(string|null $email, string $context): void
{
if ($this->_emailPattern === null) {
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
@@ -672,15 +672,19 @@ protected function _validateEmail($email, $context)
* Set only 1 email
*
* @param string $varName Property name
- * @param array|string $email String with email,
+ * @param array|string|null $email String with email,
* Array with email as key, name as value or email as value (without name)
- * @param string $name Name
+ * @param string|null $name Name
* @param string $throwMessage Exception message
* @return self
* @throws SocketException
*/
- protected function _setEmailSingle($varName, $email, $name, $throwMessage)
- {
+ protected function _setEmailSingle(
+ string $varName,
+ array|string|null $email,
+ string|null $name,
+ string $throwMessage,
+ ): self {
$current = $this->{$varName};
$this->_setEmail($varName, $email, $name);
if (count($this->{$varName}) !== 1) {
@@ -695,13 +699,13 @@ protected function _setEmailSingle($varName, $email, $name, $throwMessage)
* Add email
*
* @param string $varName Property name
- * @param array|string $email String with email,
+ * @param array|string|null $email String with email,
* Array with email as key, name as value or email as value (without name)
- * @param string $name Name
+ * @param string|null $name Name
* @return self
* @throws SocketException
*/
- protected function _addEmail($varName, $email, $name)
+ protected function _addEmail(string $varName, array|string|null $email, ?string $name): self
{
if (!is_array($email)) {
$this->_validateEmail($email, $varName);
@@ -712,6 +716,7 @@ protected function _addEmail($varName, $email, $name)
return $this;
}
+
$list = [];
foreach ($email as $key => $value) {
if (is_int($key)) {
@@ -728,10 +733,10 @@ protected function _addEmail($varName, $email, $name)
/**
* Get/Set Subject.
*
- * @param string $subject Subject string.
+ * @param string|null $subject Subject string.
* @return self|string
*/
- public function subject($subject = null)
+ public function subject(?string $subject = null): self|string
{
if ($subject === null) {
return $this->_subject;
@@ -748,11 +753,8 @@ public function subject($subject = null)
* @return self
* @throws SocketException
*/
- public function setHeaders($headers)
+ public function setHeaders(array $headers): self
{
- if (!is_array($headers)) {
- throw new SocketException(__d('cake_dev', '$headers should be an array.'));
- }
$this->_headers = $headers;
return $this;
@@ -765,11 +767,8 @@ public function setHeaders($headers)
* @return self
* @throws SocketException
*/
- public function addHeaders($headers)
+ public function addHeaders(array $headers): self
{
- if (!is_array($headers)) {
- throw new SocketException(__d('cake_dev', '$headers should be an array.'));
- }
$this->_headers = array_merge($this->_headers, $headers);
return $this;
@@ -792,15 +791,23 @@ public function addHeaders($headers)
* @param array $include List of headers.
* @return array
*/
- public function getHeaders($include = [])
+ public function getHeaders(array $include = []): array
{
if ($include == array_values($include)) {
$include = array_fill_keys($include, true);
}
$defaults = array_fill_keys(
[
- 'from', 'sender', 'replyTo', 'readReceipt', 'returnPath',
- 'to', 'cc', 'bcc', 'subject'],
+ 'from',
+ 'sender',
+ 'replyTo',
+ 'readReceipt',
+ 'returnPath',
+ 'to',
+ 'cc',
+ 'bcc',
+ 'subject',
+ ],
false,
);
$include += $defaults;
@@ -877,7 +884,7 @@ public function getHeaders($include = [])
* @param array $address Addresses to format.
* @return array
*/
- protected function _formatAddress($address)
+ protected function _formatAddress(array $address): array
{
$return = [];
foreach ($address as $email => $alias) {
@@ -901,12 +908,14 @@ protected function _formatAddress($address)
/**
* Template and layout
*
- * @param string|bool $template Template name or null to not use
- * @param string|bool $layout Layout name or null to not use
+ * @param string|false|null $template Template name or null to not use
+ * @param string|false|null $layout Layout name or null to not use
* @return self|array
*/
- public function template($template = false, $layout = false)
- {
+ public function template(
+ string|false|null $template = false,
+ string|false|null $layout = false,
+ ): self|array {
if ($template === false) {
return [
'template' => $this->_template,
@@ -924,10 +933,10 @@ public function template($template = false, $layout = false)
/**
* View class for render
*
- * @param string $viewClass View class name.
+ * @param string|null $viewClass View class name.
* @return self|string
*/
- public function viewRender($viewClass = null)
+ public function viewRender(?string $viewClass = null): self|string
{
if ($viewClass === null) {
return $this->_viewRender;
@@ -940,10 +949,10 @@ public function viewRender($viewClass = null)
/**
* Variables to be set on render
*
- * @param array $viewVars Variables to set for view.
+ * @param array|null $viewVars Variables to set for view.
* @return self|array
*/
- public function viewVars($viewVars = null)
+ public function viewVars(?array $viewVars = null): self|array
{
if ($viewVars === null) {
return $this->_viewVars;
@@ -956,10 +965,10 @@ public function viewVars($viewVars = null)
/**
* Theme to use when rendering
*
- * @param string $theme Theme name.
- * @return self|string
+ * @param string|null $theme Theme name.
+ * @return self|string|null
*/
- public function theme($theme = null)
+ public function theme(?string $theme = null): self|string|null
{
if ($theme === null) {
return $this->_theme;
@@ -972,10 +981,10 @@ public function theme($theme = null)
/**
* Helpers to be used in render
*
- * @param array $helpers Helpers list.
+ * @param array|null $helpers Helpers list.
* @return self|array
*/
- public function helpers($helpers = null)
+ public function helpers(?array $helpers = null): self|array
{
if ($helpers === null) {
return $this->_helpers;
@@ -988,11 +997,11 @@ public function helpers($helpers = null)
/**
* Email format
*
- * @param string $format Formatting string.
+ * @param string|null $format Formatting string.
* @return self|string
* @throws SocketException
*/
- public function emailFormat($format = null)
+ public function emailFormat(?string $format = null): self|string
{
if ($format === null) {
return $this->_emailFormat;
@@ -1008,10 +1017,10 @@ public function emailFormat($format = null)
/**
* Transport name
*
- * @param string $name Transport name.
+ * @param string|null $name Transport name.
* @return self|string
*/
- public function transport($name = null)
+ public function transport(?string $name = null): self|string
{
if ($name === null) {
return $this->_transportName;
@@ -1028,7 +1037,7 @@ public function transport($name = null)
* @return AbstractTransport
* @throws SocketException
*/
- public function transportClass()
+ public function transportClass(): AbstractTransport
{
if ($this->_transportClass) {
return $this->_transportClass;
@@ -1050,23 +1059,20 @@ public function transportClass()
/**
* Message-ID
*
- * @param string|bool $message True to generate a new Message-ID, False to ignore (not send in email), String to set as Message-ID
+ * @param string|bool|null $message True to generate a new Message-ID, False to ignore (not send in email), String to set as Message-ID
* @return self|string|bool
* @throws SocketException
*/
- public function messageId($message = null)
+ public function messageId(string|bool|null $message = null): self|string|bool
{
if ($message === null) {
return $this->_messageId;
}
- if (is_bool($message)) {
- $this->_messageId = $message;
- } else {
- if (!preg_match('/^\<.+@.+\>$/', $message)) {
- throw new SocketException(__d('cake_dev', 'Invalid format for Message-ID. The text should be something like ""'));
- }
- $this->_messageId = $message;
+
+ if (!is_bool($message) && !preg_match('/^<.+@.+>$/', $message)) {
+ throw new SocketException(__d('cake_dev', 'Invalid format for Message-ID. The text should be something like ""'));
}
+ $this->_messageId = $message;
return $this;
}
@@ -1074,10 +1080,10 @@ public function messageId($message = null)
/**
* Domain as top level (the part after @)
*
- * @param string $domain Manually set the domain for CLI mailing
+ * @param string|null $domain Manually set the domain for CLI mailing
* @return self|string
*/
- public function domain($domain = null)
+ public function domain(?string $domain = null): self|string
{
if ($domain === null) {
return $this->_domain;
@@ -1130,15 +1136,16 @@ public function domain($domain = null)
* The `contentDisposition` key allows you to disable the `Content-Disposition` header, this can improve
* attachment compatibility with outlook email clients.
*
- * @param array|string $attachments String with the filename or array with filenames
+ * @param array|string|null $attachments String with the filename or array with filenames
* @return self|array Either the array of attachments when getting or $this when setting.
* @throws SocketException
*/
- public function attachments($attachments = null)
+ public function attachments(array|string|null $attachments = null): self|array
{
if ($attachments === null) {
return $this->_attachments;
}
+
$attach = [];
foreach ((array)$attachments as $name => $fileInfo) {
if (!is_array($fileInfo)) {
@@ -1178,12 +1185,12 @@ public function attachments($attachments = null)
/**
* Add attachments
*
- * @param array|string $attachments String with the filename or array with filenames
+ * @param array|string|null $attachments String with the filename or array with filenames
* @return self
* @throws SocketException
* @see CakeEmail::attachments()
*/
- public function addAttachments($attachments)
+ public function addAttachments(array|string|null $attachments): self
{
$current = $this->_attachments;
$this->attachments($attachments);
@@ -1195,19 +1202,16 @@ public function addAttachments($attachments)
/**
* Get generated message (used by transport classes)
*
- * @param string $type Use MESSAGE_* constants or null to return the full message as array
+ * @param string|null $type Use MESSAGE_* constants or null to return the full message as array
* @return array|string String if have type, array if type is null
*/
- public function message($type = null)
+ public function message(?string $type = null): array|string
{
- switch ($type) {
- case static::MESSAGE_HTML:
- return $this->_htmlMessage;
- case static::MESSAGE_TEXT:
- return $this->_textMessage;
- }
-
- return $this->_message;
+ return match ($type) {
+ static::MESSAGE_HTML => $this->_htmlMessage,
+ static::MESSAGE_TEXT => $this->_textMessage,
+ default => $this->_message,
+ };
}
/**
@@ -1223,10 +1227,10 @@ public function message($type = null)
*
* `$email->config(array('to' => 'bill@example.com'));`
*
- * @param array|string $config String with configuration name (from email.php), array with config or null to return current config
+ * @param array|string|null $config String with configuration name (from email.php), array with config or null to return current config
* @return self|array|string
*/
- public function config($config = null)
+ public function config(array|string|null $config = null): self|array|string
{
if ($config === null) {
return $this->_config;
@@ -1243,11 +1247,11 @@ public function config($config = null)
/**
* Send an email using the specified content, template and layout
*
- * @param array|string $content String with message or array with messages
+ * @param array|string|null $content String with message or array with messages
* @return array
* @throws SocketException
*/
- public function send($content = '')
+ public function send(array|string|null $content = ''): array
{
if (empty($this->_from)) {
throw new SocketException(__d('cake_dev', 'From is not specified.'));
@@ -1287,18 +1291,22 @@ public function send($content = '')
/**
* Static method to fast create an instance of CakeEmail
*
- * @param array|string $to Address to send (see CakeEmail::to()). If null, will try to use 'to' from transport config
- * @param string $subject String of subject or null to use 'subject' from transport config
- * @param array|string $message String with message or array with variables to be used in render
- * @param array|string $transportConfig String to use config from EmailConfig or array with configs
+ * @param array|string|null $to Address to send (see CakeEmail::to()). If null, will try to use 'to' from transport config
+ * @param string|null $subject String of subject or null to use 'subject' from transport config
+ * @param array|string|null $message String with message or array with variables to be used in render
+ * @param array|string|null $transportConfig String to use config from EmailConfig or array with configs
* @param bool $send Send the email or just return the instance pre-configured
* @return self Instance of CakeEmail
* @throws SocketException
*/
- public static function deliver($to = null, $subject = null, $message = null, $transportConfig = 'fast', $send = true)
- {
+ public static function deliver(
+ array|string|null $to = null,
+ ?string $subject = null,
+ array|string|null $message = null,
+ array|string|null $transportConfig = 'fast',
+ bool $send = true,
+ ): self {
$class = static::class;
- /** @var CakeEmail $instance */
$instance = new $class($transportConfig);
if ($to !== null) {
$instance->to($to);
@@ -1323,12 +1331,12 @@ public static function deliver($to = null, $subject = null, $message = null, $tr
/**
* Apply the config to an instance
*
- * @param array $config Configuration options.
+ * @param array|string $config Configuration options.
* @return void
* @throws ConfigureException When configuration file cannot be found, or is missing
* the named config.
*/
- protected function _applyConfig($config)
+ protected function _applyConfig(array|string $config): void
{
if (is_string($config)) {
if (!$this->_configInstance) {
@@ -1383,7 +1391,7 @@ protected function _applyConfig($config)
*
* @return self
*/
- public function reset()
+ public function reset(): self
{
$this->_to = [];
$this->_from = [];
@@ -1404,7 +1412,7 @@ public function reset()
$this->_helpers = ['Html'];
$this->_textMessage = '';
$this->_htmlMessage = '';
- $this->_message = '';
+ $this->_message = [];
$this->_emailFormat = 'text';
$this->_transportName = 'Mail';
$this->_transportClass = null;
@@ -1423,7 +1431,7 @@ public function reset()
* @param string $text String to encode
* @return string Encoded string
*/
- protected function _encode($text)
+ protected function _encode(string $text): string
{
$internalEncoding = function_exists('mb_internal_encoding');
if ($internalEncoding) {
@@ -1453,7 +1461,7 @@ protected function _encode($text)
* @param string $charset the target encoding
* @return string
*/
- protected function _encodeString($text, $charset)
+ protected function _encodeString(string $text, string $charset): string
{
if ($this->_appCharset === $charset || !function_exists('mb_convert_encoding')) {
return $text;
@@ -1465,11 +1473,11 @@ protected function _encodeString($text, $charset)
/**
* Wrap the message to follow the RFC 2822 - 2.1.1
*
- * @param string $message Message to wrap
+ * @param string|null $message Message to wrap
* @param int $wrapLength The line length
* @return array Wrapped message
*/
- protected function _wrap($message, $wrapLength = CakeEmail::LINE_LENGTH_MUST)
+ protected function _wrap(?string $message, int $wrapLength = CakeEmail::LINE_LENGTH_MUST): array
{
if (strlen($message) === 0) {
return [''];
@@ -1576,20 +1584,20 @@ protected function _wrap($message, $wrapLength = CakeEmail::LINE_LENGTH_MUST)
*
* @return void
*/
- protected function _createBoundary()
+ protected function _createBoundary(): void
{
if (!empty($this->_attachments) || $this->_emailFormat === 'both') {
- $this->_boundary = md5(uniqid(time()));
+ $this->_boundary = md5(uniqid((string)time()));
}
}
/**
* Attach non-embedded files by adding file contents inside boundaries.
*
- * @param string $boundary Boundary to use. If null, will default to $this->_boundary
+ * @param string|null $boundary Boundary to use. If null, will default to $this->_boundary
* @return array An array of lines to add to the message
*/
- protected function _attachFiles($boundary = null)
+ protected function _attachFiles(?string $boundary = null): array
{
if ($boundary === null) {
$boundary = $this->_boundary;
@@ -1625,7 +1633,7 @@ protected function _attachFiles($boundary = null)
* @param string $path The absolute path to the file to read.
* @return string File contents in base64 encoding
*/
- protected function _readFile($path)
+ protected function _readFile(string $path): string
{
$File = new File($path);
@@ -1635,10 +1643,10 @@ protected function _readFile($path)
/**
* Attach inline/embedded files to the message.
*
- * @param string $boundary Boundary to use. If null, will default to $this->_boundary
+ * @param string|null $boundary Boundary to use. If null, will default to $this->_boundary
* @return array An array of lines to add to the message
*/
- protected function _attachInlineFiles($boundary = null)
+ protected function _attachInlineFiles(?string $boundary = null): array
{
if ($boundary === null) {
$boundary = $this->_boundary;
@@ -1670,7 +1678,7 @@ protected function _attachInlineFiles($boundary = null)
* @param array $content Content to render
* @return array Email body ready to be sent
*/
- protected function _render($content)
+ protected function _render(array $content): array
{
$this->_textMessage = $this->_htmlMessage = '';
@@ -1757,9 +1765,9 @@ protected function _render($content)
/**
* Gets the text body types that are in this email message
*
- * @return array Array of types. Valid types are 'text' and 'html'
+ * @return array Array of types. Valid types are 'text' and 'html'
*/
- protected function _getTypes()
+ protected function _getTypes(): array
{
$types = [$this->_emailFormat];
if ($this->_emailFormat === 'both') {
@@ -1777,7 +1785,7 @@ protected function _getTypes()
* @param string $content The content passed in from send() in most cases.
* @return array The rendered content with html and text keys.
*/
- protected function _renderTemplates($content)
+ protected function _renderTemplates(string $content): array
{
$types = $this->_getTypes();
$rendered = [];
@@ -1847,7 +1855,7 @@ protected function _renderTemplates($content)
*
* @return string
*/
- protected function _getContentTransferEncoding()
+ protected function _getContentTransferEncoding(): string
{
$charset = strtoupper($this->charset);
if (in_array($charset, $this->_charset8bit)) {
@@ -1865,7 +1873,7 @@ protected function _getContentTransferEncoding()
*
* @return string
*/
- protected function _getContentTypeCharset()
+ protected function _getContentTypeCharset(): string
{
$charset = strtoupper($this->charset);
if (array_key_exists($charset, $this->_contentTypeCharset)) {
diff --git a/src/Network/Email/DebugTransport.php b/src/Network/Email/DebugTransport.php
index e87d722282..8a399a3a4b 100644
--- a/src/Network/Email/DebugTransport.php
+++ b/src/Network/Email/DebugTransport.php
@@ -30,9 +30,12 @@ class DebugTransport extends AbstractTransport
* Send mail
*
* @param CakeEmail $email CakeEmail
- * @return array
+ * @return array{
+ * headers: string,
+ * message: string
+ * }
*/
- public function send(CakeEmail $email)
+ public function send(CakeEmail $email): array
{
$headers = $email->getHeaders(['from', 'sender', 'replyTo', 'readReceipt', 'returnPath', 'to', 'cc', 'subject']);
$headers = $this->_headersToString($headers);
diff --git a/src/Network/Email/EmailConfigInterface.php b/src/Network/Email/EmailConfigInterface.php
new file mode 100644
index 0000000000..8e074a81d0
--- /dev/null
+++ b/src/Network/Email/EmailConfigInterface.php
@@ -0,0 +1,7 @@
+_lastResponse;
}
@@ -86,7 +86,7 @@ public function getLastResponse()
* @return array
* @throws SocketException
*/
- public function send(CakeEmail $email)
+ public function send(CakeEmail $email): array
{
$this->_connect();
$this->_auth();
@@ -100,10 +100,10 @@ public function send(CakeEmail $email)
/**
* Set the configuration
*
- * @param array $config Configuration options.
+ * @param array|null $config Configuration options.
* @return array Returns configs
*/
- public function config($config = null)
+ public function config(?array $config = null): array
{
if ($config === null) {
return $this->_config;
@@ -129,7 +129,7 @@ public function config($config = null)
* @param array $responseLines Response lines to parse.
* @return void
*/
- protected function _bufferResponseLines(array $responseLines)
+ protected function _bufferResponseLines(array $responseLines): void
{
$response = [];
foreach ($responseLines as $responseLine) {
@@ -149,7 +149,7 @@ protected function _bufferResponseLines(array $responseLines)
* @return void
* @throws SocketException
*/
- protected function _connect()
+ protected function _connect(): void
{
$this->_generateSocket();
if (!$this->_socket->connect()) {
@@ -190,7 +190,7 @@ protected function _connect()
* @return void
* @throws SocketException
*/
- protected function _auth()
+ protected function _auth(): void
{
if (isset($this->_config['username']) && isset($this->_config['password'])) {
$replyCode = $this->_smtpSend('AUTH LOGIN', '334|500|502|504');
@@ -219,7 +219,7 @@ protected function _auth()
* @param string $email The email address to send with the command.
* @return string
*/
- protected function _prepareFromCmd($email)
+ protected function _prepareFromCmd(string $email): string
{
return 'MAIL FROM:<' . $email . '>';
}
@@ -230,7 +230,7 @@ protected function _prepareFromCmd($email)
* @param string $email The email address to send with the command.
* @return string
*/
- protected function _prepareRcptCmd($email)
+ protected function _prepareRcptCmd(string $email): string
{
return 'RCPT TO:<' . $email . '>';
}
@@ -241,7 +241,7 @@ protected function _prepareRcptCmd($email)
* @param CakeEmail $email CakeEmail
* @return array
*/
- protected function _prepareFromAddress(CakeEmail $email)
+ protected function _prepareFromAddress(CakeEmail $email): array
{
$from = $email->returnPath();
if (empty($from)) {
@@ -257,7 +257,7 @@ protected function _prepareFromAddress(CakeEmail $email)
* @param CakeEmail $email CakeEmail
* @return array
*/
- protected function _prepareRecipientAddresses(CakeEmail $email)
+ protected function _prepareRecipientAddresses(CakeEmail $email): array
{
$to = $email->to();
$cc = $email->cc();
@@ -272,7 +272,7 @@ protected function _prepareRecipientAddresses(CakeEmail $email)
* @param CakeEmail $email CakeEmail
* @return array
*/
- protected function _prepareMessageHeaders(CakeEmail $email)
+ protected function _prepareMessageHeaders(CakeEmail $email): array
{
return $email->getHeaders(['from', 'sender', 'replyTo', 'readReceipt', 'to', 'cc', 'subject']);
}
@@ -283,7 +283,7 @@ protected function _prepareMessageHeaders(CakeEmail $email)
* @param CakeEmail $email CakeEmail
* @return string
*/
- protected function _prepareMessage(CakeEmail $email)
+ protected function _prepareMessage(CakeEmail $email): string
{
$lines = $email->message();
$messages = [];
@@ -305,7 +305,7 @@ protected function _prepareMessage(CakeEmail $email)
* @return void
* @throws SocketException
*/
- protected function _sendRcpt(CakeEmail $email)
+ protected function _sendRcpt(CakeEmail $email): void
{
$from = $this->_prepareFromAddress($email);
$this->_smtpSend($this->_prepareFromCmd(key($from)));
@@ -323,7 +323,7 @@ protected function _sendRcpt(CakeEmail $email)
* @return void
* @throws SocketException
*/
- protected function _sendData(CakeEmail $email)
+ protected function _sendData(CakeEmail $email): void
{
$this->_smtpSend('DATA', '354');
@@ -340,7 +340,7 @@ protected function _sendData(CakeEmail $email)
* @return void
* @throws SocketException
*/
- protected function _disconnect()
+ protected function _disconnect(): void
{
$this->_smtpSend('QUIT', false);
$this->_socket->disconnect();
@@ -352,7 +352,7 @@ protected function _disconnect()
* @return void
* @throws SocketException
*/
- protected function _generateSocket()
+ protected function _generateSocket(): void
{
$this->_socket = new CakeSocket($this->_config);
}
@@ -361,11 +361,11 @@ protected function _generateSocket()
* Protected method for sending data to SMTP connection
*
* @param string|null $data Data to be sent to SMTP server
- * @param string|bool $checkCode Code to check for in server response, false to skip
+ * @param string|false $checkCode Code to check for in server response, false to skip
* @return string|null The matched code, or null if nothing matched
* @throws SocketException
*/
- protected function _smtpSend($data, $checkCode = '250')
+ protected function _smtpSend(?string $data, string|false $checkCode = '250'): ?string
{
$this->_lastResponse = [];
@@ -377,7 +377,7 @@ protected function _smtpSend($data, $checkCode = '250')
$startTime = time();
while (!str_ends_with($response, "\r\n") && (time() - $startTime < $this->_config['timeout'])) {
$bytes = $this->_socket->read();
- if ($bytes === false || $bytes === null) {
+ if ($bytes === false) {
break;
}
$response .= $bytes;
@@ -399,5 +399,7 @@ protected function _smtpSend($data, $checkCode = '250')
}
throw new SocketException(__d('cake_dev', 'SMTP Error: %s', $response));
}
+
+ return null;
}
}
diff --git a/src/Network/Http/DigestAuthentication.php b/src/Network/Http/DigestAuthentication.php
index f9c09a5fa9..256445d1ff 100644
--- a/src/Network/Http/DigestAuthentication.php
+++ b/src/Network/Http/DigestAuthentication.php
@@ -33,7 +33,7 @@ class DigestAuthentication
* @return void
* @link http://www.ietf.org/rfc/rfc2617.txt
*/
- public static function authentication(HttpSocket $http, &$authInfo)
+ public static function authentication(HttpSocket $http, array &$authInfo): void
{
if (isset($authInfo['user'], $authInfo['pass'])) {
if (!isset($authInfo['realm']) && !static::_getServerInformation($http, $authInfo)) {
@@ -50,7 +50,7 @@ public static function authentication(HttpSocket $http, &$authInfo)
* @param array &$authInfo Authentication info.
* @return bool
*/
- protected static function _getServerInformation(HttpSocket $http, &$authInfo)
+ protected static function _getServerInformation(HttpSocket $http, array &$authInfo): bool
{
$originalRequest = $http->request;
$http->configAuth(false);
@@ -61,7 +61,7 @@ protected static function _getServerInformation(HttpSocket $http, &$authInfo)
if (empty($http->response['header']['WWW-Authenticate'])) {
return false;
}
- preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $http->response['header']['WWW-Authenticate'], $matches, PREG_SET_ORDER);
+ preg_match_all('@(\w+)=(?:"([^"]+)"|([^\s,$]+))@', $http->response['header']['WWW-Authenticate'], $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$authInfo[$match[1]] = $match[2];
}
@@ -79,13 +79,14 @@ protected static function _getServerInformation(HttpSocket $http, &$authInfo)
* @param array &$authInfo Authentication info.
* @return string
*/
- protected static function _generateHeader(HttpSocket $http, &$authInfo)
+ protected static function _generateHeader(HttpSocket $http, array &$authInfo): string
{
$a1 = md5($authInfo['user'] . ':' . $authInfo['realm'] . ':' . $authInfo['pass']);
$a2 = md5($http->request['method'] . ':' . $http->request['uri']['path']);
if (empty($authInfo['qop'])) {
$response = md5($a1 . ':' . $authInfo['nonce'] . ':' . $a2);
+ $nc = '';
} else {
$authInfo['cnonce'] = uniqid();
$nc = sprintf('%08x', $authInfo['nc']++);
diff --git a/src/Network/Http/HttpSocket.php b/src/Network/Http/HttpSocket.php
index 2bd365a273..b5448515ad 100644
--- a/src/Network/Http/HttpSocket.php
+++ b/src/Network/Http/HttpSocket.php
@@ -41,14 +41,14 @@ class HttpSocket extends CakeSocket
*
* @var bool
*/
- public $quirksMode = false;
+ public bool $quirksMode = false;
/**
* Contain information about the last request (read only)
*
- * @var array
+ * @var array
*/
- public $request = [
+ public array $request = [
'method' => 'GET',
'uri' => [
'scheme' => 'http',
@@ -75,23 +75,23 @@ class HttpSocket extends CakeSocket
/**
* Contain information about the last response (read only)
*
- * @var array
+ * @var HttpSocketResponse|null
*/
- public $response = null;
+ public ?HttpSocketResponse $response = null;
/**
* Response class name
*
* @var string
*/
- public $responseClass = 'HttpSocketResponse';
+ public string $responseClass = 'HttpSocketResponse';
/**
* Configuration settings for the HttpSocket and the requests
*
- * @var array
+ * @var array
*/
- public $config = [
+ public array $config = [
'persistent' => false,
'host' => 'localhost',
'protocol' => 'tcp',
@@ -117,19 +117,19 @@ class HttpSocket extends CakeSocket
*
* @var array
*/
- protected $_auth = [];
+ protected array $_auth = [];
/**
* Proxy settings
*
* @var array
*/
- protected $_proxy = [];
+ protected array $_proxy = [];
/**
* Resource to receive the content of request
*
- * @var mixed
+ * @var resource|null
*/
protected $_contentResource = null;
@@ -154,7 +154,7 @@ class HttpSocket extends CakeSocket
*
* @param array|string $config Configuration information, either a string URL or an array of options.
*/
- public function __construct($config = [])
+ public function __construct(array|string $config = [])
{
if (is_string($config)) {
$this->_configUri($config);
@@ -194,13 +194,16 @@ public function __construct($config = [])
*
* `$http->configAuth();`
*
- * @param string $method Authentication method (ie. Basic, Digest). If empty, disable authentication
- * @param array|string $user Username for authentication. Can be an array with settings to authentication class
- * @param string $pass Password for authentication
+ * @param string|false|null $method Authentication method (ie. Basic, Digest). If empty, disable authentication
+ * @param array|string|null $user Username for authentication. Can be an array with settings to authentication class
+ * @param string|null $pass Password for authentication
* @return void
*/
- public function configAuth($method, $user = null, $pass = null)
- {
+ public function configAuth(
+ string|false|null $method,
+ array|string|null $user = null,
+ ?string $pass = null,
+ ): void {
if (empty($method)) {
$this->_auth = [];
@@ -217,15 +220,20 @@ public function configAuth($method, $user = null, $pass = null)
/**
* Set proxy settings
*
- * @param array|string $host Proxy host. Can be an array with settings to authentication class
+ * @param array|string|null $host Proxy host. Can be an array with settings to authentication class
* @param int $port Port. Default 3128.
- * @param string $method Proxy method (ie, Basic, Digest). If empty, disable proxy authentication
- * @param string $user Username if your proxy need authentication
- * @param string $pass Password to proxy authentication
+ * @param string|null $method Proxy method (ie, Basic, Digest). If empty, disable proxy authentication
+ * @param string|null $user Username if your proxy need authentication
+ * @param string|null $pass Password to proxy authentication
* @return void
*/
- public function configProxy($host, $port = 3128, $method = null, $user = null, $pass = null)
- {
+ public function configProxy(
+ array|string|null $host,
+ int $port = 3128,
+ ?string $method = null,
+ ?string $user = null,
+ ?string $pass = null,
+ ): void {
if (empty($host)) {
$this->_proxy = [];
@@ -246,7 +254,7 @@ public function configProxy($host, $port = 3128, $method = null, $user = null, $
* @return void
* @throws SocketException
*/
- public function setContentResource($resource)
+ public function setContentResource($resource): void
{
if ($resource === false) {
$this->_contentResource = null;
@@ -263,11 +271,11 @@ public function setContentResource($resource)
* Issue the specified request. HttpSocket::get() and HttpSocket::post() wrap this
* method and provide a more granular interface.
*
- * @param array|string $request Either an URI string, or an array defining host/uri
+ * @param mixed $request Either an URI string, or an array defining host/uri
* @return HttpSocketResponse|false false on error, HttpSocketResponse on success
* @throws SocketException
*/
- public function request($request = [])
+ public function request(mixed $request = []): HttpSocketResponse|false
{
$this->reset(false);
@@ -282,10 +290,10 @@ public function request($request = [])
}
$uri = $this->_parseUri($request['uri']);
if (!isset($uri['host'])) {
- $host = $this->config['host'];
+ $_host = $this->config['host'];
}
if (isset($request['host'])) {
- $host = $request['host'];
+ $_host = $request['host'];
unset($request['host']);
}
$request['uri'] = $this->url($request['uri']);
@@ -305,8 +313,8 @@ public function request($request = [])
$this->request['cookies'] = array_merge($this->request['cookies'], $this->config['request']['cookies'][$Host], $request['cookies']);
}
- if (isset($host)) {
- $this->config['host'] = $host;
+ if (isset($_host)) {
+ $this->config['host'] = $_host;
}
$this->_setProxy();
@@ -381,9 +389,7 @@ public function request($request = [])
$this->request['raw'] = $this->request['line'];
}
- if ($this->request['header'] !== false) {
- $this->request['raw'] .= $this->request['header'];
- }
+ $this->request['raw'] .= $this->request['header'];
$this->request['raw'] .= "\r\n";
$this->request['raw'] .= $this->request['body'];
@@ -460,13 +466,16 @@ public function request($request = [])
* );
* ```
*
- * @param array|string $uri URI to request. Either a string uri, or a uri array, see HttpSocket::_parseUri()
- * @param array $query Querystring parameters to append to URI
+ * @param array|string|null $uri URI to request. Either a string uri, or a uri array, see HttpSocket::_parseUri()
+ * @param array|string|null $query Querystring parameters to append to URI
* @param array $request An indexed array with indexes such as 'method' or uri
* @return HttpSocketResponse|false Result of request, either false on failure or the response to the request.
*/
- public function get($uri = null, $query = [], $request = [])
- {
+ public function get(
+ array|string|null $uri = null,
+ array|string|null $query = [],
+ array $request = [],
+ ): HttpSocketResponse|false {
$uri = $this->_parseUri($uri, $this->config['request']['uri']);
if (isset($uri['query'])) {
$uri['query'] = array_merge($uri['query'], $query);
@@ -486,13 +495,16 @@ public function get($uri = null, $query = [], $request = [])
* By definition HEAD request are identical to GET request except they return no response body. This means that all
* information and examples relevant to GET also applys to HEAD.
*
- * @param array|string $uri URI to request. Either a string URI, or a URI array, see HttpSocket::_parseUri()
- * @param array $query Querystring parameters to append to URI
+ * @param array|string|null $uri URI to request. Either a string URI, or a URI array, see HttpSocket::_parseUri()
+ * @param array|string|null $query Querystring parameters to append to URI
* @param array $request An indexed array with indexes such as 'method' or uri
* @return HttpSocketResponse|false Result of request, either false on failure or the response to the request.
*/
- public function head($uri = null, $query = [], $request = [])
- {
+ public function head(
+ array|string|null $uri = null,
+ array|string|null $query = [],
+ array $request = [],
+ ): HttpSocketResponse|false {
$uri = $this->_parseUri($uri, $this->config['request']['uri']);
if (isset($uri['query'])) {
$uri['query'] = array_merge($uri['query'], $query);
@@ -518,13 +530,16 @@ public function head($uri = null, $query = [], $request = [])
* ));
* ```
*
- * @param array|string $uri URI to request. See HttpSocket::_parseUri()
- * @param array $data Array of request body data keys and values.
+ * @param array|string|null $uri URI to request. See HttpSocket::_parseUri()
+ * @param array|null $data Array of request body data keys and values.
* @param array $request An indexed array with indexes such as 'method' or uri
* @return HttpSocketResponse|false Result of request, either false on failure or the response to the request.
*/
- public function post($uri = null, $data = [], $request = [])
- {
+ public function post(
+ array|string|null $uri = null,
+ ?array $data = [],
+ array $request = [],
+ ): HttpSocketResponse|false {
$request = Hash::merge(['method' => 'POST', 'uri' => $uri, 'body' => $data], $request);
return $this->request($request);
@@ -533,13 +548,16 @@ public function post($uri = null, $data = [], $request = [])
/**
* Issues a PUT request to the specified URI, query, and request.
*
- * @param array|string $uri URI to request, See HttpSocket::_parseUri()
- * @param array $data Array of request body data keys and values.
+ * @param array|string|null $uri URI to request, See HttpSocket::_parseUri()
+ * @param array|null $data Array of request body data keys and values.
* @param array $request An indexed array with indexes such as 'method' or uri
* @return HttpSocketResponse|false Result of request
*/
- public function put($uri = null, $data = [], $request = [])
- {
+ public function put(
+ array|string|null $uri = null,
+ ?array $data = [],
+ array $request = [],
+ ): HttpSocketResponse|false {
$request = Hash::merge(['method' => 'PUT', 'uri' => $uri, 'body' => $data], $request);
return $this->request($request);
@@ -548,13 +566,16 @@ public function put($uri = null, $data = [], $request = [])
/**
* Issues a PATCH request to the specified URI, query, and request.
*
- * @param array|string $uri URI to request, See HttpSocket::_parseUri()
- * @param array $data Array of request body data keys and values.
+ * @param array|string|null $uri URI to request, See HttpSocket::_parseUri()
+ * @param array|null $data Array of request body data keys and values.
* @param array $request An indexed array with indexes such as 'method' or uri
* @return HttpSocketResponse|false Result of request
*/
- public function patch($uri = null, $data = [], $request = [])
- {
+ public function patch(
+ array|string|null $uri = null,
+ ?array $data = [],
+ array $request = [],
+ ): HttpSocketResponse|false {
$request = Hash::merge(['method' => 'PATCH', 'uri' => $uri, 'body' => $data], $request);
return $this->request($request);
@@ -563,13 +584,16 @@ public function patch($uri = null, $data = [], $request = [])
/**
* Issues a DELETE request to the specified URI, query, and request.
*
- * @param array|string $uri URI to request (see {@link _parseUri()})
- * @param array $data Array of request body data keys and values.
+ * @param array|string|null $uri URI to request (see {@link _parseUri()})
+ * @param array|null $data Array of request body data keys and values.
* @param array $request An indexed array with indexes such as 'method' or uri
* @return HttpSocketResponse|false Result of request
*/
- public function delete($uri = null, $data = [], $request = [])
- {
+ public function delete(
+ array|string|null $uri = null,
+ ?array $data = [],
+ array $request = [],
+ ): HttpSocketResponse|false {
$request = Hash::merge(['method' => 'DELETE', 'uri' => $uri, 'body' => $data], $request);
return $this->request($request);
@@ -600,10 +624,12 @@ public function delete($uri = null, $data = [], $request = [])
*
* @param array|string|bool|null $url Either a string or array of URL options to create a URL with.
* @param string|null $uriTemplate A template string to use for URL formatting.
- * @return mixed Either false on failure or a string containing the composed URL.
+ * @return array|string|bool Either false on failure or a string containing the composed URL.
*/
- public function url(array|string|bool|null $url = null, ?string $uriTemplate = null)
- {
+ public function url(
+ array|string|bool|null $url = null,
+ ?string $uriTemplate = null,
+ ): array|string|bool {
if ($url === null) {
$url = '/';
}
@@ -646,7 +672,7 @@ public function url(array|string|bool|null $url = null, ?string $uriTemplate = n
* @return void
* @throws SocketException
*/
- protected function _setAuth()
+ protected function _setAuth(): void
{
if (empty($this->_auth)) {
return;
@@ -673,7 +699,7 @@ protected function _setAuth()
* @return void
* @throws SocketException
*/
- protected function _setProxy()
+ protected function _setProxy(): void
{
if (empty($this->_proxy) || !isset($this->_proxy['host'], $this->_proxy['port'])) {
return;
@@ -709,10 +735,10 @@ protected function _setProxy()
/**
* Parses and sets the specified URI into current request configuration.
*
- * @param array|string $uri URI, See HttpSocket::_parseUri()
+ * @param array|string|null $uri URI, See HttpSocket::_parseUri()
* @return bool If uri has merged in config
*/
- protected function _configUri($uri = null): bool
+ protected function _configUri(array|string|null $uri = null): bool
{
if (empty($uri)) {
return false;
@@ -741,12 +767,14 @@ protected function _configUri($uri = null): bool
/**
* Takes a $uri array and turns it into a fully qualified URL string
*
- * @param array|string $uri Either A $uri array, or a request string. Will use $this->config if left empty.
+ * @param mixed $uri Either A $uri array, or a request string. Will use $this->config if left empty.
* @param string $uriTemplate The Uri template/format to use.
- * @return mixed A fully qualified URL formatted according to $uriTemplate, or false on failure
+ * @return array|string|false A fully qualified URL formatted according to $uriTemplate, or false on failure
*/
- protected function _buildUri($uri = [], $uriTemplate = '%scheme://%user:%pass@%host:%port/%path?%query#%fragment')
- {
+ protected function _buildUri(
+ mixed $uri = [],
+ string $uriTemplate = '%scheme://%user:%pass@%host:%port/%path?%query#%fragment',
+ ): array|string|false {
if (is_string($uri)) {
$uri = ['host' => $uri];
}
@@ -791,12 +819,14 @@ protected function _buildUri($uri = [], $uriTemplate = '%scheme://%user:%pass@%h
* Parses the given URI and breaks it down into pieces as an indexed array with elements
* such as 'scheme', 'port', 'query'.
*
- * @param array|string $uri URI to parse
+ * @param mixed $uri URI to parse
* @param array|bool $base If true use default URI config, otherwise indexed array to set 'scheme', 'host', 'port', etc.
* @return array|bool Parsed URI
*/
- protected function _parseUri($uri = null, $base = [])
- {
+ protected function _parseUri(
+ mixed $uri = null,
+ array|bool $base = [],
+ ): array|bool {
$uriBase = [
'scheme' => ['http', 'https'],
'host' => null,
@@ -858,11 +888,11 @@ protected function _parseUri($uri = null, $base = [])
* A leading '?' mark in $query is optional and does not effect the outcome of this function.
* For the complete capabilities of this implementation take a look at HttpSocketTest::testparseQuery()
*
- * @param array|string $query A query string to parse into an array or an array to return directly "as is"
+ * @param array|string|null $query A query string to parse into an array or an array to return directly "as is"
* @return array The $query parsed into a possibly multi-level array. If an empty $query is
* given, an empty array is returned.
*/
- protected function _parseQuery($query)
+ protected function _parseQuery(array|string|null $query): array
{
if (is_array($query)) {
return $query;
@@ -885,7 +915,7 @@ protected function _parseQuery($query)
$key = urldecode($key);
$value = urldecode($value);
- if (preg_match_all('/\[([^\[\]]*)\]/iUs', $key, $matches)) {
+ if (preg_match_all('/\[([^\[\]]*)\]/iU', $key, $matches)) {
$subKeys = $matches[1];
$rootKey = substr($key, 0, strpos($key, '['));
if (!empty($rootKey)) {
@@ -922,11 +952,11 @@ protected function _parseQuery($query)
/**
* Builds a request line according to HTTP/1.1 specs. Activate quirks mode to work outside specs.
*
- * @param array $request Needs to contain a 'uri' key. Should also contain a 'method' key, otherwise defaults to GET.
- * @return string Request line
+ * @param mixed $request Needs to contain a 'uri' key. Should also contain a 'method' key, otherwise defaults to GET.
+ * @return string|false Request line
* @throws SocketException
*/
- protected function _buildRequestLine($request = [])
+ protected function _buildRequestLine(mixed $request = []): string|false
{
$asteriskMethods = ['OPTIONS'];
@@ -962,11 +992,11 @@ protected function _buildRequestLine($request = [])
/**
* Builds the header.
*
- * @param array $header Header to build
+ * @param mixed $header Header to build
* @param string $mode Mode
- * @return string Header built from array
+ * @return string|false Header built from array
*/
- protected function _buildHeader($header, $mode = 'standard')
+ protected function _buildHeader(mixed $header, string $mode = 'standard'): string|false
{
if (is_string($header)) {
return $header;
@@ -1008,9 +1038,9 @@ protected function _buildHeader($header, $mode = 'standard')
* a simple key => value pair.
*
* @param array $cookies Array of cookies to send with the request.
- * @return string Cookie header string to be sent with the request.
+ * @return string|false Cookie header string to be sent with the request.
*/
- public function buildCookies($cookies)
+ public function buildCookies(array $cookies): string|false
{
$header = [];
foreach ($cookies as $name => $cookie) {
@@ -1029,25 +1059,24 @@ public function buildCookies($cookies)
* Escapes a given $token according to RFC 2616 (HTTP 1.1 specs)
*
* @param string $token Token to escape
- * @param array $chars Characters to escape
+ * @param array|null $chars Characters to escape
* @return string Escaped token
*/
- protected function _escapeToken($token, $chars = null)
+ protected function _escapeToken(string $token, ?array $chars = null): string
{
$regex = '/([' . implode('', $this->_tokenEscapeChars(true, $chars)) . '])/';
- $token = preg_replace($regex, '"\\1"', $token);
- return $token;
+ return preg_replace($regex, '"\\1"', $token);
}
/**
* Gets escape chars according to RFC 2616 (HTTP 1.1 specs).
*
* @param bool $hex true to get them as HEX values, false otherwise
- * @param array $chars Characters to escape
- * @return array Escape chars
+ * @param array|null $chars Characters to escape
+ * @return array|null Escape chars
*/
- protected function _tokenEscapeChars($hex = true, $chars = null)
+ protected function _tokenEscapeChars(bool $hex = true, ?array $chars = null): ?array
{
if (!empty($chars)) {
$escape = $chars;
@@ -1073,16 +1102,16 @@ protected function _tokenEscapeChars($hex = true, $chars = null)
* Resets the state of this HttpSocket instance to it's initial state (before CakeObject::__construct got executed) or does
* the same thing partially for the request and the response property only.
*
- * @param bool $full If set to false only HttpSocket::response and HttpSocket::request are reset
+ * @param array|bool|null $state If set to false only HttpSocket::response and HttpSocket::request are reset
* @return bool True on success
*/
- public function reset($full = true)
+ public function reset(array|bool|null $state = true): bool
{
static $initalState = [];
if (empty($initalState)) {
$initalState = get_class_vars(self::class);
}
- if (!$full) {
+ if (!$state) {
$this->request = $initalState['request'];
$this->response = $initalState['response'];
diff --git a/src/Network/Http/HttpSocketResponse.php b/src/Network/Http/HttpSocketResponse.php
index c0fda2c50b..ea3a63934d 100644
--- a/src/Network/Http/HttpSocketResponse.php
+++ b/src/Network/Http/HttpSocketResponse.php
@@ -31,51 +31,51 @@ class HttpSocketResponse implements ArrayAccess
/**
* Body content
*
- * @var string
+ * @var string|null
*/
- public $body = '';
+ public ?string $body = '';
/**
* Headers
*
- * @var array
+ * @var array
*/
- public $headers = [];
+ public array $headers = [];
/**
* Cookies
*
- * @var array
+ * @var array|false
*/
- public $cookies = [];
+ public array|false $cookies = [];
/**
* HTTP version
*
* @var string
*/
- public $httpVersion = 'HTTP/1.1';
+ public string $httpVersion = 'HTTP/1.1';
/**
* Response code
*
* @var int
*/
- public $code = 0;
+ public int $code = 0;
/**
* Reason phrase
*
* @var string
*/
- public $reasonPhrase = '';
+ public string $reasonPhrase = '';
/**
* Pure raw content
*
* @var string
*/
- public $raw = '';
+ public string $raw = '';
/**
* Context data in the response.
@@ -83,14 +83,21 @@ class HttpSocketResponse implements ArrayAccess
*
* @var array
*/
- public $context = [];
+ public array $context = [];
+
+ /**
+ * Internal storage for ArrayAccess
+ *
+ * @var array
+ */
+ protected array $_data = [];
/**
* Constructor
*
- * @param string $message Message to parse.
+ * @param string|null $message Message to parse.
*/
- public function __construct($message = null)
+ public function __construct(?string $message = null)
{
if ($message !== null) {
$this->parseResponse($message);
@@ -102,7 +109,7 @@ public function __construct($message = null)
*
* @return string
*/
- public function body()
+ public function body(): string
{
return (string)$this->body;
}
@@ -111,10 +118,10 @@ public function body()
* Get header in case insensitive
*
* @param string $name Header name.
- * @param array $headers Headers to format.
- * @return mixed String if header exists or null
+ * @param array|false|null $headers Headers to format.
+ * @return array|string|null String if header exists or null
*/
- public function getHeader($name, $headers = null)
+ public function getHeader(string $name, array|false|null $headers = null): array|string|null
{
if (!is_array($headers)) {
$headers =& $this->headers;
@@ -136,7 +143,7 @@ public function getHeader($name, $headers = null)
*
* @return bool
*/
- public function isOk()
+ public function isOk(): bool
{
return in_array($this->code, [200, 201, 202, 203, 204, 205, 206]);
}
@@ -146,7 +153,7 @@ public function isOk()
*
* @return bool
*/
- public function isRedirect()
+ public function isRedirect(): bool
{
return in_array($this->code, [301, 302, 303, 307]) && $this->getHeader('Location') !== null;
}
@@ -154,11 +161,11 @@ public function isRedirect()
/**
* Parses the given message and breaks it down in parts.
*
- * @param string $message Message to parse
+ * @param mixed $message Message to parse
* @return void
* @throws SocketException
*/
- public function parseResponse($message)
+ public function parseResponse(mixed $message): void
{
if (!is_string($message)) {
throw new SocketException(__d('cake_dev', 'Invalid response.'));
@@ -170,11 +177,11 @@ public function parseResponse($message)
[, $statusLine, $header] = $match;
$this->raw = $message;
- $this->body = (string)substr($message, strlen($match[0]));
+ $this->body = substr($message, strlen($match[0]));
- if (preg_match("/(.+) ([0-9]{3})(?:\s+(\w.+))?\s*\r\n/DU", $statusLine, $match)) {
+ if (preg_match("/(.+) ([0-9]{3})(?:\s+(\w.+))?\s*\r\n/U", $statusLine, $match)) {
$this->httpVersion = $match[1];
- $this->code = $match[2];
+ $this->code = (int)$match[2];
if (isset($match[3])) {
$this->reasonPhrase = $match[3];
}
@@ -198,11 +205,11 @@ public function parseResponse($message)
* Generic function to decode a $body with a given $encoding. Returns either an array with the keys
* 'body' and 'header' or false on failure.
*
- * @param string $body A string containing the body to decode.
- * @param string|bool $encoding Can be false in case no encoding is being used, or a string representing the encoding.
+ * @param mixed $body A string containing the body to decode.
+ * @param string|false|null $encoding Can be false in case no encoding is being used, or a string representing the encoding.
* @return mixed Array of response headers and body or false.
*/
- protected function _decodeBody($body, $encoding = 'chunked')
+ protected function _decodeBody(mixed $body, string|false|null $encoding = 'chunked'): mixed
{
if (!is_string($body)) {
return false;
@@ -223,17 +230,17 @@ protected function _decodeBody($body, $encoding = 'chunked')
* Decodes a chunked message $body and returns either an array with the keys 'body' and 'header' or false as
* a result.
*
- * @param string $body A string containing the chunked body to decode.
- * @return mixed Array of response headers and body or false.
+ * @param mixed $body A string containing the chunked body to decode.
+ * @return array{body: string, header: array|false}|false Array of response headers and body or false.
* @throws SocketException
*/
- protected function _decodeChunkedBody($body)
+ protected function _decodeChunkedBody(mixed $body): array|false
{
if (!is_string($body)) {
return false;
}
- $decodedBody = null;
+ $decodedBody = '';
$chunkLength = null;
while ($chunkLength !== 0) {
@@ -246,14 +253,8 @@ protected function _decodeChunkedBody($body)
1 => dechex($length),
];
}
- $chunkSize = 0;
- $hexLength = 0;
- if (isset($match[0])) {
- $chunkSize = $match[0];
- }
- if (isset($match[1])) {
- $hexLength = $match[1];
- }
+ $chunkSize = $match[0];
+ $hexLength = $match[1];
$chunkLength = hexdec($hexLength);
$body = substr($body, strlen($chunkSize));
@@ -275,10 +276,10 @@ protected function _decodeChunkedBody($body)
/**
* Parses an array based header.
*
- * @param array|string $header Header as an indexed array (field => value)
+ * @param mixed $header Header as an indexed array (field => value)
* @return array|false Parsed header
*/
- protected function _parseHeader($header)
+ protected function _parseHeader(mixed $header): array|false
{
if (is_array($header)) {
return $header;
@@ -290,6 +291,7 @@ protected function _parseHeader($header)
$lines = explode("\r\n", $header);
$header = [];
+ $field = '';
$value = '';
foreach ($lines as $line) {
if (strlen($line) === 0) {
@@ -307,6 +309,10 @@ protected function _parseHeader($header)
$field = $this->_unescapeToken($field);
}
+ if ($field === '') {
+ continue;
+ }
+
$value = trim($value);
if (!isset($header[$field]) || $continuation) {
$header[$field] = $value;
@@ -322,9 +328,9 @@ protected function _parseHeader($header)
* Parses cookies in response headers.
*
* @param array $header Header array containing one ore more 'Set-Cookie' headers.
- * @return mixed Either false on no cookies, or an array of cookies received.
+ * @return array|false Either false on no cookies, or an array of cookies received.
*/
- public function parseCookies($header)
+ public function parseCookies(array $header): array|false
{
$cookieHeader = $this->getHeader('Set-Cookie', $header);
if (!$cookieHeader) {
@@ -337,7 +343,7 @@ public function parseCookies($header)
$cookie = str_replace('";"', '{__cookie_replace__}', $cookie);
$parts = str_replace('{__cookie_replace__}', '";"', explode(';', $cookie));
} else {
- $parts = preg_split('/\;[ \t]*/', $cookie);
+ $parts = preg_split('/;[ \t]*/', $cookie);
}
$nameParts = explode('=', array_shift($parts), 2);
@@ -369,25 +375,24 @@ public function parseCookies($header)
* Unescapes a given $token according to RFC 2616 (HTTP 1.1 specs)
*
* @param string $token Token to unescape.
- * @param array $chars Characters to unescape.
+ * @param array|null $chars Characters to unescape.
* @return string Unescaped token
*/
- protected function _unescapeToken($token, $chars = null)
+ protected function _unescapeToken(string $token, ?array $chars = null): string
{
$regex = '/"([' . implode('', $this->_tokenEscapeChars(true, $chars)) . '])"/';
- $token = preg_replace($regex, '\\1', $token);
- return $token;
+ return preg_replace($regex, '\\1', $token);
}
/**
* Gets escape chars according to RFC 2616 (HTTP 1.1 specs).
*
* @param bool $hex True to get them as HEX values, false otherwise.
- * @param array $chars Characters to uescape.
+ * @param array|null $chars Characters to uescape.
* @return array Escape chars
*/
- protected function _tokenEscapeChars($hex = true, $chars = null)
+ protected function _tokenEscapeChars(bool $hex = true, ?array $chars = null): array
{
if (!empty($chars)) {
$escape = $chars;
@@ -417,16 +422,16 @@ protected function _tokenEscapeChars($hex = true, $chars = null)
*/
public function offsetExists(mixed $offset): bool
{
- return in_array($offset, ['raw', 'status', 'header', 'body', 'cookies']);
+ return in_array($offset, ['raw', 'status', 'header', 'body', 'cookies']) || isset($this->_data[$offset]);
}
/**
* ArrayAccess - Offset Get
*
* @param mixed $offset Offset to get.
- * @return mixed
+ * @return array|string|null
*/
- public function offsetGet(mixed $offset): mixed
+ public function offsetGet(mixed $offset): array|string|null
{
switch ($offset) {
case 'raw':
@@ -457,6 +462,10 @@ public function offsetGet(mixed $offset): mixed
return $this->cookies;
}
+ if (isset($this->_data[$offset])) {
+ return $this->_data[$offset];
+ }
+
return null;
}
@@ -469,6 +478,7 @@ public function offsetGet(mixed $offset): mixed
*/
public function offsetSet(mixed $offset, mixed $value): void
{
+ $this->_data[$offset] = $value;
}
/**
@@ -479,6 +489,7 @@ public function offsetSet(mixed $offset, mixed $value): void
*/
public function offsetUnset(mixed $offset): void
{
+ unset($this->_data[$offset]);
}
/**
@@ -486,7 +497,7 @@ public function offsetUnset(mixed $offset): void
*
* @return string
*/
- public function __toString()
+ public function __toString(): string
{
return $this->body();
}
diff --git a/src/Rector/TypeDeclaration/CakeTestFixturePropertyTypeRector.php b/src/Rector/TypeDeclaration/CakeTestFixturePropertyTypeRector.php
index 43bab198a4..1252e2e828 100644
--- a/src/Rector/TypeDeclaration/CakeTestFixturePropertyTypeRector.php
+++ b/src/Rector/TypeDeclaration/CakeTestFixturePropertyTypeRector.php
@@ -113,7 +113,7 @@ public function refactor(Node $node): ?Node
/**
* Get PhpParser type node for property
*/
- private function getPhpParserTypeForProperty(string $propertyName): Identifier|Name|NullableType|null
+ private function getPhpParserTypeForProperty(string $propertyName): Identifier|NullableType|null
{
return match ($propertyName) {
'name', 'table', 'primaryKey' => new NullableType(new Identifier('string')),
diff --git a/src/Routing/Dispatcher.php b/src/Routing/Dispatcher.php
index 35f5e6ac0d..d27b359b6e 100644
--- a/src/Routing/Dispatcher.php
+++ b/src/Routing/Dispatcher.php
@@ -46,16 +46,16 @@ class Dispatcher implements CakeEventListener
/**
* Event manager, used to handle dispatcher filters
*
- * @var CakeEventManager
+ * @var CakeEventManager|null
*/
- protected $_eventManager;
+ protected ?CakeEventManager $_eventManager = null;
/**
* Constructor.
*
- * @param string $base The base directory for the application. Writes `App.base` to Configure.
+ * @param string|false $base The base directory for the application. Writes `App.base` to Configure.
*/
- public function __construct($base = false)
+ public function __construct(string|false $base = false)
{
if ($base !== false) {
Configure::write('App.base', $base);
@@ -68,7 +68,7 @@ public function __construct($base = false)
*
* @return CakeEventManager
*/
- public function getEventManager()
+ public function getEventManager(): CakeEventManager
{
if (!$this->_eventManager) {
$this->_eventManager = new CakeEventManager();
@@ -82,7 +82,7 @@ public function getEventManager()
/**
* Returns the list of events this object listens to.
*
- * @return array
+ * @return array
*/
public function implementedEvents(): array
{
@@ -97,7 +97,7 @@ public function implementedEvents(): array
* @return void
* @throws MissingDispatcherFilterException
*/
- protected function _attachFilters($manager)
+ protected function _attachFilters(CakeEventManager $manager): void
{
$filters = Configure::read('Dispatcher.filters');
if (empty($filters)) {
@@ -147,13 +147,16 @@ protected function _attachFilters($manager)
* @param CakeRequest $request Request object to dispatch.
* @param CakeResponse $response Response object to put the results of the dispatch into.
* @param array $additionalParams Settings array ("bare", "return") which is melded with the GET and POST params
- * @return string|null if `$request['return']` is set then it returns response body, null otherwise
+ * @return mixed if `$request['return']` is set then it returns response body, null otherwise
* @triggers Dispatcher.beforeDispatch $this, compact('request', 'response', 'additionalParams')
* @triggers Dispatcher.afterDispatch $this, compact('request', 'response')
* @throws MissingControllerException When the controller is missing.
*/
- public function dispatch(CakeRequest $request, CakeResponse $response, $additionalParams = [])
- {
+ public function dispatch(
+ CakeRequest $request,
+ CakeResponse $response,
+ array $additionalParams = [],
+ ): mixed {
$beforeEvent = new CakeEvent('Dispatcher.beforeDispatch', $this, compact('request', 'response', 'additionalParams'));
$this->getEventManager()->dispatch($beforeEvent);
@@ -184,6 +187,8 @@ public function dispatch(CakeRequest $request, CakeResponse $response, $addition
$afterEvent = new CakeEvent('Dispatcher.afterDispatch', $this, compact('request', 'response'));
$this->getEventManager()->dispatch($afterEvent);
$afterEvent->data['response']->send();
+
+ return null;
}
/**
@@ -196,8 +201,10 @@ public function dispatch(CakeRequest $request, CakeResponse $response, $addition
* @param CakeRequest $request The request object to invoke the controller for.
* @return CakeResponse the resulting response object
*/
- protected function _invoke(Controller $controller, CakeRequest $request)
- {
+ protected function _invoke(
+ Controller $controller,
+ CakeRequest $request,
+ ): CakeResponse {
$controller->constructClasses();
$controller->startupProcess();
@@ -226,7 +233,7 @@ protected function _invoke(Controller $controller, CakeRequest $request)
* @param CakeEvent $event containing the request, response and additional params
* @return void
*/
- public function parseParams($event)
+ public function parseParams(CakeEvent $event): void
{
$request = $event->data['request'];
Router::setRequestInfo($request);
@@ -243,29 +250,34 @@ public function parseParams($event)
*
* @param CakeRequest $request Request object
* @param CakeResponse $response Response for the controller.
- * @return mixed|false name of controller if not loaded, or object if loaded
+ * @return Controller|null name of controller if not loaded, or object if loaded
*/
- protected function _getController($request, $response)
- {
+ protected function _getController(
+ CakeRequest $request,
+ CakeResponse $response,
+ ): ?Controller {
$ctrlClass = $this->_loadController($request);
if (!$ctrlClass) {
- return false;
+ return null;
}
$reflection = new ReflectionClass($ctrlClass);
if ($reflection->isAbstract() || $reflection->isInterface()) {
- return false;
+ return null;
}
- return $reflection->newInstance($request, $response);
+ /** @var Controller|null $instance */
+ $instance = $reflection->newInstance($request, $response);
+
+ return $instance;
}
/**
* Load controller and return controller class name
*
* @param CakeRequest $request Request instance.
- * @return string|bool Name of controller class name
+ * @return class-string|false Name of controller class name
*/
- protected function _loadController($request)
+ protected function _loadController(CakeRequest $request): string|false
{
$pluginName = $pluginPath = $controller = null;
if (!empty($request->params['plugin'])) {
diff --git a/src/Routing/DispatcherFilter.php b/src/Routing/DispatcherFilter.php
index a9b9b2b642..019f0b9b3a 100644
--- a/src/Routing/DispatcherFilter.php
+++ b/src/Routing/DispatcherFilter.php
@@ -35,21 +35,21 @@ abstract class DispatcherFilter implements CakeEventListener
*
* @var int
*/
- public $priority = 10;
+ public int $priority = 10;
/**
* Settings for this filter
*
* @var array
*/
- public $settings = [];
+ public array $settings = [];
/**
* Constructor.
*
* @param array $settings Configuration settings for the filter.
*/
- public function __construct($settings = [])
+ public function __construct(array $settings = [])
{
$this->settings = Hash::merge($this->settings, $settings);
}
@@ -102,9 +102,10 @@ public function beforeDispatch(CakeEvent $event): CakeResponse|false|null
*
* @param CakeEvent $event container object having the `request` and `response`
* keys in the data property.
- * @return mixed boolean to stop the event dispatching or null to continue
+ * @return false|null false to stop the event dispatching or null to continue
*/
- public function afterDispatch(CakeEvent $event)
+ public function afterDispatch(CakeEvent $event): ?bool
{
+ return null;
}
}
diff --git a/src/Routing/Filter/AssetDispatcher.php b/src/Routing/Filter/AssetDispatcher.php
index 7a9986a81c..6647d63899 100644
--- a/src/Routing/Filter/AssetDispatcher.php
+++ b/src/Routing/Filter/AssetDispatcher.php
@@ -39,7 +39,7 @@ class AssetDispatcher extends DispatcherFilter
*
* @var int
*/
- public $priority = 9;
+ public int $priority = 9;
/**
* Checks if a requested asset exists and sends it to the browser
diff --git a/src/Routing/Filter/CacheDispatcher.php b/src/Routing/Filter/CacheDispatcher.php
index 31a3de778a..bd03373e57 100644
--- a/src/Routing/Filter/CacheDispatcher.php
+++ b/src/Routing/Filter/CacheDispatcher.php
@@ -36,7 +36,7 @@ class CacheDispatcher extends DispatcherFilter
*
* @var int
*/
- public $priority = 9;
+ public int $priority = 9;
/**
* Checks whether the response was cached and set the body accordingly.
diff --git a/src/Routing/Route/CakeRoute.php b/src/Routing/Route/CakeRoute.php
index bf36815b54..6cf18a558b 100644
--- a/src/Routing/Route/CakeRoute.php
+++ b/src/Routing/Route/CakeRoute.php
@@ -15,6 +15,7 @@
namespace Cake\Routing\Route;
+use Cake\Network\CakeResponse;
use Cake\Routing\Router;
use Cake\Utility\Hash;
@@ -26,6 +27,7 @@
* Routes for your application.
*
* @package Cake.Routing.Route
+ * @property CakeResponse $response
*/
class CakeRoute
{
@@ -35,50 +37,50 @@ class CakeRoute
*
* @var array
*/
- public $keys = [];
+ public array $keys = [];
/**
* An array of additional parameters for the Route.
*
* @var array
*/
- public $options = [];
+ public array $options = [];
/**
* Default parameters for a Route
*
* @var array
*/
- public $defaults = [];
+ public array $defaults = [];
/**
* The routes template string.
*
- * @var string
+ * @var string|null
*/
- public $template = null;
+ public ?string $template = null;
/**
* Is this route a greedy route? Greedy routes have a `/*` in their
* template
*
- * @var string
+ * @var bool
*/
- protected $_greedy = false;
+ protected bool $_greedy = false;
/**
* The compiled route regular expression
*
- * @var string
+ * @var string|null
*/
- protected $_compiledRoute = null;
+ protected ?string $_compiledRoute = null;
/**
* HTTP header shortcut map. Used for evaluating header-based route expressions.
*
- * @var array
+ * @var array
*/
- protected $_headerMap = [
+ protected array $_headerMap = [
'type' => 'content_type',
'method' => 'request_method',
'server' => 'server_name',
@@ -88,11 +90,14 @@ class CakeRoute
* Constructor for a Route
*
* @param string $template Template string with parameter placeholders
- * @param array $defaults Array of defaults for the route.
- * @param array $options Array of additional options for the Route
+ * @param array|string|null $defaults Array of defaults for the route.
+ * @param array|string|null $options Array of additional options for the Route
*/
- public function __construct($template, $defaults = [], $options = [])
- {
+ public function __construct(
+ string $template,
+ array|string|null $defaults = [],
+ array|string|null $options = [],
+ ) {
$this->template = $template;
$this->defaults = (array)$defaults;
$this->options = (array)$options;
@@ -103,7 +108,7 @@ public function __construct($template, $defaults = [], $options = [])
*
* @return bool
*/
- public function compiled()
+ public function compiled(): bool
{
return !empty($this->_compiledRoute);
}
@@ -114,9 +119,9 @@ public function compiled()
* Modifies defaults property so all necessary keys are set
* and populates $this->names with the named routing elements.
*
- * @return array Returns a string regular expression of the compiled route.
+ * @return string|null Returns a string regular expression of the compiled route.
*/
- public function compile()
+ public function compile(): ?string
{
if ($this->compiled()) {
return $this->_compiledRoute;
@@ -134,7 +139,7 @@ public function compile()
*
* @return void
*/
- protected function _writeRoute()
+ protected function _writeRoute(): void
{
if (empty($this->template) || ($this->template === '/')) {
$this->_compiledRoute = '#^/*$#';
@@ -208,11 +213,7 @@ public function parse(string $url): array|bool
foreach ($this->defaults as $key => $val) {
$key = (string)$key;
if ($key[0] === '[' && preg_match('/^\[(\w+)\]$/', $key, $header)) {
- if (isset($this->_headerMap[$header[1]])) {
- $header = $this->_headerMap[$header[1]];
- } else {
- $header = 'http_' . $header[1];
- }
+ $header = $this->_headerMap[$header[1]] ?? 'http_' . $header[1];
$header = strtoupper($header);
$val = (array)$val;
@@ -277,10 +278,10 @@ public function parse(string $url): array|bool
* The local and global configuration for named parameters will be used.
*
* @param string $args A string with the passed & named params. eg. /1/page:2
- * @param string $context The current route context, which should contain controller/action keys.
+ * @param array $context The current route context, which should contain controller/action keys.
* @return array Array of ($pass, $named)
*/
- protected function _parseArgs($args, $context)
+ protected function _parseArgs(string $args, array $context): array
{
$pass = $named = [];
$args = explode('/', $args);
@@ -300,7 +301,7 @@ protected function _parseArgs($args, $context)
}
foreach ($args as $param) {
- if (empty($param) && $param !== '0' && $param !== 0) {
+ if ($param === '') {
continue;
}
@@ -345,15 +346,19 @@ protected function _parseArgs($args, $context)
* Currently implemented rule types are controller, action and match that can be combined with each other.
*
* @param string $val The value of the named parameter
- * @param array $rule The rule(s) to apply, can also be a match string
- * @param string $context An array with additional context information (controller / action)
+ * @param array|string|bool $rule The rule(s) to apply, can also be a match string
+ * @param array $context An array with additional context information (controller / action)
* @return bool
*/
- protected function _matchNamed($val, $rule, $context)
- {
+ protected function _matchNamed(
+ string $val,
+ array|string|bool|null $rule,
+ array $context,
+ ): bool {
if ($rule === true || $rule === false) {
return $rule;
}
+
if (is_string($rule)) {
$rule = ['match' => $rule];
}
@@ -388,7 +393,7 @@ protected function _matchNamed($val, $rule, $context)
* @param array $params An array of persistent values to replace persistent ones.
* @return array An array with persistent parameters applied.
*/
- public function persistParams($url, $params)
+ public function persistParams(array $url, array $params): array
{
if (empty($this->options['persist']) || !is_array($this->options['persist'])) {
return $url;
@@ -410,9 +415,9 @@ public function persistParams($url, $params)
* This method handles the reverse routing or conversion of URL arrays into string URLs.
*
* @param array $url An array of parameters to check matching with.
- * @return mixed Either a string URL for the parameters if they match or false.
+ * @return string|false Either a string URL for the parameters if they match or false.
*/
- public function match($url)
+ public function match(array $url): string|false
{
if (!$this->compiled()) {
$this->compile();
@@ -476,7 +481,7 @@ public function match($url)
}
// keys that don't exist are different.
- if (!$defaultExists && !empty($value)) {
+ if (!empty($value)) {
return false;
}
}
@@ -508,7 +513,7 @@ public function match($url)
* @param array $params The params to convert to a string URL.
* @return string Composed route string.
*/
- protected function _writeUrl($params)
+ protected function _writeUrl(array $params): string
{
if (isset($params['prefix'])) {
$prefixed = $params['prefix'] . '_';
@@ -577,7 +582,7 @@ protected function _writeUrl($params)
* @param array $fields Key/Value of object attributes
* @return CakeRoute A new instance of the route
*/
- public static function __set_state($fields)
+ public static function __set_state(array $fields): CakeRoute
{
$class = function_exists('get_called_class') ? static::class : self::class;
$obj = new $class('');
diff --git a/src/Routing/Route/PluginShortRoute.php b/src/Routing/Route/PluginShortRoute.php
index 710a1c047b..c92f78d226 100644
--- a/src/Routing/Route/PluginShortRoute.php
+++ b/src/Routing/Route/PluginShortRoute.php
@@ -30,7 +30,7 @@ class PluginShortRoute extends CakeRoute
* @param string $url The URL to parse
* @return array|bool false on failure, or an array of request parameters
*/
- public function parse($url): array|bool
+ public function parse(string $url): array|bool
{
$params = parent::parse($url);
if (!$params) {
@@ -46,9 +46,9 @@ public function parse($url): array|bool
* are not the same the match is an auto fail.
*
* @param array $url Array of parameters to convert to a string.
- * @return mixed either false or a string URL.
+ * @return string|false either false or a string URL.
*/
- public function match($url)
+ public function match(array $url): string|false
{
if (isset($url['controller']) && isset($url['plugin']) && $url['plugin'] != $url['controller']) {
return false;
diff --git a/src/Routing/Route/RedirectRoute.php b/src/Routing/Route/RedirectRoute.php
index 441205a060..8ff0231e45 100644
--- a/src/Routing/Route/RedirectRoute.php
+++ b/src/Routing/Route/RedirectRoute.php
@@ -31,33 +31,36 @@ class RedirectRoute extends CakeRoute
/**
* A CakeResponse object
*
- * @var CakeResponse
+ * @var CakeResponse|null
*/
- public $response = null;
+ public ?CakeResponse $response = null;
/**
* The location to redirect to. Either a string or a CakePHP array URL.
*
- * @var mixed
+ * @var array|string
*/
- public $redirect;
+ public array|string $redirect;
/**
* Flag for disabling exit() when this route parses a URL.
*
* @var bool
*/
- public $stop = true;
+ public bool $stop = true;
/**
* Constructor
*
* @param string $template Template string with parameter placeholders
- * @param array $defaults Array of defaults for the route.
- * @param array $options Array of additional options for the Route
+ * @param array|string|null $defaults Array of defaults for the route.
+ * @param array|string|null $options Array of additional options for the Route
*/
- public function __construct($template, $defaults = [], $options = [])
- {
+ public function __construct(
+ string $template,
+ array|string|null $defaults = [],
+ array|string|null $options = [],
+ ) {
parent::__construct($template, $defaults, $options);
$this->redirect = (array)$defaults;
}
@@ -79,7 +82,7 @@ public function parse(string $url): bool
$this->response = new CakeResponse();
}
$redirect = $this->redirect;
- if (count($this->redirect) === 1 && !isset($this->redirect['controller'])) {
+ if (count((array)$this->redirect) === 1 && !isset($this->redirect['controller'])) {
$redirect = $this->redirect[0];
}
if (isset($this->options['persist']) && is_array($redirect)) {
@@ -109,9 +112,9 @@ public function parse(string $url): bool
* There is no reverse routing redirection routes
*
* @param array $url Array of parameters to convert to a string.
- * @return mixed either false or a string URL.
+ * @return string|false either false or a string URL.
*/
- public function match($url)
+ public function match(array $url): string|false
{
return false;
}
@@ -123,7 +126,7 @@ public function match($url)
* @param string|int $code See http://php.net/exit for values
* @return void
*/
- protected function _stop($code = 0): void
+ protected function _stop(string|int $code = 0): void
{
if ($this->stop) {
exit($code);
diff --git a/src/Routing/Router.php b/src/Routing/Router.php
index 5dcd8bf550..bf2570d664 100644
--- a/src/Routing/Router.php
+++ b/src/Routing/Router.php
@@ -23,6 +23,7 @@
use Cake\Error\RouterException;
use Cake\Network\CakeRequest;
use Cake\Routing\Route\CakeRoute;
+use Cake\Routing\Route\RedirectRoute;
use Cake\Utility\Hash;
use Cake\Utility\Inflector;
@@ -52,14 +53,14 @@ class Router
*
* @var array
*/
- public static $routes = [];
+ public static array $routes = [];
/**
* Have routes been loaded
*
* @var bool
*/
- public static $initialized = false;
+ public static bool $initialized = false;
/**
* Contains the base string that will be applied to all generated URLs
@@ -67,7 +68,7 @@ class Router
*
* @var string
*/
- protected static $_fullBaseUrl;
+ protected static string $_fullBaseUrl = '';
/**
* List of action prefixes used in connected routes.
@@ -75,14 +76,14 @@ class Router
*
* @var array
*/
- protected static $_prefixes = [];
+ protected static array $_prefixes = [];
/**
* Directive for Router to parse out file extensions for mapping to Content-types.
*
* @var bool
*/
- protected static $_parseExtensions = false;
+ protected static bool $_parseExtensions = false;
/**
* List of valid extensions to parse from a URL. If null, any extension is allowed.
@@ -136,9 +137,9 @@ class Router
/**
* Named expressions
*
- * @var array
+ * @var array
*/
- protected static $_namedExpressions = [
+ protected static array $_namedExpressions = [
'Action' => Router::ACTION,
'Year' => Router::YEAR,
'Month' => Router::MONTH,
@@ -150,9 +151,11 @@ class Router
/**
* Stores all information necessary to decide what named arguments are parsed under what conditions.
*
- * @var string
+ * @var array{
+ * default: array, greedyNamed: bool, separator: string, rules: mixed
+ * }
*/
- protected static $_namedConfig = [
+ protected static array $_namedConfig = [
'default' => ['page', 'fields', 'order', 'limit', 'recursive', 'sort', 'direction', 'step'],
'greedyNamed' => true,
'separator' => ':',
@@ -162,16 +165,16 @@ class Router
/**
* The route matching the URL of the current request
*
- * @var array
+ * @var array
*/
- protected static $_currentRoute = [];
+ protected static array $_currentRoute = [];
/**
* Default HTTP request method => controller action map.
*
- * @var array
+ * @var array
*/
- protected static $_resourceMap = [
+ protected static array $_resourceMap = [
['action' => 'index', 'method' => 'GET', 'id' => false],
['action' => 'view', 'method' => 'GET', 'id' => true],
['action' => 'add', 'method' => 'POST', 'id' => false],
@@ -185,7 +188,7 @@ class Router
*
* @var array
*/
- protected static $_resourceMapped = [];
+ protected static array $_resourceMapped = [];
/**
* Maintains the request object stack for the current request.
@@ -193,7 +196,7 @@ class Router
*
* @var array
*/
- protected static $_requests = [];
+ protected static array $_requests = [];
/**
* Initial state is populated the first time reload() is called which is at the bottom
@@ -202,29 +205,31 @@ class Router
*
* @var array
*/
- protected static $_initialState = [];
+ protected static array $_initialState = [];
/**
* Default route class to use
*
* @var string
*/
- protected static $_routeClass = CakeRoute::class;
+ protected static string $_routeClass = CakeRoute::class;
/**
* Set the default route class to use or return the current one
*
- * @param string $routeClass The route class to set as default.
+ * @param string|null $routeClass The route class to set as default.
* @return string|null The default route class.
* @throws RouterException
*/
- public static function defaultRouteClass($routeClass = null)
+ public static function defaultRouteClass(?string $routeClass = null): ?string
{
if ($routeClass === null) {
return static::$_routeClass;
}
static::$_routeClass = static::_validateRouteClass($routeClass);
+
+ return static::$_routeClass;
}
/**
@@ -234,7 +239,7 @@ public static function defaultRouteClass($routeClass = null)
* @return string
* @throws RouterException
*/
- protected static function _validateRouteClass($routeClass)
+ protected static function _validateRouteClass(string $routeClass): string
{
if (
$routeClass !== 'CakeRoute' &&
@@ -251,7 +256,7 @@ protected static function _validateRouteClass($routeClass)
*
* @return void
*/
- protected static function _setPrefixes()
+ protected static function _setPrefixes(): void
{
$routing = Configure::read('Routing');
if (!empty($routing['prefixes'])) {
@@ -265,7 +270,7 @@ protected static function _setPrefixes()
* @return array Named route elements
* @see Router::$_namedExpressions
*/
- public static function getNamedExpressions()
+ public static function getNamedExpressions(): array
{
return static::$_namedExpressions;
}
@@ -273,16 +278,18 @@ public static function getNamedExpressions()
/**
* Resource map getter & setter.
*
- * @param array $resourceMap Resource map
- * @return mixed
+ * @param ?array $resourceMap Resource map
+ * @return array
* @see Router::$_resourceMap
*/
- public static function resourceMap($resourceMap = null)
+ public static function resourceMap(?array $resourceMap = null): array
{
if ($resourceMap === null) {
return static::$_resourceMap;
}
static::$_resourceMap = $resourceMap;
+
+ return static::$_resourceMap;
}
/**
@@ -355,8 +362,11 @@ public static function resourceMap($resourceMap = null)
* @return array Array of routes
* @throws RouterException
*/
- public static function connect($route, $defaults = [], $options = [])
- {
+ public static function connect(
+ $route,
+ $defaults = [],
+ $options = [],
+ ): array {
static::$initialized = true;
foreach (static::$_prefixes as $prefix) {
@@ -390,7 +400,7 @@ public static function connect($route, $defaults = [], $options = [])
$routeClass = static::_validateRouteClass($routeClass);
unset($options['routeClass']);
}
- if ($routeClass === 'RedirectRoute' && isset($defaults['redirect'])) {
+ if ($routeClass === RedirectRoute::class && isset($defaults['redirect'])) {
$defaults = $defaults['redirect'];
}
static::$routes[] = new $routeClass($route, $defaults, $options);
@@ -631,9 +641,10 @@ public static function parse($url)
if (strlen($url) && !str_starts_with($url, '/')) {
$url = '/' . $url;
}
+ $result = [];
if (str_contains($url, '?')) {
[$url, $queryParameters] = explode('?', $url, 2);
- parse_str($queryParameters, $queryParameters);
+ parse_str($queryParameters, $result);
}
extract(static::_parseExtension($url));
@@ -653,8 +664,8 @@ public static function parse($url)
$out['ext'] = $ext;
}
- if (!empty($queryParameters) && !isset($out['?'])) {
- $out['?'] = $queryParameters;
+ if (!empty($result) && !isset($out['?'])) {
+ $out['?'] = $result;
}
return $out;
@@ -663,10 +674,10 @@ public static function parse($url)
/**
* Parses a file extension out of a URL, if Router::parseExtensions() is enabled.
*
- * @param string $url URL.
- * @return array Returns an array containing the altered URL and the parsed extension.
+ * @param string|null $url URL.
+ * @return array{ext: string|null, url: string|null} Returns an array containing the altered URL and the parsed extension.
*/
- protected static function _parseExtension($url)
+ protected static function _parseExtension(?string $url): array
{
$ext = null;
@@ -864,7 +875,10 @@ public static function promote($which = null)
* or an array specifying any of the following: 'controller', 'action',
* and/or 'plugin', in addition to named arguments (keyed array elements),
* and standard URL arguments (indexed array elements)
- * @param array|bool $full If (bool) true, the full base URL will be prepended to the result.
+ * @param array{
+ * escape?: bool,
+ * full?: bool
+ * }|bool $full If (bool) true, the full base URL will be prepended to the result.
* If an array accepts the following keys
* - escape - used when making URLs embedded in html escapes query string '&'
* - full - if true the full base URL will be prepended.
@@ -876,14 +890,21 @@ public static function url(array|string|null $url = null, array|bool $full = fal
static::_loadRoutes();
}
- $params = ['plugin' => null, 'controller' => null, 'action' => 'index'];
-
if (is_bool($full)) {
$escape = false;
} else {
- extract($full + ['escape' => false, 'full' => false]);
+ $full += ['escape' => false, 'full' => false];
+
+ $escape = $full['escape'];
+ $full = $full['full'];
}
+ $params = [
+ 'plugin' => null,
+ 'controller' => null,
+ 'action' => 'index',
+ ];
+
$path = ['base' => null];
if (!empty(static::$_requests)) {
$request = static::$_requests[count(static::$_requests) - 1];
@@ -982,7 +1003,7 @@ public static function url(array|string|null $url = null, array|bool $full = fal
$output .= Inflector::underscore($params['controller']) . '/' . $url;
}
}
- $protocol = preg_match('#^[a-z][a-z0-9+\-.]*\://#i', $output);
+ $protocol = preg_match('#^[a-z][a-z0-9+\-.]*://#i', $output);
if ($protocol === 0) {
$output = str_replace('//', '/', $base . '/' . $output);
@@ -1215,7 +1236,7 @@ public static function reverse($params, $full = false)
* @param array|string $url URL to normalize Either an array or a string URL.
* @return string Normalized URL
*/
- public static function normalize($url = '/')
+ public static function normalize(array|string $url = '/'): string
{
if (is_array($url)) {
$url = Router::url($url);
@@ -1233,7 +1254,7 @@ public static function normalize($url = '/')
while (str_contains($url, '//')) {
$url = str_replace('//', '/', $url);
}
- $url = preg_replace('/(?:(\/$))/', '', $url);
+ $url = preg_replace('/(\/$)/', '', $url);
if (empty($url)) {
return '/';
@@ -1255,9 +1276,9 @@ public static function requestRoute()
/**
* Returns the route matching the current request (useful for requestAction traces)
*
- * @return CakeRoute Matching route object.
+ * @return CakeRoute|false Matching route object.
*/
- public static function currentRoute()
+ public static function currentRoute(): CakeRoute|false
{
$count = count(static::$_currentRoute) - 1;
@@ -1268,13 +1289,13 @@ public static function currentRoute()
* Removes the plugin name from the base URL.
*
* @param string $base Base URL
- * @param string $plugin Plugin name
+ * @param string|null $plugin Plugin name
* @return string base URL with plugin name removed if present
*/
- public static function stripPlugin($base, $plugin = null)
+ public static function stripPlugin(string $base, ?string $plugin = null)
{
if ($plugin) {
- $base = preg_replace('/(?:' . $plugin . ')/', '', $base);
+ $base = preg_replace('/' . preg_quote($plugin, '/') . '/', '', $base);
$base = str_replace('//', '', $base);
$pos1 = strrpos($base, '/');
$char = strlen($base) - 1;
@@ -1359,12 +1380,12 @@ public static function setExtensions(?array $extensions, bool $merge = true): ar
*
* @return void
*/
- protected static function _loadRoutes()
+ protected static function _loadRoutes(): void
{
static::$initialized = true;
include CONFIG . 'routes.php';
}
}
-//Save the initial state
+// Save the initial state
Router::reload();
diff --git a/src/TestSuite/CakeTestCase.php b/src/TestSuite/CakeTestCase.php
index 20ac62b5f8..29e591a84f 100644
--- a/src/TestSuite/CakeTestCase.php
+++ b/src/TestSuite/CakeTestCase.php
@@ -43,10 +43,17 @@
#[AllowDynamicProperties]
abstract class CakeTestCase extends TestCase
{
+ /**
+ * fixtures property
+ *
+ * @var array
+ */
+ public array $fixtures = [];
+
/**
* The class responsible for managing the creation, loading and removing of fixtures
*
- * @var CakeFixtureManager
+ * @var CakeFixtureManager|null
*/
public ?CakeFixtureManager $fixtureManager = null;
@@ -54,9 +61,9 @@ abstract class CakeTestCase extends TestCase
* By default, all fixtures attached to this class will be truncated and reloaded after each test.
* Set this to false to handle manually
*
- * @var array
+ * @var bool
*/
- public $autoFixtures = true;
+ public bool $autoFixtures = true;
/**
* Control table create/drops on each test method.
@@ -67,21 +74,21 @@ abstract class CakeTestCase extends TestCase
*
* @var bool
*/
- public $dropTables = true;
+ public bool $dropTables = true;
/**
* Configure values to restore at end of test.
*
* @var array
*/
- protected $_configure = [];
+ protected array $_configure = [];
/**
* Path settings to restore at the end of the test.
*
* @var array
*/
- protected $_pathRestore = [];
+ protected array $_pathRestore = [];
/**
* Called when a test case method is about to start (to be overridden when needed.)
@@ -89,7 +96,7 @@ abstract class CakeTestCase extends TestCase
* @param string $method Test method about to get executed.
* @return void
*/
- public function startTest($method)
+ public function startTest(string $method): void
{
}
@@ -99,19 +106,21 @@ public function startTest($method)
* @param string $method Test method about that was executed.
* @return void
*/
- public function endTest($method)
+ public function endTest(string $method): void
{
}
/**
* Overrides SimpleTestCase::skipIf to provide a boolean return value
*
- * @param bool $shouldSkip Whether or not the test should be skipped.
+ * @param bool|null $shouldSkip Whether or not the test should be skipped.
* @param string $message The message to display.
- * @return bool
+ * @return bool|null
*/
- public function skipIf($shouldSkip, $message = '')
- {
+ public function skipIf(
+ ?bool $shouldSkip,
+ string $message = '',
+ ): ?bool {
if ($shouldSkip) {
$this->markTestSkipped($message);
}
@@ -170,13 +179,11 @@ public function tearDown(): void
* @param string $format format to be used.
* @return string
*/
- public static function date($format = 'Y-m-d H:i:s')
+ public static function date(string $format = 'Y-m-d H:i:s'): string
{
return CakeTestSuiteDispatcher::date($format);
}
-// @codingStandardsIgnoreStart PHPUnit overrides don't match CakePHP
-
/**
* Announces the start of a test.
*
@@ -199,8 +206,6 @@ protected function assertPostConditions(): void
$this->endTest($this->getName());
}
-// @codingStandardsIgnoreEnd
-
/**
* Chooses which fixtures to load for a given test
*
@@ -212,7 +217,7 @@ protected function assertPostConditions(): void
* @throws Exception when no fixture manager is available.
* @see CakeTestCase::$autoFixtures
*/
- public function loadFixtures(...$classes): void
+ public function loadFixtures(string ...$classes): void
{
if (empty($this->fixtureManager)) {
throw new Exception(__d('cake_dev', 'No fixture manager to load the test fixture'));
@@ -231,8 +236,11 @@ public function loadFixtures(...$classes): void
* @param string $message The message to use for failure.
* @return void
*/
- public function assertTextNotEquals($expected, $result, $message = '')
- {
+ public function assertTextNotEquals(
+ string $expected,
+ string $result,
+ string $message = '',
+ ): void {
$expected = str_replace(["\r\n", "\r"], "\n", $expected);
$result = str_replace(["\r\n", "\r"], "\n", $result);
@@ -248,8 +256,11 @@ public function assertTextNotEquals($expected, $result, $message = '')
* @param string $message message The message to use for failure.
* @return void
*/
- public function assertTextEquals($expected, $result, $message = '')
- {
+ public function assertTextEquals(
+ string $expected,
+ string $result,
+ string $message = '',
+ ): void {
$expected = str_replace(["\r\n", "\r"], "\n", $expected);
$result = str_replace(["\r\n", "\r"], "\n", $result);
@@ -265,8 +276,11 @@ public function assertTextEquals($expected, $result, $message = '')
* @param string $message The message to use for failure.
* @return void
*/
- public function assertTextStartsWith($prefix, $string, $message = '')
- {
+ public function assertTextStartsWith(
+ string $prefix,
+ string $string,
+ string $message = '',
+ ): void {
$prefix = str_replace(["\r\n", "\r"], "\n", $prefix);
$string = str_replace(["\r\n", "\r"], "\n", $string);
@@ -282,8 +296,11 @@ public function assertTextStartsWith($prefix, $string, $message = '')
* @param string $message The message to use for failure.
* @return void
*/
- public function assertTextStartsNotWith($prefix, $string, $message = '')
- {
+ public function assertTextStartsNotWith(
+ string $prefix,
+ string $string,
+ string $message = '',
+ ): void {
$prefix = str_replace(["\r\n", "\r"], "\n", $prefix);
$string = str_replace(["\r\n", "\r"], "\n", $string);
@@ -299,8 +316,11 @@ public function assertTextStartsNotWith($prefix, $string, $message = '')
* @param string $message The message to use for failure.
* @return void
*/
- public function assertTextEndsWith($suffix, $string, $message = '')
- {
+ public function assertTextEndsWith(
+ string $suffix,
+ string $string,
+ string $message = '',
+ ): void {
$suffix = str_replace(["\r\n", "\r"], "\n", $suffix);
$string = str_replace(["\r\n", "\r"], "\n", $string);
@@ -316,8 +336,11 @@ public function assertTextEndsWith($suffix, $string, $message = '')
* @param string $message The message to use for failure.
* @return void
*/
- public function assertTextEndsNotWith($suffix, $string, $message = '')
- {
+ public function assertTextEndsNotWith(
+ string $suffix,
+ string $string,
+ string $message = '',
+ ): void {
$suffix = str_replace(["\r\n", "\r"], "\n", $suffix);
$string = str_replace(["\r\n", "\r"], "\n", $string);
@@ -334,8 +357,12 @@ public function assertTextEndsNotWith($suffix, $string, $message = '')
* @param bool $ignoreCase Whether or not the search should be case-sensitive.
* @return void
*/
- public function assertTextContains($needle, $haystack, $message = '', $ignoreCase = false)
- {
+ public function assertTextContains(
+ string $needle,
+ string $haystack,
+ string $message = '',
+ bool $ignoreCase = false,
+ ): void {
$needle = str_replace(["\r\n", "\r"], "\n", $needle);
$haystack = str_replace(["\r\n", "\r"], "\n", $haystack);
if ($ignoreCase) {
@@ -357,8 +384,12 @@ public function assertTextContains($needle, $haystack, $message = '', $ignoreCas
* @param bool $ignoreCase Whether or not the search should be case-sensitive.
* @return void
*/
- public function assertTextNotContains($needle, $haystack, $message = '', $ignoreCase = false)
- {
+ public function assertTextNotContains(
+ string $needle,
+ string $haystack,
+ string $message = '',
+ bool $ignoreCase = false,
+ ): void {
$needle = str_replace(["\r\n", "\r"], "\n", $needle);
$haystack = str_replace(["\r\n", "\r"], "\n", $haystack);
if ($ignoreCase) {
@@ -408,12 +439,15 @@ public function assertTextNotContains($needle, $haystack, $message = '', $ignore
* permutation of attribute order. It will also allow whitespace between specified tags.
*
* @param string $string An HTML/XHTML/XML string
- * @param array $expected An array, see above
- * @param string $fullDebug Whether or not more verbose output should be used.
- * @return bool
+ * @param array|string $expected An array, see above
+ * @param bool $fullDebug Whether or not more verbose output should be used.
+ * @return void
*/
- public function assertTags($string, $expected, $fullDebug = false)
- {
+ public function assertTags(
+ string $string,
+ array|string $expected,
+ bool $fullDebug = false,
+ ): void {
$regex = [];
$normalized = [];
foreach ((array)$expected as $key => $val) {
@@ -530,19 +564,16 @@ public function assertTags($string, $expected, $fullDebug = false)
}
}
if (!$matches) {
- $this->assertTrue(false, sprintf('Item #%d / regex #%d failed: %s', $itemNum, $i, $description));
if ($fullDebug) {
debug($string, true);
debug($regex, true);
}
- return false;
+ $this->fail(sprintf('Item #%d / regex #%d failed: %s', $itemNum, $i, $description));
}
}
- $this->assertTrue(true, '%s');
-
- return true;
+ $this->addToAssertionCount(1);
}
/**
@@ -552,13 +583,14 @@ public function assertTags($string, $expected, $fullDebug = false)
* @param string $string The HTML string to check.
* @return string
*/
- protected function _assertAttributes($assertions, $string)
+ protected function _assertAttributes(array $assertions, string $string): string
{
$asserts = $assertions['attrs'];
$explains = $assertions['explains'];
$len = count($asserts);
do {
$matches = false;
+ $j = null;
foreach ($asserts as $j => $assert) {
if (preg_match(sprintf('/^%s/s', $assert), $string, $match)) {
$matches = true;
@@ -569,7 +601,7 @@ protected function _assertAttributes($assertions, $string)
}
}
if ($matches === false) {
- $this->assertTrue(false, 'Attribute did not match. Was expecting ' . $explains[$j]);
+ $this->fail('Attribute did not match. Was expecting ' . ($explains[$j] ?? ''));
}
$len = count($asserts);
} while ($len > 0);
@@ -577,8 +609,6 @@ protected function _assertAttributes($assertions, $string)
return $string;
}
-// @codingStandardsIgnoreStart
-
/**
* Compatibility wrapper function for assertEquals
*
@@ -588,8 +618,11 @@ protected function _assertAttributes($assertions, $string)
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
- protected static function assertEqual($result, $expected, $message = '')
- {
+ protected static function assertEqual(
+ mixed $result,
+ mixed $expected,
+ string $message = '',
+ ): void {
static::assertEquals($expected, $result, $message);
}
@@ -602,8 +635,11 @@ protected static function assertEqual($result, $expected, $message = '')
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
- protected static function assertNotEqual($result, $expected, $message = '')
- {
+ protected static function assertNotEqual(
+ mixed $result,
+ mixed $expected,
+ string $message = '',
+ ): void {
static::assertNotEquals($expected, $result, $message);
}
@@ -616,8 +652,11 @@ protected static function assertNotEqual($result, $expected, $message = '')
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
- protected static function assertPattern($pattern, $string, $message = '')
- {
+ protected static function assertPattern(
+ mixed $pattern,
+ string $string,
+ string $message = '',
+ ): void {
static::assertMatchesRegularExpression($pattern, $string, $message);
}
@@ -630,8 +669,11 @@ protected static function assertPattern($pattern, $string, $message = '')
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
- protected static function assertIdentical($actual, $expected, $message = '')
- {
+ protected static function assertIdentical(
+ mixed $actual,
+ mixed $expected,
+ string $message = '',
+ ): void {
static::assertSame($expected, $actual, $message);
}
@@ -644,8 +686,11 @@ protected static function assertIdentical($actual, $expected, $message = '')
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
- protected static function assertNotIdentical($actual, $expected, $message = '')
- {
+ protected static function assertNotIdentical(
+ mixed $actual,
+ mixed $expected,
+ string $message = '',
+ ): void {
static::assertNotSame($expected, $actual, $message);
}
@@ -658,8 +703,11 @@ protected static function assertNotIdentical($actual, $expected, $message = '')
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
- protected static function assertNoPattern($pattern, $string, $message = '')
- {
+ protected static function assertNoPattern(
+ mixed $pattern,
+ string $string,
+ string $message = '',
+ ): void {
static::assertDoesNotMatchRegularExpression($pattern, $string, $message);
}
@@ -669,7 +717,7 @@ protected static function assertNoPattern($pattern, $string, $message = '')
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
- protected function assertNoErrors()
+ protected function assertNoErrors(): void
{
}
@@ -682,8 +730,11 @@ protected function assertNoErrors()
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
- protected static function assertReference(&$first, &$second, $message = '')
- {
+ protected static function assertReference(
+ mixed $first,
+ mixed $second,
+ string $message = '',
+ ): void {
static::assertSame($first, $second, $message);
}
@@ -696,8 +747,11 @@ protected static function assertReference(&$first, &$second, $message = '')
* @deprecated 3.0.0 This is a compatibility wrapper for 1.x. It will be removed in 3.0
* @return void
*/
- protected static function assertIsA($object, $type, $message = '')
- {
+ protected static function assertIsA(
+ string $object,
+ string $type,
+ string $message = '',
+ ): void {
static::assertInstanceOf($type, $object, $message);
}
@@ -710,8 +764,12 @@ protected static function assertIsA($object, $type, $message = '')
* @param string $message the text to display if the assertion is not correct
* @return void
*/
- protected static function assertWithinMargin($result, $expected, $margin, $message = '')
- {
+ protected static function assertWithinMargin(
+ mixed $result,
+ mixed $expected,
+ mixed $margin,
+ string $message = '',
+ ): void {
$upper = $result + $margin;
$lower = $result - $margin;
@@ -725,8 +783,10 @@ protected static function assertWithinMargin($result, $expected, $margin, $messa
* @param string $message Message for skip
* @return bool
*/
- protected function skipUnless($condition, $message = '')
- {
+ protected function skipUnless(
+ bool $condition,
+ string $message = '',
+ ): bool {
if (!$condition) {
$this->markTestSkipped($message);
}
@@ -734,8 +794,6 @@ protected function skipUnless($condition, $message = '')
return $condition;
}
- // @codingStandardsIgnoreEnd
-
/**
* Returns a mock object for the specified class.
*
@@ -764,14 +822,14 @@ protected function skipUnless($condition, $message = '')
* @see https://phpunit.de/manual/current/en/test-doubles.html
*/
protected function _buildMock(
- $originalClassName,
- $methods = [],
+ string $originalClassName,
+ array $methods = [],
array $arguments = [],
- $mockClassName = '',
- $callOriginalConstructor = true,
- $callOriginalClone = true,
- $callAutoload = true,
- ) {
+ string $mockClassName = '',
+ bool $callOriginalConstructor = true,
+ bool $callOriginalClone = true,
+ bool $callAutoload = true,
+ ): object {
$mockBuilder = $this->getMockBuilder($originalClassName);
if (!empty($methods)) {
$mockBuilder = $mockBuilder->setMethods($methods);
@@ -820,24 +878,24 @@ protected function _buildMock(
* disable __autoload() during the generation of the test double class.
* @param bool $cloneArguments Not supported.
* @param bool $callOriginalMethods Not supported.
- * @param string $proxyTarget Not supported.
+ * @param string|null $proxyTarget Not supported.
* @return T&MockObject
* @throws InvalidArgumentException When not supported parameters are set.
* @deprecated Use `getMockBuilder()` or `createMock()` in new unit tests.
* @see https://phpunit.de/manual/current/en/test-doubles.html
*/
public function getMock(
- $originalClassName,
- $methods = [],
+ string $originalClassName,
+ array $methods = [],
array $arguments = [],
- $mockClassName = '',
- $callOriginalConstructor = true,
- $callOriginalClone = true,
- $callAutoload = true,
- $cloneArguments = false,
- $callOriginalMethods = false,
- $proxyTarget = null,
- ) {
+ string $mockClassName = '',
+ bool $callOriginalConstructor = true,
+ bool $callOriginalClone = true,
+ bool $callAutoload = true,
+ bool $cloneArguments = false,
+ bool $callOriginalMethods = false,
+ ?string $proxyTarget = null,
+ ): object {
if ($cloneArguments) {
throw new InvalidArgumentException('$cloneArguments parameter is not supported');
}
@@ -869,8 +927,11 @@ public function getMock(
* @throws MissingModelException
* @return T|MockObject
*/
- public function getMockForModel(string $model, $methods = [], array $config = []): Model|MockObject
- {
+ public function getMockForModel(
+ string $model,
+ mixed $methods = [],
+ array $config = [],
+ ): Model|MockObject {
$defaults = ClassRegistry::config('Model');
unset($defaults['ds']);
@@ -886,8 +947,9 @@ public function getMockForModel(string $model, $methods = [], array $config = []
throw new MissingModelException([$model]);
}
- $config = array_merge($defaults, (array)$config, ['name' => $name]);
+ $config = array_merge($defaults, $config, ['name' => $name]);
+ /** @var Model&MockObject $mock */
$mock = $this->getMock($className, $methods, [$config]);
$availableDs = array_keys(ConnectionManager::enumConnectionObjects());
diff --git a/src/TestSuite/CakeTestSuite.php b/src/TestSuite/CakeTestSuite.php
index 202fcb8860..ea5e5a5e2c 100644
--- a/src/TestSuite/CakeTestSuite.php
+++ b/src/TestSuite/CakeTestSuite.php
@@ -35,7 +35,7 @@ class CakeTestSuite extends TestSuite
* @param string $directory The directory to add tests from.
* @return void
*/
- public function addTestDirectory($directory = '.')
+ public function addTestDirectory(string $directory = '.'): void
{
$Folder = new Folder($directory);
[, $files] = $Folder->read(true, true, true);
diff --git a/src/TestSuite/ControllerTestCase.php b/src/TestSuite/ControllerTestCase.php
index 241ccb76cd..993723af9a 100644
--- a/src/TestSuite/ControllerTestCase.php
+++ b/src/TestSuite/ControllerTestCase.php
@@ -28,96 +28,12 @@
use Cake\Event\CakeEvent;
use Cake\Network\CakeRequest;
use Cake\Network\CakeResponse;
-use Cake\Routing\Dispatcher;
use Cake\Routing\Route\RedirectRoute;
use Cake\Routing\Router;
use Cake\Utility\ClassRegistry;
use Cake\Utility\Inflector;
-use Cake\View\Helper;
use PHPUnit\Framework\MockObject\MockObject;
-/**
- * ControllerTestDispatcher class
- *
- * @package Cake.TestSuite
- */
-class ControllerTestDispatcher extends Dispatcher
-{
- /**
- * The controller to use in the dispatch process
- *
- * @var Controller
- */
- public $testController = null;
-
- /**
- * Use custom routes during tests
- *
- * @var bool
- */
- public $loadRoutes = true;
-
- /**
- * Returns the test controller
- *
- * @param CakeRequest $request The request instance.
- * @param CakeResponse $response The response instance.
- * @return Controller
- */
- protected function _getController($request, $response)
- {
- if ($this->testController === null) {
- $this->testController = parent::_getController($request, $response);
- }
- $this->testController->helpers = array_merge(['InterceptContent'], $this->testController->helpers);
- $this->testController->setRequest($request);
- $this->testController->response = $response;
- foreach ($this->testController->Components->loaded() as $component) {
- $object = $this->testController->Components->{$component};
- if (isset($object->response)) {
- $object->response = $response;
- }
- if (isset($object->request)) {
- $object->request = $request;
- }
- }
-
- return $this->testController;
- }
-
- /**
- * Loads routes and resets if the test case dictates it should
- *
- * @return void
- */
- protected function _loadRoutes()
- {
- if (!$this->loadRoutes) {
- Router::reload();
- }
- }
-}
-
-/**
- * InterceptContentHelper class
- *
- * @package Cake.TestSuite
- */
-class InterceptContentHelper extends Helper
-{
- /**
- * Intercepts and stores the contents of the view before the layout is rendered
- *
- * @param string $viewFile The view file
- * @return void
- */
- public function afterRender($viewFile): void
- {
- $this->_View->assign('__view_no_layout__', $this->_View->fetch('content'));
- $this->_View->Helpers->unload('InterceptContent');
- }
-}
-
/**
* ControllerTestCase class
*
@@ -196,7 +112,7 @@ abstract class ControllerTestCase extends CakeTestCase
*
* @var string
*/
- protected string $_responseClass = 'CakeResponse';
+ protected string $_responseClass = CakeResponse::class;
/**
* Used to enable calling ControllerTestCase::testAction() without the testing
@@ -308,7 +224,9 @@ protected function _testAction($url, $options = [])
$params['requested'] = 1;
}
$dispatcher->testController = $this->controller;
- $dispatcher->response = $this->getMock($this->_responseClass, ['send', '_clearBuffer']);
+ /** @var CakeResponse $response */
+ $response = $this->getMock($this->_responseClass, ['send', '_clearBuffer']);
+ $dispatcher->response = $response;
$this->result = $dispatcher->dispatch($request, $dispatcher->response, $params);
// Clear out any stored requests.
@@ -334,7 +252,7 @@ protected function _testAction($url, $options = [])
/**
* Creates the test dispatcher class
*
- * @return Dispatcher
+ * @return ControllerTestDispatcher
*/
protected function _createDispatcher()
{
@@ -362,7 +280,7 @@ protected function _createDispatcher()
* @throws MissingControllerException When controllers could not be created.
* @throws MissingComponentException When components could not be created.
*/
- public function generate($controller, $mocks = [])
+ public function generate(string $controller, array $mocks = [])
{
[$plugin, $controller] = pluginSplit($controller);
if ($plugin) {
diff --git a/src/TestSuite/ControllerTestDispatcher.php b/src/TestSuite/ControllerTestDispatcher.php
new file mode 100644
index 0000000000..2b552a0ddb
--- /dev/null
+++ b/src/TestSuite/ControllerTestDispatcher.php
@@ -0,0 +1,90 @@
+
+ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice
+ *
+ * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @link https://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
+ * @package Cake.TestSuite
+ * @since CakePHP(tm) v 2.0
+ * @license https://opensource.org/licenses/mit-license.php MIT License
+ */
+
+namespace Cake\TestSuite;
+
+use Cake\Controller\Controller;
+use Cake\Network\CakeRequest;
+use Cake\Network\CakeResponse;
+use Cake\Routing\Dispatcher;
+use Cake\Routing\Router;
+
+/**
+ * ControllerTestDispatcher class
+ *
+ * @package Cake.TestSuite
+ */
+class ControllerTestDispatcher extends Dispatcher
+{
+ /**
+ * The controller to use in the dispatch process
+ *
+ * @var Controller|null
+ */
+ public ?Controller $testController = null;
+
+ public ?CakeResponse $response = null;
+
+ /**
+ * Use custom routes during tests
+ *
+ * @var bool
+ */
+ public $loadRoutes = true;
+
+ /**
+ * Returns the test controller
+ *
+ * @param CakeRequest $request The request instance.
+ * @param CakeResponse $response The response instance.
+ * @return Controller|null
+ */
+ protected function _getController(CakeRequest $request, CakeResponse $response): ?Controller
+ {
+ if ($this->testController === null) {
+ $this->testController = parent::_getController($request, $response);
+ }
+ $this->testController->helpers = array_merge(['InterceptContent'], $this->testController->helpers);
+ $this->testController->setRequest($request);
+ $this->testController->response = $response;
+ foreach ($this->testController->Components->loaded() as $component) {
+ $object = $this->testController->Components->{$component};
+ if (isset($object->response)) {
+ $object->response = $response;
+ }
+ if (isset($object->request)) {
+ $object->request = $request;
+ }
+ }
+
+ return $this->testController;
+ }
+
+ /**
+ * Loads routes and resets if the test case dictates it should
+ *
+ * @return void
+ */
+ protected function _loadRoutes(): void
+ {
+ if (!$this->loadRoutes) {
+ Router::reload();
+ }
+ }
+}
diff --git a/src/TestSuite/Coverage/BaseCoverageReport.php b/src/TestSuite/Coverage/BaseCoverageReport.php
index 6ce8bfc9f8..e59e2e9d87 100644
--- a/src/TestSuite/Coverage/BaseCoverageReport.php
+++ b/src/TestSuite/Coverage/BaseCoverageReport.php
@@ -24,6 +24,7 @@
use Cake\Core\CakePlugin;
use Cake\TestSuite\Reporter\CakeBaseReporter;
use Cake\Utility\Inflector;
+use SebastianBergmann\CodeCoverage\ProcessedCodeCoverageData;
/**
* Abstract class for common CoverageReport methods.
@@ -36,23 +37,23 @@ abstract class BaseCoverageReport
/**
* coverage data
*
- * @var string
+ * @var ProcessedCodeCoverageData
*/
- protected $_rawCoverage;
+ protected ProcessedCodeCoverageData $_rawCoverage;
/**
* is the test an app test
*
- * @var string
+ * @var bool
*/
- public $appTest = false;
+ public bool $appTest = false;
/**
* is the test a plugin test
*
- * @var string
+ * @var string|null
*/
- public $pluginTest = false;
+ public ?string $pluginTest = null;
/**
* Array of test case file names. Used to do basename() matching with
@@ -60,15 +61,15 @@ abstract class BaseCoverageReport
*
* @var array
*/
- protected $_testNames = [];
+ protected array $_testNames = [];
/**
* Constructor
*
- * @param array $coverage Array of coverage data from PHPUnit_Test_Result
+ * @param ProcessedCodeCoverageData $coverage Array of coverage data from PHPUnit_Test_Result
* @param CakeBaseReporter $reporter A reporter to use for the coverage report.
*/
- public function __construct($coverage, CakeBaseReporter $reporter)
+ public function __construct(ProcessedCodeCoverageData $coverage, CakeBaseReporter $reporter)
{
$this->_rawCoverage = $coverage;
$this->_setParams($reporter);
@@ -93,10 +94,10 @@ protected function _setParams(CakeBaseReporter $reporter)
/**
* Set the coverage data array
*
- * @param array $coverage Coverage data to use.
+ * @param ProcessedCodeCoverageData $coverage Coverage data to use.
* @return void
*/
- public function setCoverage($coverage)
+ public function setCoverage(ProcessedCodeCoverageData $coverage)
{
$this->_rawCoverage = $coverage;
}
@@ -129,7 +130,7 @@ public function getPathFilter()
public function filterCoverageDataByPath(string $path): array
{
$files = [];
- foreach ($this->_rawCoverage as $fileName => $fileCoverage) {
+ foreach ($this->_rawCoverage->lineCoverage() as $fileName => $fileCoverage) {
if (!str_starts_with($fileName, $path)) {
continue;
}
diff --git a/src/TestSuite/Coverage/HtmlCoverageReport.php b/src/TestSuite/Coverage/HtmlCoverageReport.php
index 8fda67d65f..1c903289bb 100644
--- a/src/TestSuite/Coverage/HtmlCoverageReport.php
+++ b/src/TestSuite/Coverage/HtmlCoverageReport.php
@@ -56,13 +56,13 @@ public function report()
}
$output = $this->coverageScript();
$output .= <<Code coverage results
- Toggle all files
-
-HTML;
- foreach ($coverageData as $file => $coverageData) {
+
+ HTML;
}
/**
diff --git a/src/TestSuite/Fixture/CakeFixtureInjector.php b/src/TestSuite/Fixture/CakeFixtureInjector.php
index 6a043888a6..925d8f8594 100644
--- a/src/TestSuite/Fixture/CakeFixtureInjector.php
+++ b/src/TestSuite/Fixture/CakeFixtureInjector.php
@@ -10,15 +10,11 @@
use PHPUnit\Framework\Warning;
use Throwable;
-/**
- * @property CakeFixtureManager $_fixtureManager
- * @property TestSuite $_first
- */
class CakeFixtureInjector implements TestListener
{
- protected $_fixtureManager;
+ protected ?CakeFixtureManager $_fixtureManager = null;
- protected $_first;
+ protected ?TestSuite $_first = null;
public function __construct(?CakeFixtureManager $manager = null)
{
@@ -42,8 +38,8 @@ public function endTestSuite(TestSuite $suite): void
public function startTest(Test $test): void
{
- $test->fixtureManager = $this->_fixtureManager;
if ($test instanceof CakeTestCase) {
+ $test->fixtureManager = $this->_fixtureManager;
$this->_fixtureManager->fixturize($test);
$this->_fixtureManager->load($test);
}
@@ -56,7 +52,7 @@ public function endTest(Test $test, $time): void
}
}
- public function addError(Test $test, Throwable $e, $time): void
+ public function addError(Test $test, Throwable $t, $time): void
{
}
@@ -64,15 +60,15 @@ public function addFailure(Test $test, AssertionFailedError $e, $time): void
{
}
- public function addIncompleteTest(Test $test, Throwable $e, $time): void
+ public function addIncompleteTest(Test $test, Throwable $t, $time): void
{
}
- public function addSkippedTest(Test $test, Throwable $e, $time): void
+ public function addSkippedTest(Test $test, Throwable $t, $time): void
{
}
- public function addRiskyTest(Test $test, Throwable $e, $time): void
+ public function addRiskyTest(Test $test, Throwable $t, $time): void
{
}
diff --git a/src/TestSuite/Fixture/CakeFixtureManager.php b/src/TestSuite/Fixture/CakeFixtureManager.php
index 49d5919851..b3d7ded95e 100644
--- a/src/TestSuite/Fixture/CakeFixtureManager.php
+++ b/src/TestSuite/Fixture/CakeFixtureManager.php
@@ -20,7 +20,7 @@
use Cake\Core\CakePlugin;
use Cake\Model\ConnectionManager;
-use Cake\Model\Datasource\DataSource;
+use Cake\Model\Datasource\DboSource;
use Cake\TestSuite\CakeTestCase;
use Cake\Utility\ClassRegistry;
use Cake\Utility\Inflector;
@@ -38,19 +38,19 @@ class CakeFixtureManager
*
* @var bool
*/
- protected $_initialized = false;
+ protected bool $_initialized = false;
/**
* Default datasource to use
*
- * @var DataSource|null
+ * @var DboSource|null
*/
- protected ?DataSource $_db = null;
+ protected ?DboSource $_db = null;
/**
* Holds the fixture classes that where instantiated
*
- * @var array
+ * @var array
*/
protected array $_loaded = [];
@@ -62,7 +62,7 @@ class CakeFixtureManager
protected array $_fixtureMap = [];
/**
- * @var array
+ * @var array
*/
protected array $_processed = [];
@@ -72,7 +72,7 @@ class CakeFixtureManager
* @param CakeTestCase $test the test case to inspect
* @return void
*/
- public function fixturize($test)
+ public function fixturize(CakeTestCase $test): void
{
if (!$this->_initialized) {
ClassRegistry::config(['ds' => 'test', 'testing' => true]);
@@ -82,15 +82,10 @@ public function fixturize($test)
return;
}
+
$this->_initDb();
$test->db = $this->_db;
- if (!is_array($test->fixtures)) {
- $test->fixtures = array_map('trim', explode(',', $test->fixtures));
- }
- if (isset($test->fixtures)) {
- $this->_loadFixtures($test->fixtures);
- }
-
+ $this->_loadFixtures($test->fixtures);
$this->_processed[$test::class] = true;
}
@@ -99,12 +94,12 @@ public function fixturize($test)
*
* @return void
*/
- protected function _initDb()
+ protected function _initDb(): void
{
if ($this->_initialized) {
return;
}
- $db = ConnectionManager::getDataSource('test');
+ $db = ConnectionManager::getDboSource('test');
$db->cacheSources = false;
$this->_db = $db;
$this->_initialized = true;
@@ -115,9 +110,12 @@ protected function _initDb()
* real fixture path including sub-directories
*
* @param string $fixturePath the fixture path to parse
- * @return array containing fixture class name and optional additional path
+ * @return array{
+ * fixture: string,
+ * additionalPath: string
+ * } containing fixture class name and optional additional path
*/
- protected function _parseFixturePath($fixturePath)
+ protected function _parseFixturePath(string $fixturePath): array
{
$pathTokenArray = explode('/', $fixturePath);
$fixture = array_pop($pathTokenArray);
@@ -132,11 +130,11 @@ protected function _parseFixturePath($fixturePath)
/**
* Looks for fixture files and instantiates the classes accordingly
*
- * @param array $fixtures the fixture names to load using the notation {type}.{name}
+ * @param array $fixtures the fixture names to load using the notation {type}.{name}
* @return void
* @throws UnexpectedValueException when a referenced fixture does not exist.
*/
- protected function _loadFixtures($fixtures)
+ protected function _loadFixtures(array $fixtures): void
{
foreach ($fixtures as $fixture) {
$fixtureFile = null;
@@ -232,15 +230,18 @@ protected function _loadFixtures($fixtures)
* Runs the drop, create and truncate commands on the fixtures if necessary.
*
* @param CakeTestFixture $fixture the fixture object to create
- * @param DataSource $db the datasource instance to use
+ * @param DboSource|null $db the datasource instance to use
* @param bool $drop whether drop the fixture if it is already created or not
* @return void
*/
- protected function _setupTable($fixture, $db = null, $drop = true)
- {
+ protected function _setupTable(
+ CakeTestFixture $fixture,
+ ?DboSource $db = null,
+ bool $drop = true,
+ ): void {
if (!$db) {
if (!empty($fixture->useDbConfig)) {
- $db = ConnectionManager::getDataSource($fixture->useDbConfig);
+ $db = ConnectionManager::getDboSource($fixture->useDbConfig);
} else {
$db = $this->_db;
}
@@ -251,9 +252,9 @@ protected function _setupTable($fixture, $db = null, $drop = true)
return;
}
- $sources = (array)$db->listSources();
+ $sources = $db->listSources();
$table = $db->config['prefix'] . $fixture->table;
- $exists = in_array($table, $sources);
+ $exists = in_array($table, (array)$sources);
if ($drop && $exists) {
$fixture->drop($db);
@@ -272,20 +273,17 @@ protected function _setupTable($fixture, $db = null, $drop = true)
* @param CakeTestCase $test the test to inspect for fixture loading
* @return void
*/
- public function load(CakeTestCase $test)
+ public function load(CakeTestCase $test): void
{
- if (empty($test->fixtures)) {
+ if (empty($test->fixtures) || !$test->autoFixtures) {
return;
}
$fixtures = $test->fixtures;
- if (empty($fixtures) || !$test->autoFixtures) {
- return;
- }
foreach ($fixtures as $f) {
if (!empty($this->_loaded[$f])) {
$fixture = $this->_loaded[$f];
- $db = ConnectionManager::getDataSource($fixture->useDbConfig);
+ $db = ConnectionManager::getDboSource($fixture->useDbConfig);
$this->_setupTable($fixture, $db, $test->dropTables);
$db->begin();
$fixture->insert($db);
@@ -300,7 +298,7 @@ public function load(CakeTestCase $test)
* @param CakeTestCase $test the test to inspect for fixture unloading
* @return void
*/
- public function unload(CakeTestCase $test)
+ public function unload(CakeTestCase $test): void
{
$fixtures = !empty($test->fixtures) ? $test->fixtures : [];
foreach (array_reverse($fixtures) as $f) {
@@ -308,7 +306,7 @@ public function unload(CakeTestCase $test)
$fixture = $this->_loaded[$f];
if (!empty($fixture->created)) {
foreach ($fixture->created as $ds) {
- $db = ConnectionManager::getDataSource($ds);
+ $db = ConnectionManager::getDboSource($ds);
$fixture->truncate($db);
}
}
@@ -320,18 +318,21 @@ public function unload(CakeTestCase $test)
* Creates a single fixture table and loads data into it.
*
* @param string $name of the fixture
- * @param DataSource $db DataSource instance or leave null to get DataSource from the fixture
+ * @param DboSource|null $db DataSource instance or leave null to get DboSource from the fixture
* @param bool $dropTables Whether or not tables should be dropped and re-created.
* @return void
* @throws UnexpectedValueException if $name is not a previously loaded class
*/
- public function loadSingle($name, $db = null, $dropTables = true)
- {
+ public function loadSingle(
+ string $name,
+ ?DboSource $db = null,
+ bool $dropTables = true,
+ ): void {
$name .= 'Fixture';
if (isset($this->_fixtureMap[$name])) {
$fixture = $this->_fixtureMap[$name];
if (!$db) {
- $db = ConnectionManager::getDataSource($fixture->useDbConfig);
+ $db = ConnectionManager::getDboSource($fixture->useDbConfig);
}
$this->_setupTable($fixture, $db, $dropTables);
$fixture->insert($db);
@@ -348,7 +349,7 @@ public function loadSingle($name, $db = null, $dropTables = true)
*
* @return void
*/
- public function shutDown()
+ public function shutDown(): void
{
if (session_id()) {
session_write_close();
@@ -356,7 +357,7 @@ public function shutDown()
foreach ($this->_loaded as $fixture) {
if (!empty($fixture->created)) {
foreach ($fixture->created as $ds) {
- $db = ConnectionManager::getDataSource($ds);
+ $db = ConnectionManager::getDboSource($ds);
$fixture->drop($db);
}
}
diff --git a/src/TestSuite/Fixture/CakeTestFixture.php b/src/TestSuite/Fixture/CakeTestFixture.php
index 128add09df..32217287f0 100644
--- a/src/TestSuite/Fixture/CakeTestFixture.php
+++ b/src/TestSuite/Fixture/CakeTestFixture.php
@@ -29,6 +29,7 @@
use Cake\Utility\Hash;
use Cake\Utility\Inflector;
use Exception;
+use PDOStatement;
/**
* CakeTestFixture is responsible for building and destroying tables to be used
@@ -281,7 +282,7 @@ public function create(DboSource $db): bool
* @param DboSource $db An instance of the database object used to create the fixture table
* @return bool True on success, false on failure
*/
- public function drop($db)
+ public function drop(DboSource $db): bool
{
if (empty($this->fields)) {
return false;
@@ -357,10 +358,11 @@ public function insert($db): bool
* CakeFixture to trigger other events before / after truncate.
*
* @param DboSource $db A reference to a db instance
- * @return bool
+ * @return PDOStatement|bool|null
*/
- public function truncate($db)
- {
+ public function truncate(
+ DboSource $db,
+ ): PDOStatement|bool|null {
$fullDebug = $db->fullDebug;
$db->fullDebug = false;
$return = $db->truncate($this->table);
diff --git a/src/TestSuite/Fixture/CakeTestModel.php b/src/TestSuite/Fixture/CakeTestModel.php
index 6226f9efc8..afd2853ec7 100644
--- a/src/TestSuite/Fixture/CakeTestModel.php
+++ b/src/TestSuite/Fixture/CakeTestModel.php
@@ -34,12 +34,15 @@ class CakeTestModel extends Model
* incorrect order when no order has been defined in the finds.
* Postgres can return the results in any order it considers appropriate if none is specified
*
- * @param array|string|int $id Set this ID for this model on startup, can also be an array of options, see above.
- * @param string $table Name of database table to use.
- * @param string $ds DataSource connection name.
+ * @param array|string|int|false|null $id Set this ID for this model on startup, can also be an array of options, see above.
+ * @param string|false|null $table Name of database table to use.
+ * @param string|null $ds DataSource connection name.
*/
- public function __construct($id = false, $table = null, $ds = null)
- {
+ public function __construct(
+ array|string|int|false|null $id = false,
+ string|false|null $table = null,
+ string|null $ds = null,
+ ) {
parent::__construct($id, $table, $ds);
$this->order = [$this->alias . '.' . $this->primaryKey => 'ASC'];
}
@@ -47,13 +50,16 @@ public function __construct($id = false, $table = null, $ds = null)
/**
* Overriding save() to set CakeTestSuiteDispatcher::date() as formatter for created, modified and updated fields
*
- * @param array $data Data to save
+ * @param array|null $data Data to save
* @param array|bool $validate Validate or options.
* @param array $fieldList Whitelist of fields
* @return mixed
*/
- public function save($data = null, $validate = true, $fieldList = [])
- {
+ public function save(
+ array|null $data = null,
+ array|bool $validate = true,
+ array $fieldList = [],
+ ): mixed {
$db = $this->getDataSource();
$db->columns['datetime']['formatter'] = 'CakeTestSuiteDispatcher::date';
diff --git a/src/TestSuite/InterceptContentHelper.php b/src/TestSuite/InterceptContentHelper.php
new file mode 100644
index 0000000000..b1e348ad6b
--- /dev/null
+++ b/src/TestSuite/InterceptContentHelper.php
@@ -0,0 +1,42 @@
+
+ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ *
+ * Licensed under The MIT License
+ * For full copyright and license information, please see the LICENSE.txt
+ * Redistributions of files must retain the above copyright notice
+ *
+ * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
+ * @link https://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
+ * @package Cake.TestSuite
+ * @since CakePHP(tm) v 2.0
+ * @license https://opensource.org/licenses/mit-license.php MIT License
+ */
+
+namespace Cake\TestSuite;
+
+use Cake\View\Helper;
+
+/**
+ * InterceptContentHelper class
+ *
+ * @package Cake.TestSuite
+ */
+class InterceptContentHelper extends Helper
+{
+ /**
+ * Intercepts and stores the contents of the view before the layout is rendered
+ *
+ * @param string $viewFile The view file
+ * @return void
+ */
+ public function afterRender(string $viewFile): void
+ {
+ $this->_View->assign('__view_no_layout__', $this->_View->fetch('content'));
+ $this->_View->Helpers->unload('InterceptContent');
+ }
+}
diff --git a/src/TestSuite/Reporter/CakeBaseReporter.php b/src/TestSuite/Reporter/CakeBaseReporter.php
index 93dfade762..c84954a398 100644
--- a/src/TestSuite/Reporter/CakeBaseReporter.php
+++ b/src/TestSuite/Reporter/CakeBaseReporter.php
@@ -17,8 +17,6 @@
namespace Cake\TestSuite\Reporter;
-use Cake\TestSuite\CakeTestLoader;
-use Exception;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestResult;
@@ -39,21 +37,21 @@ abstract class CakeBaseReporter implements ResultPrinter
*
* @var bool
*/
- protected $_headerSent = false;
+ protected bool $_headerSent = false;
/**
* Array of request parameters. Usually parsed GET params.
*
* @var array
*/
- public $params = [];
+ public array $params = [];
/**
* Character set for the output of test reporting.
*
* @var string
*/
- protected $_characterSet;
+ protected string $_characterSet;
/**
* @var int
@@ -75,7 +73,7 @@ abstract class CakeBaseReporter implements ResultPrinter
* @param string $charset The character set to output with. Defaults to UTF-8
* @param array $params Array of request parameters the reporter should use. See above.
*/
- public function __construct($charset = 'utf-8', $params = [])
+ public function __construct(string $charset = 'utf-8', array $params = [])
{
if (!$charset) {
$charset = 'utf-8';
@@ -88,14 +86,9 @@ public function __construct($charset = 'utf-8', $params = [])
* Retrieves a list of test cases from the active Manager class,
* displaying it in the correct format for the reporter subclass
*
- * @return mixed
+ * @return void
*/
- public function testCaseList()
- {
- $testList = CakeTestLoader::generateTestList($this->params);
-
- return $testList;
- }
+ abstract public function testCaseList(): void;
/**
* Paints the start of the response from the test suite.
@@ -103,9 +96,7 @@ public function testCaseList()
*
* @return void
*/
- public function paintDocumentStart()
- {
- }
+ abstract public function paintDocumentStart(): void;
/**
* Paints the end of the response from the test suite.
@@ -113,9 +104,7 @@ public function paintDocumentStart()
*
* @return void
*/
- public function paintDocumentEnd()
- {
- }
+ abstract public function paintDocumentEnd(): void;
/**
* Paint a list of test sets, core, app, and plugin test sets
@@ -123,7 +112,7 @@ public function paintDocumentEnd()
*
* @return void
*/
- public function paintTestMenu()
+ public function paintTestMenu(): void
{
}
@@ -132,7 +121,7 @@ public function paintTestMenu()
*
* @return string The base URL for the request.
*/
- public function baseUrl()
+ public function baseUrl(): string
{
if (!empty($_SERVER['PHP_SELF'])) {
return $_SERVER['PHP_SELF'];
@@ -158,7 +147,7 @@ public function printResult(TestResult $result): void
* @param TestResult $result The result object
* @return void
*/
- public function paintResult(TestResult $result)
+ public function paintResult(TestResult $result): void
{
$this->paintFooter($result);
}
@@ -166,14 +155,14 @@ public function paintResult(TestResult $result)
/**
* An error occurred.
*
- * @param \PHPUnit\Framework\Test $test The test to add an error for.
- * @param Exception|Throwable $e The exception object to add.
+ * @param Test $test The test to add an error for.
+ * @param Throwable $t The exception object to add.
* @param float $time The current time.
* @return void
*/
- public function addError(Test $test, Exception|Throwable $e, $time): void
+ public function addError(Test $test, Throwable $t, float $time): void
{
- $this->paintException($e, $test);
+ $this->paintException($t, $test);
}
/**
@@ -184,7 +173,7 @@ public function addError(Test $test, Exception|Throwable $e, $time): void
* @param float $time The current time.
* @return void
*/
- public function addFailure(Test $test, AssertionFailedError $e, $time): void
+ public function addFailure(Test $test, AssertionFailedError $e, float $time): void
{
$this->paintFail($e, $test);
}
@@ -193,26 +182,26 @@ public function addFailure(Test $test, AssertionFailedError $e, $time): void
* Incomplete test.
*
* @param Test $test The test that was incomplete.
- * @param Exception|Throwable $e The incomplete exception
+ * @param Throwable $t The incomplete exception
* @param float $time The current time.
* @return void
*/
- public function addIncompleteTest(Test $test, Exception|Throwable $e, $time): void
+ public function addIncompleteTest(Test $test, Throwable $t, float $time): void
{
- $this->paintSkip($e, $test);
+ $this->paintSkip($t, $test);
}
/**
* Skipped test.
*
* @param Test $test The test that failed.
- * @param Exception|Throwable $e The skip object.
+ * @param Throwable $t The skip object.
* @param float $time The current time.
* @return void
*/
- public function addSkippedTest(Test $test, Exception|Throwable $e, $time): void
+ public function addSkippedTest(Test $test, Throwable $t, $time): void
{
- $this->paintSkip($e, $test);
+ $this->paintSkip($t, $test);
}
/**
@@ -243,7 +232,7 @@ public function endTestSuite(TestSuite $suite): void
/**
* A test started.
*
- * @param \PHPUnit\Framework\Test $test The test that started.
+ * @param Test $test The test that started.
* @return void
*/
public function startTest(Test $test): void
@@ -253,14 +242,16 @@ public function startTest(Test $test): void
/**
* A test ended.
*
- * @param \PHPUnit\Framework\Test $test The test that ended
+ * @param Test $test The test that ended
* @param float $time The current time.
* @return void
*/
public function endTest(Test $test, $time): void
{
- $this->numAssertions += $test->getNumAssertions();
- if ($test->hasFailed()) {
+ if (method_exists($test, 'getNumAssertions')) {
+ $this->numAssertions += $test->getNumAssertions();
+ }
+ if (!method_exists($test, 'hasFailed') || $test->hasFailed()) {
return;
}
$this->paintPass($test, $time);
@@ -281,8 +272,11 @@ public function write(string $buffer): void
* @param float $time
* @return void
*/
- public function addWarning(Test $test, Warning $e, float $time): void
- {
+ public function addWarning(
+ Test $test,
+ Warning $e,
+ float $time,
+ ): void {
$this->paintFail($e, $test);
}
@@ -292,8 +286,11 @@ public function addWarning(Test $test, Warning $e, float $time): void
* @param float $time
* @return void
*/
- public function addRiskyTest(Test $test, Throwable $t, float $time): void
- {
+ public function addRiskyTest(
+ Test $test,
+ Throwable $t,
+ float $time,
+ ): void {
}
/**
@@ -305,33 +302,47 @@ abstract public function paintHeader(): void;
* @param TestResult $result
* @return void
*/
- abstract public function paintFooter(TestResult $result): void;
+ abstract public function paintFooter(
+ TestResult $result,
+ ): void;
/**
* @param Test $test
* @param float|null $time
* @return void
*/
- abstract public function paintPass(Test $test, $time = null): void;
+ abstract public function paintPass(
+ Test $test,
+ ?float $time = null,
+ ): void;
/**
- * @param Exception|Throwable $message
+ * @param Throwable $message
* @param Test $test
* @return void
*/
- abstract public function paintSkip(Exception|Throwable $message, Test $test): void;
+ abstract public function paintSkip(
+ Throwable $message,
+ Test $test,
+ ): void;
/**
- * @param Exception $exception
+ * @param Throwable $exception
* @param Test $test
* @return void
*/
- abstract public function paintException(Exception $exception, Test $test): void;
+ abstract public function paintException(
+ Throwable $exception,
+ Test $test,
+ ): void;
/**
- * @param mixed $message
+ * @param AssertionFailedError|Warning $message
* @param Test $test
* @return void
*/
- abstract public function paintFail($message, Test $test): void;
+ abstract public function paintFail(
+ AssertionFailedError|Warning $message,
+ Test $test,
+ ): void;
}
diff --git a/src/TestSuite/Reporter/CakeHtmlReporter.php b/src/TestSuite/Reporter/CakeHtmlReporter.php
index 2705e1f816..93dd94cdf9 100644
--- a/src/TestSuite/Reporter/CakeHtmlReporter.php
+++ b/src/TestSuite/Reporter/CakeHtmlReporter.php
@@ -4,13 +4,16 @@
use Cake\Core\App;
use Cake\Core\Configure;
+use Cake\TestSuite\CakeTestLoader;
use Cake\TestSuite\Coverage\HtmlCoverageReport;
use Cake\Utility\Inflector;
-use Exception;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestResult;
use PHPUnit\Framework\TestSuite;
+use PHPUnit\Framework\Warning;
+use SebastianBergmann\CodeCoverage\ProcessedCodeCoverageData;
+use SebastianBergmann\Comparator\ComparisonFailure;
use SebastianBergmann\Diff\Differ;
use Throwable;
@@ -43,7 +46,7 @@ class CakeHtmlReporter extends CakeBaseReporter
*
* @var string
*/
- protected $_buffer = '';
+ protected string $_buffer = '';
/**
* Paints the top of the web page setting the
@@ -80,7 +83,7 @@ public function sendContentType()
*
* @return void
*/
- public function paintDocumentStart()
+ public function paintDocumentStart(): void
{
$baseDir = $this->params['baseDir'];
include CAKE . 'TestSuite' . DS . 'templates' . DS . 'header.php';
@@ -92,7 +95,7 @@ public function paintDocumentStart()
*
* @return void
*/
- public function paintTestMenu()
+ public function paintTestMenu(): void
{
$plugins = App::objects('plugin', null, false);
sort($plugins);
@@ -105,9 +108,9 @@ public function paintTestMenu()
*
* @return void
*/
- public function testCaseList()
+ public function testCaseList(): void
{
- $testCases = parent::testCaseList();
+ $testCases = CakeTestLoader::generateTestList($this->params);
$core = $this->params['core'];
$plugin = $this->params['plugin'];
@@ -142,7 +145,7 @@ public function testCaseList()
*
* @return void
*/
- public function sendNoCacheHeaders()
+ public function sendNoCacheHeaders(): void
{
if (!headers_sent()) {
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
@@ -181,14 +184,9 @@ public function paintFooter(TestResult $result): void
echo '
\n";
@@ -353,16 +356,18 @@ public function paintException(Exception $message, Test $test): void
/**
* Prints the message for skipping tests.
*
- * @param Exception|Throwable $message Text of skip condition.
+ * @param Throwable $message Text of skip condition.
* @param Test $test the test method skipped
* @return void
*/
- public function paintSkip(Exception|Throwable $message, Test $test): void
+ public function paintSkip(Throwable $message, Test $test): void
{
+ $name = method_exists($test, 'getName') ? $test->getName() : '';
+
ob_start();
echo "
';
- $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+ $this->assertNotEmpty($this->Controller->EmailTest->send('This is the body of the message'));
$this->assertTextEquals($expected, DebugCompTransport::$lastEmail);
$this->Controller->EmailTest->sendAs = 'html';
$expected = '
';
- $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+ $this->assertNotEmpty($this->Controller->EmailTest->send('This is the body of the message'));
$this->assertTextEquals($expected, DebugCompTransport::$lastEmail);
$this->Controller->EmailTest->sendAs = 'both';
@@ -303,31 +306,31 @@ public function testTemplates()
$expected = '
' . $expected . '
';
- $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+ $this->assertNotEmpty($this->Controller->EmailTest->send('This is the body of the message'));
$this->assertTextEquals(
$expected,
preg_replace('/[a-z0-9]{32}/i', '{boundary}', DebugCompTransport::$lastEmail),
);
$html = <<
+
-
-
- Email Test
-
+
+
+ Email Test
+
-
-
';
- $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message', 'default', 'thin'));
+ $this->assertNotEmpty($this->Controller->EmailTest->send('This is the body of the message', 'default', 'thin'));
$this->assertTextEquals($expected, DebugCompTransport::$lastEmail);
}
@@ -371,7 +374,7 @@ public function testSendNullProperties()
$this->Controller->EmailTest->template = null;
$this->Controller->EmailTest->delivery = 'DebugComp';
- $this->assertTrue($this->Controller->EmailTest->send(null));
+ $this->assertNotEmpty($this->Controller->EmailTest->send(null));
$result = DebugCompTransport::$lastEmail;
$this->assertMatchesRegularExpression('/To: test@example.com\n/', $result);
@@ -399,7 +402,7 @@ public function testSendDebug()
$this->Controller->EmailTest->template = null;
$this->Controller->EmailTest->delivery = 'DebugComp';
- $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+ $this->assertNotEmpty($this->Controller->EmailTest->send('This is the body of the message'));
$result = DebugCompTransport::$lastEmail;
$this->assertMatchesRegularExpression('/To: postmaster@example.com\n/', $result);
@@ -472,17 +475,17 @@ public function testMessageRetrievalWithoutTemplate()
$text = $html = "This is the body of the message\n";
$this->Controller->EmailTest->sendAs = 'both';
- $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+ $this->assertNotEmpty($this->Controller->EmailTest->send('This is the body of the message'));
$this->assertTextEquals($this->Controller->EmailTest->textMessage, $text);
$this->assertTextEquals($this->Controller->EmailTest->htmlMessage, $html);
$this->Controller->EmailTest->sendAs = 'text';
- $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+ $this->assertNotEmpty($this->Controller->EmailTest->send('This is the body of the message'));
$this->assertTextEquals($this->Controller->EmailTest->textMessage, $text);
$this->assertNull($this->Controller->EmailTest->htmlMessage);
$this->Controller->EmailTest->sendAs = 'html';
- $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+ $this->assertNotEmpty($this->Controller->EmailTest->send('This is the body of the message'));
$this->assertNull($this->Controller->EmailTest->textMessage);
$this->assertTextEquals($this->Controller->EmailTest->htmlMessage, $html);
}
@@ -536,17 +539,17 @@ public function testMessageRetrievalWithTemplate()
HTMLBLOC;
$this->Controller->EmailTest->sendAs = 'both';
- $this->assertTrue($this->Controller->EmailTest->send());
+ $this->assertNotEmpty($this->Controller->EmailTest->send());
$this->assertTextEquals($this->Controller->EmailTest->textMessage, $text);
$this->assertTextEquals($this->Controller->EmailTest->htmlMessage, $html);
$this->Controller->EmailTest->sendAs = 'text';
- $this->assertTrue($this->Controller->EmailTest->send());
+ $this->assertNotEmpty($this->Controller->EmailTest->send());
$this->assertTextEquals($this->Controller->EmailTest->textMessage, $text);
$this->assertNull($this->Controller->EmailTest->htmlMessage);
$this->Controller->EmailTest->sendAs = 'html';
- $this->assertTrue($this->Controller->EmailTest->send());
+ $this->assertNotEmpty($this->Controller->EmailTest->send());
$this->assertNull($this->Controller->EmailTest->textMessage);
$this->assertTextEquals($this->Controller->EmailTest->htmlMessage, $html);
}
@@ -579,7 +582,7 @@ public function testMessageRetrievalWithHelper()
$this->Controller->EmailTest->sendAs = 'text';
$this->Controller->EmailTest->delivery = 'DebugComp';
- $this->assertTrue($this->Controller->EmailTest->send());
+ $this->assertNotEmpty($this->Controller->EmailTest->send());
$this->assertTrue((bool)strpos($this->Controller->EmailTest->textMessage, 'Right now: ' . date('Y-m-d\TH:i:s\Z', $timestamp)));
}
@@ -598,7 +601,7 @@ public function testSendContentArray()
$this->Controller->EmailTest->delivery = 'DebugComp';
$content = ['First line', 'Second line', 'Third line'];
- $this->assertTrue($this->Controller->EmailTest->send($content));
+ $this->assertNotEmpty($this->Controller->EmailTest->send($content));
$result = DebugCompTransport::$lastEmail;
$this->assertMatchesRegularExpression('/To: postmaster@example.com\n/', $result);
@@ -627,7 +630,7 @@ public function testDateProperty()
$this->Controller->EmailTest->template = null;
$this->Controller->EmailTest->delivery = 'DebugComp';
- $this->assertTrue($this->Controller->EmailTest->send('test message'));
+ $this->assertNotEmpty($this->Controller->EmailTest->send('test message'));
$result = DebugCompTransport::$lastEmail;
$this->assertMatchesRegularExpression('/Date: Today!\n/', $result);
}
@@ -679,7 +682,7 @@ public function testEncodeSettingInternalCharset()
$this->Controller->EmailTest->delivery = 'DebugComp';
$this->Controller->EmailTest->sendAs = 'text';
- $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+ $this->assertNotEmpty($this->Controller->EmailTest->send('This is the body of the message'));
$subject = '=?UTF-8?B?2YfYsNmHINix2LPYp9mE2Kkg2KjYudmG2YjYp9mGINi32YjZitmEINmF2LE=?=' . "\r\n" . ' =?UTF-8?B?2LPZhCDZhNmE2YXYs9iq2YTZhQ==?=';
@@ -710,17 +713,17 @@ public function testMultibyte()
$subject = '=?UTF-8?B?2YfYsNmHINix2LPYp9mE2Kkg2KjYudmG2YjYp9mGINi32YjZitmEINmF2LE=?=' . "\r\n" . ' =?UTF-8?B?2LPZhCDZhNmE2YXYs9iq2YTZhQ==?=';
$this->Controller->EmailTest->sendAs = 'text';
- $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+ $this->assertNotEmpty($this->Controller->EmailTest->send('This is the body of the message'));
preg_match('/Subject: (.*)Header:/s', DebugCompTransport::$lastEmail, $matches);
$this->assertEquals(trim($matches[1]), $subject);
$this->Controller->EmailTest->sendAs = 'html';
- $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+ $this->assertNotEmpty($this->Controller->EmailTest->send('This is the body of the message'));
preg_match('/Subject: (.*)Header:/s', DebugCompTransport::$lastEmail, $matches);
$this->assertEquals(trim($matches[1]), $subject);
$this->Controller->EmailTest->sendAs = 'both';
- $this->assertTrue($this->Controller->EmailTest->send('This is the body of the message'));
+ $this->assertNotEmpty($this->Controller->EmailTest->send('This is the body of the message'));
preg_match('/Subject: (.*)Header:/s', DebugCompTransport::$lastEmail, $matches);
$this->assertEquals(trim($matches[1]), $subject);
}
@@ -745,7 +748,7 @@ public function testSendWithAttachments()
$body = '