Browse Source

Fix up PHPStan issues.

mscherer 3 years ago
parent
commit
f09371b097

+ 1 - 0
src/Controller/Component/CommonComponent.php

@@ -331,6 +331,7 @@ class CommonComponent extends Component {
 	 */
 	public function isForeignReferer($ref = null) {
 		if ($ref === null) {
+			/** @var string|null $ref */
 			$ref = env('HTTP_REFERER');
 		}
 		if (!$ref) {

+ 1 - 1
src/Controller/Component/RefererRedirectComponent.php

@@ -58,7 +58,7 @@ class RefererRedirectComponent extends Component {
 	 */
 	protected function referer() {
 		$referer = $this->getController()->getRequest()->getQuery(static::QUERY_REFERER);
-		if (!$referer) {
+		if (!$referer || !is_string($referer)) {
 			return null;
 		}
 

+ 1 - 1
src/Controller/Component/UrlComponent.php

@@ -78,7 +78,7 @@ class UrlComponent extends Component {
 	 * @param array|string|null $url Either a relative string url like `/products/view/23` or
 	 *    an array of URL parameters. Using an array for URLs will allow you to leverage
 	 *    the reverse routing features of CakePHP.
-	 * @param array $options Array of options
+	 * @param array<string, mixed> $options Array of options
 	 * @return string Full translated URL with base path.
 	 */
 	public function build($url = null, array $options = []): string {

+ 6 - 6
src/Mailer/Message.php

@@ -148,7 +148,7 @@ class Message extends CakeMessage {
 	 * @param string $contentId
 	 * @param string $file
 	 * @param string|null $name
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return $this
 	 */
 	public function addEmbeddedAttachmentByContentId($contentId, $file, $name = null, array $options = []) {
@@ -177,7 +177,7 @@ class Message extends CakeMessage {
 	 *
 	 * @param string $file Absolute path
 	 * @param string|null $name (optional)
-	 * @param array $options Options
+	 * @param array<string, mixed> $options Options
 	 * @return string
 	 */
 	public function addEmbeddedAttachment(string $file, ?string $name = null, array $options = []): string {
@@ -213,7 +213,7 @@ class Message extends CakeMessage {
 	 * @param string $content Blob data
 	 * @param string $file File File path to file
 	 * @param string|null $mimeType (leave it empty to get mimetype from $filename)
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return $this
 	 */
 	public function addEmbeddedBlobAttachmentByContentId($contentId, $content, $file, $mimeType = null, array $options = []) {
@@ -288,7 +288,7 @@ class Message extends CakeMessage {
 	 *
 	 * @param string $file
 	 * @param string $name
-	 * @return string|bool CID of the found file or false if no such attachment can be found
+	 * @return string|false CID of the found file or false if no such attachment can be found
 	 */
 	protected function _isEmbeddedAttachment($file, $name) {
 		foreach ($this->getAttachments() as $filename => $fileInfo) {
@@ -309,7 +309,7 @@ class Message extends CakeMessage {
 	 *
 	 * @param string $content
 	 * @param string $name
-	 * @return string|bool CID of the found file or false if no such attachment can be found
+	 * @return string|false CID of the found file or false if no such attachment can be found
 	 */
 	protected function _isEmbeddedBlobAttachment($content, $name) {
 		foreach ($this->getAttachments() as $filename => $fileInfo) {
@@ -377,7 +377,7 @@ class Message extends CakeMessage {
 		);
 		$content = file_get_contents($path, false, $context);
 		if (!$content) {
-			trigger_error('No content found for ' . $path);
+			throw new \RuntimeException('No content found for ' . $path);
 		}
 
 		return chunk_split(base64_encode($content));

+ 1 - 1
src/Model/Behavior/BitmaskedBehavior.php

@@ -52,7 +52,7 @@ class BitmaskedBehavior extends Behavior {
 
 	/**
 	 * @param \Cake\ORM\Query $query
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @throws \InvalidArgumentException If the 'slug' key is missing in options
 	 * @return \Cake\ORM\Query
 	 */

+ 22 - 4
src/Model/Behavior/JsonableBehavior.php

@@ -39,7 +39,7 @@ use Tools\Utility\Text;
 class JsonableBehavior extends Behavior {
 
 	/**
-	 * @var string|false|null
+	 * @var array|string|false|null
 	 */
 	protected $decoded;
 
@@ -167,7 +167,7 @@ class JsonableBehavior extends Behavior {
 	protected function _getMappedFields() {
 		$usedFields = $this->_config['fields'];
 		$mappedFields = $this->_config['map'];
-		if (empty($mappedFields)) {
+		if (!$mappedFields) {
 			$mappedFields = $usedFields;
 		}
 
@@ -192,13 +192,25 @@ class JsonableBehavior extends Behavior {
 	public function _encode($val) {
 		if (!empty($this->_config['fields'])) {
 			if ($this->_config['input'] === 'param') {
+				if (!is_string($val)) {
+					throw new \InvalidArgumentException('Only accepts string for input type `param`');
+				}
 				$val = $this->_fromParam($val);
 			} elseif ($this->_config['input'] === 'list') {
+				if (!is_string($val)) {
+					throw new \InvalidArgumentException('Only accepts string for input type `list`');
+				}
 				$val = $this->_fromList($val);
 				if ($this->_config['unique']) {
+					if (!is_array($val)) {
+						throw new \InvalidArgumentException('Only accepts array for input type `unique`');
+					}
 					$val = array_unique($val);
 				}
 				if ($this->_config['sort']) {
+					if (!is_array($val)) {
+						throw new \InvalidArgumentException('Only accepts array for input type `sort`');
+					}
 					sort($val);
 				}
 			}
@@ -208,7 +220,12 @@ class JsonableBehavior extends Behavior {
 			return null;
 		}
 
-		return json_encode($val, $this->_config['encodeParams']['options'], $this->_config['encodeParams']['depth']);
+		$result = json_encode($val, $this->_config['encodeParams']['options'], $this->_config['encodeParams']['depth']);
+		if ($result === false) {
+			return null;
+		}
+
+		return $result;
 	}
 
 	/**
@@ -222,7 +239,8 @@ class JsonableBehavior extends Behavior {
 			return $val;
 		}
 
-		$decoded = json_decode($val, $this->_config['decodeParams']['assoc'], $this->_config['decodeParams']['depth'], $this->_config['decodeParams']['options']);
+		$flags = JSON_THROW_ON_ERROR | $this->_config['decodeParams']['options'];
+		$decoded = json_decode($val, $this->_config['decodeParams']['assoc'], $this->_config['decodeParams']['depth'], $flags);
 
 		if ($decoded === false) {
 			return false;

+ 6 - 4
src/Model/Behavior/NeighborBehavior.php

@@ -19,7 +19,7 @@ class NeighborBehavior extends Behavior {
 
 	/**
 	 * @param int $id
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 *
 	 * @throws \InvalidArgumentException
 	 * @return array
@@ -29,7 +29,9 @@ class NeighborBehavior extends Behavior {
 			throw new InvalidArgumentException("The 'id' key is required for find('neighbors')");
 		}
 
-		$sortField = $this->_table->hasField('created') ? 'created' : $this->_table->getPrimaryKey();
+		/** @var string $primaryKey */
+		$primaryKey = $this->_table->getPrimaryKey();
+		$sortField = $this->_table->hasField('created') ? 'created' : $primaryKey;
 		$defaults = [
 			'sortField' => $this->_table->getAlias() . '.' . $sortField,
 		];
@@ -40,7 +42,7 @@ class NeighborBehavior extends Behavior {
 		$sortDirSymb = $normalDirection ? ['>=', '<='] : ['<=', '>='];
 
 		if (empty($options['value'])) {
-			$data = $this->_table->find('all', ['conditions' => [$this->_table->getPrimaryKey() => $id]])->first();
+			$data = $this->_table->find('all', ['conditions' => [$primaryKey => $id]])->first();
 			[$model, $sortField] = pluginSplit($options['sortField']);
 			$options['value'] = $data[$sortField];
 		}
@@ -55,7 +57,7 @@ class NeighborBehavior extends Behavior {
 		if (!empty($options['fields'])) {
 			$findOptions['fields'] = $options['fields'];
 		}
-		$findOptions['conditions'][$this->_table->getAlias() . '.' . $this->_table->getPrimaryKey() . ' !='] = $id;
+		$findOptions['conditions'][$this->_table->getAlias() . '.' . $primaryKey . ' !='] = $id;
 
 		$prevOptions = $findOptions;
 		$prevOptions['conditions'] = Hash::merge($prevOptions['conditions'], [$options['sortField'] . ' ' . $sortDirSymb[1] => $options['value']]);

+ 13 - 9
src/Model/Behavior/PasswordableBehavior.php

@@ -388,7 +388,7 @@ class PasswordableBehavior extends Behavior {
 	 * If not implemented in Table class
 	 *
 	 * @param string $value
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @param array $context
 	 * @return bool Success
 	 */
@@ -406,7 +406,7 @@ class PasswordableBehavior extends Behavior {
 	 * If not implemented in Table class
 	 *
 	 * @param string $data
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @param array $context
 	 * @return bool Success
 	 */
@@ -429,14 +429,16 @@ class PasswordableBehavior extends Behavior {
 	 */
 	public function validateNotSameHash($data, $context) {
 		$field = $this->_config['field'];
-		if (empty($context['data'][$this->_table->getPrimaryKey()])) {
+		/** @var string $primaryKey */
+		$primaryKey = $this->_table->getPrimaryKey();
+		if (empty($context['data'][$primaryKey])) {
 			return true;
 		}
 
-		$primaryKey = $context['data'][$this->_table->getPrimaryKey()];
+		$primaryKey = $context['data'][$primaryKey];
 		$value = $context['data'][$context['field']];
 
-		$dbValue = $this->_table->find()->where([$this->_table->getAlias() . '.' . $this->_table->getPrimaryKey() => $primaryKey])->first();
+		$dbValue = $this->_table->find()->where([$this->_table->getAlias() . '.' . $primaryKey => $primaryKey])->first();
 		if (!$dbValue) {
 			return true;
 		}
@@ -458,8 +460,10 @@ class PasswordableBehavior extends Behavior {
 	protected function _validateSameHash($pwd, $context) {
 		$field = $this->_config['field'];
 
-		$primaryKey = $context['data'][$this->_table->getPrimaryKey()];
-		$dbValue = $this->_table->find()->where([$this->_table->getAlias() . '.' . $this->_table->getPrimaryKey() => $primaryKey])->first();
+		/** @var string $primaryKey */
+		$primaryKey = $this->_table->getPrimaryKey();
+		$primaryKeyValue = $context['data'][$primaryKey];
+		$dbValue = $this->_table->find()->where([$this->_table->getAlias() . '.' . $primaryKey => $primaryKeyValue])->first();
 		if (!$dbValue) {
 			return false;
 		}
@@ -474,8 +478,8 @@ class PasswordableBehavior extends Behavior {
 	}
 
 	/**
-	 * @param array|string $hasher Name or options array.
-	 * @param array $options
+	 * @param array<string, mixed>|string $hasher Name or options array.
+	 * @param array<string, mixed> $options
 	 * @return \Cake\Auth\AbstractPasswordHasher
 	 */
 	protected function _getPasswordHasher($hasher, array $options = []) {

+ 9 - 5
src/Model/Behavior/ResetBehavior.php

@@ -83,11 +83,13 @@ class ResetBehavior extends Behavior {
 	 * @return int Modified records
 	 */
 	public function resetRecords(array $params = []) {
+		/** @var string $primaryKey */
+		$primaryKey = $this->_table->getPrimaryKey();
 		$defaults = [
 			'page' => 1,
 			'limit' => $this->_config['limit'],
 			'fields' => [],
-			'order' => [$this->_table->getAlias() . '.' . $this->_table->getPrimaryKey() => 'ASC'],
+			'order' => [$this->_table->getAlias() . '.' . $primaryKey => 'ASC'],
 			'conditions' => $this->_config['scope'],
 			'validate' => $this->_config['validate'],
 		];
@@ -97,11 +99,13 @@ class ResetBehavior extends Behavior {
 					throw new RuntimeException('Table does not have field ' . $field);
 				}
 			}
-			$defaults['fields'] = array_merge([$this->_table->getAlias() . '.' . $this->_table->getPrimaryKey()], $this->_config['fields']);
+			$defaults['fields'] = array_merge([$this->_table->getAlias() . '.' . $primaryKey], $this->_config['fields']);
 		} else {
-			$defaults['fields'] = [$this->_table->getAlias() . '.' . $this->_table->getPrimaryKey()];
-			if ($this->_table->getDisplayField() !== $this->_table->getPrimaryKey()) {
-				$defaults['fields'][] = $this->_table->getAlias() . '.' . $this->_table->getDisplayField();
+			$defaults['fields'] = [$this->_table->getAlias() . '.' . $primaryKey];
+			/** @var string $displayField */
+			$displayField = $this->_table->getDisplayField();
+			if ($displayField !== $primaryKey) {
+				$defaults['fields'][] = $this->_table->getAlias() . '.' . $displayField;
 			}
 		}
 

+ 15 - 11
src/Model/Behavior/SluggedBehavior.php

@@ -172,7 +172,7 @@ class SluggedBehavior extends Behavior {
 	 * ->find('slugged')
 	 *
 	 * @param \Cake\ORM\Query $query
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @throws \InvalidArgumentException If the 'slug' key is missing in options
 	 * @return \Cake\ORM\Query
 	 */
@@ -218,7 +218,7 @@ class SluggedBehavior extends Behavior {
 	 * SluggedBehavior::slug()
 	 *
 	 * @param \Cake\Datasource\EntityInterface $entity Entity
-	 * @param array $options Options
+	 * @param array<string, mixed> $options Options
 	 * @return void
 	 */
 	public function slug(EntityInterface $entity, array $options = []) {
@@ -350,9 +350,11 @@ class SluggedBehavior extends Behavior {
 			$field = $this->_table->getAlias() . '.' . $this->_config['field'];
 			$conditions = [$field => $slug];
 			$conditions = array_merge($conditions, $this->_config['scope']);
-			$id = $entity->get($this->_table->getPrimaryKey());
+			/** @var string $primaryKey */
+			$primaryKey = $this->_table->getPrimaryKey();
+			$id = $entity->get($primaryKey);
 			if ($id) {
-				$conditions['NOT'][$this->_table->getAlias() . '.' . $this->_table->getPrimaryKey()] = $id;
+				$conditions['NOT'][$this->_table->getAlias() . '.' . $primaryKey] = $id;
 			}
 			$i = 0;
 			$suffix = '';
@@ -382,7 +384,7 @@ class SluggedBehavior extends Behavior {
 	 * Note that you should use the Reset behavior if you need additional functionality such
 	 * as callbacks or timeouts.
 	 *
-	 * @param array $params
+	 * @param array<string, mixed> $params
 	 * @throws \RuntimeException
 	 * @return bool Success
 	 */
@@ -390,19 +392,21 @@ class SluggedBehavior extends Behavior {
 		if (!$this->_table->hasField($this->_config['field'])) {
 			throw new RuntimeException('Table does not have field ' . $this->_config['field']);
 		}
+		/** @var string $displayField */
+		$displayField = $this->_table->getDisplayField();
 		$defaults = [
 			'page' => 1,
 			'limit' => 100,
 			'fields' => array_merge([$this->_table->getPrimaryKey()], $this->_config['label']),
-			'order' => $this->_table->getDisplayField() . ' ASC',
+			'order' =>  $displayField . ' ASC',
 			'conditions' => $this->_config['scope'],
 			'overwrite' => true,
 		];
-		$params = array_merge($defaults, $params);
+		$params += $defaults;
 
 		$conditions = $params['conditions'];
 		$count = $this->_table->find('all', compact('conditions'))->count();
-		$max = ini_get('max_execution_time');
+		$max = (int)ini_get('max_execution_time');
 		if ($max) {
 			set_time_limit(max($max, $count / 100));
 		}
@@ -444,7 +448,7 @@ class SluggedBehavior extends Behavior {
 		$field = current($label);
 		$fields = (array)$entity->get($field);
 
-		$locale = [];
+		$locales = [];
 		foreach ($fields as $locale => $_) {
 			$res = null;
 			foreach ($label as $field) {
@@ -454,9 +458,9 @@ class SluggedBehavior extends Behavior {
 				}
 			}
 
-			$locale[$locale] = $res;
+			$locales[$locale] = $res;
 		}
-		$entity->set($this->getConfig('slugField'), $locale);
+		$entity->set($this->getConfig('slugField'), $locales);
 	}
 
 	/**

+ 2 - 0
src/Model/Behavior/ToggleBehavior.php

@@ -57,6 +57,7 @@ class ToggleBehavior extends Behavior {
 				$order['modified'] = 'DESC';
 			}
 		}
+		/** @var \Cake\Datasource\EntityInterface|null $entity */
 		$entity = $this->_table->find()->where($conditions)->order($order)->first();
 		if (!$entity) {
 			// This should be caught with a validation rule if at least one "primary" must exist
@@ -130,6 +131,7 @@ class ToggleBehavior extends Behavior {
 	protected function getCurrent(EntityInterface $entity) {
 		$conditions = $this->buildConditions($entity);
 
+		/** @var \Cake\Datasource\EntityInterface|null */
 		return $this->_table->find()
 			->where($conditions)
 			->first();

+ 1 - 1
src/Model/Entity/EnumTrait.php

@@ -10,7 +10,7 @@ trait EnumTrait {
 	 *
 	 * @link https://www.dereuromark.de/2010/06/24/static-enums-or-semihardcoded-attributes/
 	 * @param array|string|int|null $value Integer or array of keys or NULL for complete array result
-	 * @param array $options Options
+	 * @param array<string, mixed> $options Options
 	 * @param string|null $default Default value
 	 * @return array|string|null
 	 */

+ 22 - 8
src/Model/Table/Table.php

@@ -65,7 +65,7 @@ class Table extends ShimTable {
 	 * @override To allow multiple scoped values
 	 *
 	 * @param mixed $value The value of column to be checked for uniqueness
-	 * @param array $options The options array, optionally containing the 'scope' key
+	 * @param array<string, mixed> $options The options array, optionally containing the 'scope' key
 	 * @param array $context The validation context as provided by the validation routine
 	 * @return bool true if the value is unique
 	 */
@@ -116,7 +116,7 @@ class Table extends ShimTable {
 	 * @param string $tableName The related model
 	 * @param string|null $groupField Field to group by
 	 * @param string $type Find type
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return \Cake\ORM\Query
 	 */
 	public function getRelatedInUse($tableName, $groupField = null, $type = 'all', array $options = []) {
@@ -153,13 +153,15 @@ class Table extends ShimTable {
 	 *
 	 * @param string $groupField Field to group by
 	 * @param string $type Find type
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return \Cake\ORM\Query
 	 */
 	public function getFieldInUse($groupField, $type = 'all', array $options = []) {
+		/** @var string $displayField */
+		$displayField = $this->getDisplayField();
 		$defaults = [
 			'group' => $groupField,
-			'order' => [$this->getDisplayField() => 'ASC'],
+			'order' => [$displayField => 'ASC'],
 		];
 		if ($type === 'list') {
 			$defaults['fields'] = [$this->getPrimaryKey(), $this->getDisplayField(), $groupField];
@@ -213,7 +215,7 @@ class Table extends ShimTable {
 	 * - deep (default: TRUE)
 	 *
 	 * @param array|string $url Full URL starting with http://...
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @param array $context
 	 * @return bool Success
 	 */
@@ -226,6 +228,9 @@ class Table extends ShimTable {
 			return false;
 		}
 		if (!isset($options['autoComplete']) || $options['autoComplete'] !== false) {
+			if (!is_string($url)) {
+				throw new \InvalidArgumentException('Can only accept string for autoComplete case');
+			}
 			$url = $this->_autoCompleteUrl($url);
 		}
 
@@ -239,7 +244,12 @@ class Table extends ShimTable {
 		}
 		// same domain?
 		if (!empty($options['sameDomain']) && env('HTTP_HOST')) {
+			if (!is_string($url)) {
+				throw new \InvalidArgumentException('Can only accept string for sameDomain case');
+			}
+			/** @var string $is */
 			$is = parse_url($url, PHP_URL_HOST);
+			/** @var string $expected */
 			$expected = env('HTTP_HOST');
 			if (mb_strtolower($is) !== mb_strtolower($expected)) {
 				return false;
@@ -250,6 +260,10 @@ class Table extends ShimTable {
 			return true;
 		}
 
+		if (!is_string($url)) {
+			throw new \InvalidArgumentException('Can only accept string for deep case');
+		}
+
 		return $this->_validUrl($url);
 	}
 
@@ -296,7 +310,7 @@ class Table extends ShimTable {
 	 * Validation of DateTime Fields (both Date and Time together)
 	 *
 	 * @param mixed $value
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * - dateFormat (defaults to 'ymd')
 	 * - allowEmpty
 	 * - after/before (fieldName to validate against)
@@ -390,7 +404,7 @@ class Table extends ShimTable {
 	 * Validation of Date fields (as the core one is buggy!!!)
 	 *
 	 * @param mixed $value
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * - dateFormat (defaults to 'ymd')
 	 * - allowEmpty
 	 * - after/before (fieldName to validate against)
@@ -452,7 +466,7 @@ class Table extends ShimTable {
 	 * Validation of Time fields
 	 *
 	 * @param mixed $value
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * - timeFormat (defaults to 'hms')
 	 * - allowEmpty
 	 * - after/before (fieldName to validate against)

+ 1 - 1
src/Model/Table/TokensTable.php

@@ -228,7 +228,7 @@ class TokensTable extends Table {
 		}
 
 		$function = 'random_bytes';
-		$value = bin2hex($function($length / 2));
+		$value = bin2hex($function((int)($length / 2)));
 		if (strlen($value) !== $length) {
 			$value = str_pad($value, $length, (string)random_int(0, 9));
 		}

+ 1 - 1
src/Utility/FileLog.php

@@ -44,7 +44,7 @@ class FileLog {
 	/**
 	 * Log data into custom file
 	 *
-	 * @param object|array|string $data Data to store
+	 * @param \ArrayObject|array|string $data Data to store
 	 * @param string|null $filename Filename of log file
 	 * @param bool $traceKey Add trace string key into log data
 	 * @return bool Success

+ 10 - 10
src/Utility/FrozenTime.php

@@ -78,7 +78,7 @@ class FrozenTime extends CakeFrozenTime {
 	 *
 	 * @param mixed $startTime (db format or timestamp)
 	 * @param mixed|null $endTime (db format or timestamp)
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return int The distance in seconds
 	 */
 	public static function difference($startTime, $endTime = null, array $options = []) {
@@ -408,7 +408,7 @@ class FrozenTime extends CakeFrozenTime {
 	 *
 	 * @param string|null $dateString
 	 * @param string|null $format Format (YYYY-MM-DD, DD.MM.YYYY)
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return string
 	 */
 	public static function localDate($dateString, $format = null, array $options = []) {
@@ -489,7 +489,7 @@ class FrozenTime extends CakeFrozenTime {
 	 *
 	 * @param \Cake\I18n\I18nDateTimeInterface|string|null $dateString
 	 * @param string|null $format Format (YYYY-MM-DD, DD.MM.YYYY)
-	 * @param array $options Options
+	 * @param array<string, mixed> $options Options
 	 * @return string
 	 */
 	public static function niceDate($dateString, $format = null, array $options = []) {
@@ -613,7 +613,7 @@ class FrozenTime extends CakeFrozenTime {
 	 * @param int $month
 	 * 1..12
 	 * @param bool $abbr (if abbreviation should be returned)
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * - appendDot (only for 3 letter abbr; defaults to false)
 	 * @return string translatedText
 	 */
@@ -667,7 +667,7 @@ class FrozenTime extends CakeFrozenTime {
 	 * - abbr
 	 *
 	 * @param array<int> $monthKeys
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return array<string>
 	 */
 	public static function monthNames(array $monthKeys = [], array $options = []) {
@@ -687,7 +687,7 @@ class FrozenTime extends CakeFrozenTime {
 	 * Weekdays
 	 *
 	 * @param array<int> $dayKeys
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return array<string>
 	 */
 	public static function dayNames(array $dayKeys = [], array $options = []) {
@@ -793,7 +793,7 @@ class FrozenTime extends CakeFrozenTime {
 	 * @see timeAgoInWords()
 	 * @param int $seconds
 	 * @param string|null $format
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * - boolean v: verbose
 	 * - boolean zero: if false: 0 days 5 hours => 5 hours etc.
 	 * - int: accuracy (how many sub-formats displayed?) //TODO
@@ -914,7 +914,7 @@ class FrozenTime extends CakeFrozenTime {
 	 *
 	 * @param mixed $date
 	 * @param string|null $format Format
-	 * @param array $options Options
+	 * @param array<string, mixed> $options Options
 	 * - default, separator
 	 * - boolean zero: if false: 0 days 5 hours => 5 hours etc.
 	 * - verbose/past/future: string with %s or boolean true/false
@@ -1081,7 +1081,7 @@ class FrozenTime extends CakeFrozenTime {
 	 * Parse a period (from ... to)
 	 *
 	 * @param string $searchString Search string to parse
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * - separator (defaults to space [ ])
 	 * - format (defaults to Y-m-d H:i:s)
 	 * @return array period [0=>min, 1=>max]
@@ -1107,7 +1107,7 @@ class FrozenTime extends CakeFrozenTime {
 	 *
 	 * @param string $searchString to parse
 	 * @param string $fieldName (Model.field)
-	 * @param array $options (see Time::period)
+	 * @param array<string, mixed> $options (see Time::period)
 	 * @return string query SQL Query
 	 */
 	public static function periodAsSql($searchString, $fieldName, array $options = []) {

+ 2 - 2
src/Utility/L10n.php

@@ -287,8 +287,8 @@ class L10n {
 			return $result;
 		}
 		if (is_string($mixed)) {
-			if (strlen($mixed) === 2 && in_array($mixed, $this->_l10nMap)) {
-				return array_search($mixed, $this->_l10nMap);
+			if (strlen($mixed) === 2 && in_array($mixed, $this->_l10nMap, true)) {
+				return (string)array_search($mixed, $this->_l10nMap, true);
 			}
 			if (isset($this->_l10nMap[$mixed])) {
 				return $this->_l10nMap[$mixed];

+ 4 - 1
src/Utility/Language.php

@@ -32,6 +32,7 @@ class Language {
 			if (!env('HTTP_ACCEPT_LANGUAGE')) {
 				return [];
 			}
+			/** @var string $languageList */
 			$languageList = env('HTTP_ACCEPT_LANGUAGE');
 		}
 
@@ -58,8 +59,10 @@ class Language {
 				if ($options['forceLowerCase']) {
 					$language = strtolower($language);
 				} else {
+					/** @var string $language */
 					$language = substr_replace($language, strtolower(substr($language, 0, 2)), 0, 2);
 					if (strlen($language) === 5) {
+						/** @var string $language */
 						$language = substr_replace($language, strtoupper(substr($language, 3, 2)), 3, 2);
 					}
 				}
@@ -69,7 +72,7 @@ class Language {
 					$languagesRanks[$language] = $rank;
 				} elseif ($rank > $languagesRanks[$language]) {
 					foreach ($languages as $existRank => $existLangs) {
-						$key = array_search($existLangs, $languages);
+						$key = array_search($existLangs, $languages, true);
 						if ($key !== false) {
 							unset($languages[$existRank][$key]);
 							if (empty($languages[$existRank])) {

+ 1 - 1
src/Utility/Mime.php

@@ -701,7 +701,7 @@ class Mime extends Response {
 	/**
 	 * Override constructor
 	 *
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 */
 	public function __construct(array $options = []) {
 	}

+ 2 - 2
src/Utility/Number.php

@@ -117,7 +117,7 @@ class Number extends CakeNumber {
 	 * - positive
 	 *
 	 * @param float $number
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return string
 	 */
 	public static function format($number, array $options = []): string {
@@ -143,7 +143,7 @@ class Number extends CakeNumber {
 	 *
 	 * @param float $number
 	 * @param string|null $currency
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return string
 	 */
 	public static function currency($number, ?string $currency = null, array $options = []): string {

+ 10 - 7
src/Utility/Text.php

@@ -135,7 +135,7 @@ class Text extends CakeText {
 	 * - 'whitespace': If whitespace should be counted, as well, defaults to false
 	 *
 	 * @param string $text
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return int
 	 */
 	public static function numberOfChars($text, array $options = []) {
@@ -170,6 +170,7 @@ class Text extends CakeText {
 	 * @return string
 	 */
 	public function convertToOrd($str, $separator = '-') {
+		/** @var array<string> $chars */
 		$chars = preg_split('//', $str, -1);
 		$res = [];
 		foreach ($chars as $char) {
@@ -198,7 +199,9 @@ class Text extends CakeText {
 			// If a user has escaped a term (to demonstrate that it is a group,
 			// or includes a comma or quote character), we remove the escape
 			// formatting so to save the term into the database as the user intends.
-			$tag = trim(str_replace('""', '"', preg_replace('/^"(.*)"$/', '\1', $tag)));
+			/** @var string $replacedString */
+			$replacedString = str_replace('""', '"', preg_replace('/^"(.*)"$/', '\1', $tag));
+			$tag = trim($replacedString);
 			if ($tag) {
 				$tags[] = $tag;
 			}
@@ -251,7 +254,7 @@ class Text extends CakeText {
 	 * Extract words
 	 *
 	 * @param string $text
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * - min_char, max_char, case_sensititive, ...
 	 * @return array<string>
 	 */
@@ -295,7 +298,7 @@ class Text extends CakeText {
 	 *
 	 * @param string $value
 	 * @param int $words
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * - ellipsis
 	 * - html
 	 * @return string
@@ -389,11 +392,11 @@ class Text extends CakeText {
 				if ($digits < 128) {
 					$out .= chr($digits);
 				} elseif ($digits < 2048) {
-					$out .= chr(192 + (($digits - ($digits % 64)) / 64));
+					$out .= chr(192 + (int)(($digits - ($digits % 64)) / 64));
 					$out .= chr(128 + ($digits % 64));
 				} else {
-					$out .= chr(224 + (($digits - ($digits % 4096)) / 4096));
-					$out .= chr(128 + ((($digits % 4096) - ($digits % 64)) / 64));
+					$out .= chr(224 + (int)(($digits - ($digits % 4096)) / 4096));
+					$out .= chr(128 + (int)((($digits % 4096) - ($digits % 64)) / 64));
 					$out .= chr(128 + ($digits % 64));
 				}
 

+ 10 - 10
src/Utility/Time.php

@@ -81,7 +81,7 @@ class Time extends CakeTime {
 	 *
 	 * @param mixed $startTime (db format or timestamp)
 	 * @param mixed|null $endTime (db format or timestamp)
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return int The distance in seconds
 	 */
 	public static function difference($startTime, $endTime = null, array $options = []) {
@@ -411,7 +411,7 @@ class Time extends CakeTime {
 	 *
 	 * @param string|null $dateString
 	 * @param string|null $format Format (YYYY-MM-DD, DD.MM.YYYY)
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return string
 	 */
 	public static function localDate($dateString, $format = null, array $options = []) {
@@ -492,7 +492,7 @@ class Time extends CakeTime {
 	 *
 	 * @param \Cake\I18n\I18nDateTimeInterface|string|null $dateString
 	 * @param string|null $format Format (YYYY-MM-DD, DD.MM.YYYY)
-	 * @param array $options Options
+	 * @param array<string, mixed> $options Options
 	 * @return string
 	 */
 	public static function niceDate($dateString, $format = null, array $options = []) {
@@ -616,7 +616,7 @@ class Time extends CakeTime {
 	 * @param int $month
 	 * 1..12
 	 * @param bool $abbr (if abbreviation should be returned)
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * - appendDot (only for 3 letter abbr; defaults to false)
 	 * @return string translatedText
 	 */
@@ -670,7 +670,7 @@ class Time extends CakeTime {
 	 * - abbr
 	 *
 	 * @param array<int> $monthKeys
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return array<string>
 	 */
 	public static function monthNames(array $monthKeys = [], array $options = []) {
@@ -690,7 +690,7 @@ class Time extends CakeTime {
 	 * Weekdays
 	 *
 	 * @param array<int> $dayKeys
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return array<string>
 	 */
 	public static function dayNames(array $dayKeys = [], array $options = []) {
@@ -796,7 +796,7 @@ class Time extends CakeTime {
 	 * @see timeAgoInWords()
 	 * @param int $seconds
 	 * @param string|null $format
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * - boolean v: verbose
 	 * - boolean zero: if false: 0 days 5 hours => 5 hours etc.
 	 * - int: accuracy (how many sub-formats displayed?) //TODO
@@ -917,7 +917,7 @@ class Time extends CakeTime {
 	 *
 	 * @param mixed $date
 	 * @param string|null $format Format
-	 * @param array $options Options
+	 * @param array<string, mixed> $options Options
 	 * - default, separator
 	 * - boolean zero: if false: 0 days 5 hours => 5 hours etc.
 	 * - verbose/past/future: string with %s or boolean true/false
@@ -1084,7 +1084,7 @@ class Time extends CakeTime {
 	 * Parse a period (from ... to)
 	 *
 	 * @param string $searchString Search string to parse
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * - separator (defaults to space [ ])
 	 * - format (defaults to Y-m-d H:i:s)
 	 * @return array period [0=>min, 1=>max]
@@ -1110,7 +1110,7 @@ class Time extends CakeTime {
 	 *
 	 * @param string $searchString to parse
 	 * @param string $fieldName (Model.field)
-	 * @param array $options (see Time::period)
+	 * @param array<string, mixed> $options (see Time::period)
 	 * @return string query SQL Query
 	 */
 	public static function periodAsSql($searchString, $fieldName, array $options = []) {

+ 5 - 3
src/Utility/Utility.php

@@ -55,7 +55,7 @@ class Utility {
 	 *
 	 * @param string $data The data to tokenize
 	 * @param string $separator The token to split the data on.
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return array
 	 */
 	public static function tokenize($data, $separator = ',', $options = []) {
@@ -158,7 +158,7 @@ class Utility {
 	 */
 	public static function getClientIp($safe = true) {
 		if (!$safe && env('HTTP_X_FORWARDED_FOR')) {
-			$ipaddr = preg_replace('/(?:,.*)/', '', env('HTTP_X_FORWARDED_FOR'));
+			$ipaddr = preg_replace('/(?:,.*)/', '', (string)env('HTTP_X_FORWARDED_FOR'));
 		} elseif (!$safe && env('HTTP_CLIENT_IP')) {
 			$ipaddr = env('HTTP_CLIENT_IP');
 		} else {
@@ -175,12 +175,14 @@ class Utility {
 	 * @return string referer (local or foreign)
 	 */
 	public static function getReferer($full = false) {
+		/** @var string|null $ref */
 		$ref = env('HTTP_REFERER');
+		/** @var string|null $forwarded */
 		$forwarded = env('HTTP_X_FORWARDED_HOST');
 		if ($forwarded) {
 			$ref = $forwarded;
 		}
-		if (empty($ref)) {
+		if (!$ref) {
 			return $ref;
 		}
 		if ($full) {

+ 1 - 1
src/View/Helper/CommonHelper.php

@@ -115,7 +115,7 @@ class CommonHelper extends Helper {
 	 *
 	 * @param string $content
 	 * @param string|null $language (iso2: de, en-us, ...)
-	 * @param array $options Additional options
+	 * @param array<string, mixed> $options Additional options
 	 * @return string HTML Markup
 	 */
 	public function metaDescription(string $content, ?string $language = null, array $options = []): string {

+ 1 - 1
src/View/Helper/FormHelper.php

@@ -43,7 +43,7 @@ class FormHelper extends CakeFormHelper {
 	 * @param mixed $model The context for which the form is being defined. Can
 	 *   be an ORM entity, ORM resultset, or an array of meta data. You can use false or null
 	 *   to make a model-less form.
-	 * @param array $options An array of html attributes and options.
+	 * @param array<string, mixed> $options An array of html attributes and options.
 	 * @return string An formatted opening FORM tag.
 	 */
 	public function create($model = null, array $options = []): string {

+ 34 - 27
src/View/Helper/FormatHelper.php

@@ -88,8 +88,8 @@ class FormatHelper extends Helper {
 	 * jqueryAccess: {id}Pro, {id}Contra
 	 *
 	 * @param mixed $value Boolish value
-	 * @param array $options
-	 * @param array $attributes
+	 * @param array<string, mixed> $options
+	 * @param array<string, mixed> $attributes
 	 * @return string
 	 */
 	public function thumbs($value, array $options = [], array $attributes = []) {
@@ -103,7 +103,7 @@ class FormatHelper extends Helper {
 	 *
 	 * @param array $neighbors (containing prev and next)
 	 * @param string $field : just field or Model.field syntax
-	 * @param array $options :
+	 * @param array<string, mixed> $options :
 	 * - name: title name: next{Record} (if none is provided, "record" is used - not translated!)
 	 * - slug: true/false (defaults to false)
 	 * - titleField: field or Model.field
@@ -183,8 +183,8 @@ class FormatHelper extends Helper {
 	 * Displays gender icon
 	 *
 	 * @param string|int $value
-	 * @param array $options
-	 * @param array $attributes
+	 * @param array<string, mixed> $options
+	 * @param array<string, mixed> $attributes
 	 * @return string
 	 */
 	public function genderIcon($value, array $options = [], array $attributes = []) {
@@ -213,8 +213,8 @@ class FormatHelper extends Helper {
 	 * - pull (string: left, right)
 	 *
 	 * @param array|string $icon
-	 * @param array $options
-	 * @param array $attributes
+	 * @param array<string, mixed> $options
+	 * @param array<string, mixed> $attributes
 	 * @return string
 	 */
 	public function fontIcon($icon, array $options = [], array $attributes = []) {
@@ -252,9 +252,9 @@ class FormatHelper extends Helper {
 	 * Icons using the default namespace or an already prefixed one.
 	 *
 	 * @param string $icon (constant or filename)
-	 * @param array $options :
+	 * @param array<string, mixed> $options :
 	 * - translate, title, ...
-	 * @param array $attributes :
+	 * @param array<string, mixed> $attributes :
 	 * - class, ...
 	 * @return string
 	 */
@@ -310,9 +310,9 @@ class FormatHelper extends Helper {
 	 * Img Icons
 	 *
 	 * @param string $icon (constant or filename)
-	 * @param array $options :
+	 * @param array<string, mixed> $options :
 	 * - translate, title, ...
-	 * @param array $attributes :
+	 * @param array<string, mixed> $attributes :
 	 * - class, ...
 	 * @return string
 	 */
@@ -325,9 +325,9 @@ class FormatHelper extends Helper {
 	 * we still need a custom img icon.
 	 *
 	 * @param string $icon (constant or filename)
-	 * @param array $options :
+	 * @param array<string, mixed> $options :
 	 * - translate, title, ...
-	 * @param array $attributes :
+	 * @param array<string, mixed> $attributes :
 	 * - class, ...
 	 * @return string
 	 */
@@ -358,8 +358,8 @@ class FormatHelper extends Helper {
 	 * Renders a font icon.
 	 *
 	 * @param string $type
-	 * @param array $options
-	 * @param array $attributes
+	 * @param array<string, mixed> $options
+	 * @param array<string, mixed> $attributes
 	 * @return string
 	 */
 	protected function _fontIcon($type, $options, $attributes) {
@@ -416,11 +416,11 @@ class FormatHelper extends Helper {
 	 * Displays yes/no symbol.
 	 *
 	 * @param int|bool $value Value
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * - on (defaults to 1/true)
 	 * - onTitle
 	 * - offTitle
-	 * @param array $attributes
+	 * @param array<string, mixed> $attributes
 	 * - title, ...
 	 * @return string HTML icon Yes/No
 	 */
@@ -455,7 +455,9 @@ class FormatHelper extends Helper {
 		if (strpos($domain, 'http') === 0) {
 			// Strip protocol
 			$pieces = parse_url($domain);
-			$domain = $pieces['host'];
+			if ($pieces !== false) {
+				$domain = $pieces['host'];
+			}
 		}
 
 		return 'http://www.google.com/s2/favicons?domain=' . $domain;
@@ -466,7 +468,7 @@ class FormatHelper extends Helper {
 	 * if not available, will return a fallback image (a globe)
 	 *
 	 * @param string $domain (preferably without protocol, e.g. "www.site.com")
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return string
 	 */
 	public function siteIcon($domain, array $options = []) {
@@ -487,7 +489,7 @@ class FormatHelper extends Helper {
 	 * Display a disabled link tag
 	 *
 	 * @param string $text
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return string
 	 */
 	public function disabledLink($text, array $options = []) {
@@ -583,7 +585,7 @@ class FormatHelper extends Helper {
 	 *
 	 * @param mixed $content Output
 	 * @param bool $ok Boolish value
-	 * @param array $attributes
+	 * @param array<string, mixed> $attributes
 	 * @return string Value nicely formatted/colored
 	 */
 	public function ok($content, $ok = false, array $attributes = []) {
@@ -617,13 +619,18 @@ class FormatHelper extends Helper {
 	 * @return string
 	 */
 	public function tab2space($text, $spaces = 4) {
-		$spaces = str_repeat(' ', $spaces);
-		$text = preg_split("/\r\n|\r|\n/", trim($text));
+		$spacesString = str_repeat(' ', $spaces);
+		$splitText = preg_split("/\r\n|\r|\n/", trim($text));
+		if ($splitText === false) {
+			return $text;
+		}
+
 		$wordLengths = [];
 		$wArray = [];
 
 		// Store word lengths
-		foreach ($text as $line) {
+		foreach ($splitText as $line) {
+			/** @var array<int, string> $words */
 			$words = preg_split("/(\t+)/", $line, -1, PREG_SPLIT_DELIM_CAPTURE);
 			foreach (array_keys($words) as $i) {
 				$strlen = strlen($words[$i]);
@@ -646,7 +653,7 @@ class FormatHelper extends Helper {
 					$wArray[$i][$ii] = str_pad($wArray[$i][$ii], $wordLengths[$ii]);
 				}
 			}
-			$text .= str_replace("\t", $spaces, implode('', $wArray[$i])) . "\n";
+			$text .= str_replace("\t", $spacesString, implode('', $wArray[$i])) . "\n";
 		}
 
 		return $text;
@@ -667,8 +674,8 @@ class FormatHelper extends Helper {
 	 * @version 1.3.2
 	 * @link http://aidanlister.com/2004/04/converting-arrays-to-human-readable-tables/
 	 * @param array $array The result (numericaly keyed, associative inner) array.
-	 * @param array $options
-	 * @param array $attributes For the table
+	 * @param array<string, mixed> $options
+	 * @param array<string, mixed> $attributes For the table
 	 * @return string
 	 */
 	public function array2table(array $array, array $options = [], array $attributes = []) {

+ 5 - 5
src/View/Helper/GravatarHelper.php

@@ -77,7 +77,7 @@ class GravatarHelper extends Helper {
 	 * Show gravatar for the supplied email address
 	 *
 	 * @param string $email Email address
-	 * @param array $options Array of options, keyed from default settings
+	 * @param array<string, mixed> $options Array of options, keyed from default settings
 	 * @return string Gravatar image string
 	 */
 	public function image($email, array $options = []) {
@@ -96,7 +96,7 @@ class GravatarHelper extends Helper {
 	 * TODO: rename to avoid E_STRICT errors here
 	 *
 	 * @param string $email Email address
-	 * @param array $options Array of options, keyed from default settings
+	 * @param array<string, mixed> $options Array of options, keyed from default settings
 	 * @return string Gravatar Image URL
 	 */
 	public function url($email, array $options = []) {
@@ -124,7 +124,7 @@ class GravatarHelper extends Helper {
 	/**
 	 * Generate an array of default images for preview purposes
 	 *
-	 * @param array $options Array of options, keyed from default settings
+	 * @param array<string, mixed> $options Array of options, keyed from default settings
 	 * @return array Default images array
 	 */
 	public function defaultImages($options = []) {
@@ -141,7 +141,7 @@ class GravatarHelper extends Helper {
 	/**
 	 * Sanitize the options array
 	 *
-	 * @param array $options Array of options, keyed from default settings
+	 * @param array<string, mixed> $options Array of options, keyed from default settings
 	 * @return array Clean options array
 	 */
 	protected function _cleanOptions($options) {
@@ -179,7 +179,7 @@ class GravatarHelper extends Helper {
 	/**
 	 * Build Options URL string
 	 *
-	 * @param array $options Array of options, keyed from default settings
+	 * @param array<string, mixed> $options Array of options, keyed from default settings
 	 * @return string URL string of options
 	 */
 	protected function _buildOptions($options = []) {

+ 4 - 4
src/View/Helper/MeterHelper.php

@@ -73,8 +73,8 @@ class MeterHelper extends Helper {
 	 * @param float $value
 	 * @param float $max
 	 * @param float|null $min
-	 * @param array $options
-	 * @param array $attributes
+	 * @param array<string, mixed> $options
+	 * @param array<string, mixed> $attributes
 	 * @return string
 	 */
 	public function htmlMeterBar($value, $max, $min = null, array $options = [], array $attributes = []) {
@@ -123,8 +123,8 @@ class MeterHelper extends Helper {
 	 * @param float $max
 	 * @param float $min
 	 * @param int $length As char count
-	 * @param array $options
-	 * @param array $attributes
+	 * @param array<string, mixed> $options
+	 * @param array<string, mixed> $attributes
 	 * @return string
 	 */
 	public function meterBar($value, $max, $min, $length, array $options = [], array $attributes = []) {

+ 3 - 3
src/View/Helper/ProgressHelper.php

@@ -69,8 +69,8 @@ class ProgressHelper extends Helper {
 	 * Options:
 	 *
 	 * @param float $value Value 0...1
-	 * @param array $options
-	 * @param array $attributes
+	 * @param array<string, mixed> $options
+	 * @param array<string, mixed> $attributes
 	 * @return string
 	 */
 	public function htmlProgressBar($value, array $options = [], array $attributes = []) {
@@ -98,7 +98,7 @@ class ProgressHelper extends Helper {
 	/**
 	 * @param float $value Value 0...1
 	 * @param int $length As char count
-	 * @param array $attributes
+	 * @param array<string, mixed> $attributes
 	 * @return string
 	 */
 	public function progressBar($value, $length, array $attributes = []) {

+ 1 - 1
src/View/Helper/QrCodeHelper.php

@@ -119,7 +119,7 @@ class QrCodeHelper extends Helper {
 	 * Note: set size or level manually prior to calling this method
 	 *
 	 * @param string $text Text (utf8 encoded)
-	 * @param array $options Options
+	 * @param array<string, mixed> $options Options
 	 * @return string HTML
 	 */
 	public function image($text, array $options = []) {

+ 2 - 1
src/View/Helper/TextHelper.php

@@ -50,7 +50,7 @@ class TextHelper extends CakeTextHelper {
 	 *
 	 * @param string $url the url
 	 * @param int|null $max the maximum length
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * - placeholder
 	 * @return string the manipulated url (+ eventuell ...)
 	 */
@@ -63,6 +63,7 @@ class TextHelper extends CakeTextHelper {
 		$url = Utility::stripProtocol($url);
 		// cut the parameters
 		if (mb_strpos($url, '/') !== false) {
+			/** @var string $url */
 			$url = strtok($url, '/');
 		}
 		// return if the url is short enough

+ 11 - 7
src/View/Helper/TimeHelper.php

@@ -56,6 +56,7 @@ class TimeHelper extends CakeTimeHelper {
 		parent::__construct($View, $config);
 
 		$config = $this->_config + $defaults;
+		/** @var class-string<\Tools\Utility\Number>|null $engineClass */
 		$engineClass = App::className($config['engine'], 'Utility');
 		if (!$engineClass) {
 			throw new CakeException(sprintf('Class for %s could not be found', $config['engine']));
@@ -72,7 +73,10 @@ class TimeHelper extends CakeTimeHelper {
 	 * @return mixed Whatever is returned by called method, or false on failure
 	 */
 	public function __call($method, $params) {
-		return call_user_func_array([$this->_engine, $method], $params);
+		/** @var callable $callable */
+		$callable = [$this->_engine, $method];
+
+		return call_user_func_array($callable, $params);
 	}
 
 	/**
@@ -117,7 +121,7 @@ class TimeHelper extends CakeTimeHelper {
 	 *
 	 * @param string|null $dateString
 	 * @param string|null $format
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return string
 	 */
 	public function localDateMarkup($dateString = null, $format = null, $options = []) {
@@ -132,7 +136,7 @@ class TimeHelper extends CakeTimeHelper {
 	 *
 	 * @param string|null $dateString
 	 * @param string|null $format
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return string
 	 */
 	public function niceDateMarkup($dateString = null, $format = null, $options = []) {
@@ -147,12 +151,11 @@ class TimeHelper extends CakeTimeHelper {
 	 * // TODO refactor! $userOffset is deprecated!
 	 *
 	 * @param \DateTimeInterface $date Date
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @param array $attr HTML attributes
 	 * @return string Nicely formatted date
 	 */
 	public function published($date, array $options = [], array $attr = []) {
-		$when = null;
 		$whenArray = ['-1' => 'already', '0' => 'today', '1' => 'notyet'];
 		$titles = ['-1' => __d('tools', 'publishedAlready'), '0' => __d('tools', 'publishedToday'), '1' => __d('tools', 'publishedNotYet')];
 
@@ -163,6 +166,7 @@ class TimeHelper extends CakeTimeHelper {
 		// Hack
 		// //TODO: get this to work with datetime - somehow cleaner
 		$timeAttachment = '';
+		$whenOverride = false;
 		if (isset($options['niceDateTime'])) {
 			$timeAttachment = ', ' . $this->format($date, $options['niceDateTime']);
 			$whenOverride = true;
@@ -187,7 +191,7 @@ class TimeHelper extends CakeTimeHelper {
 			$niceDate = $this->format($date, $format) . $timeAttachment; //date("M jS{$y}", $date);
 		}
 
-		if (!empty($whenOverride) && $when === 0) {
+		if ($whenOverride && $when === 0) {
 			if ($this->isInTheFuture($date)) {
 				$when = 1;
 			} else {
@@ -195,7 +199,7 @@ class TimeHelper extends CakeTimeHelper {
 			}
 		}
 
-		if (empty($niceDate)) {
+		if (!$niceDate) {
 			$niceDate = '<i>n/a</i>';
 		} else {
 			if (!isset($attr['title'])) {

+ 2 - 2
src/View/Helper/TimelineHelper.php

@@ -153,7 +153,7 @@ JS;
 	/**
 	 * Format options to JS code
 	 *
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return string
 	 */
 	protected function _options($options) {
@@ -240,7 +240,7 @@ JS;
 	 * - `block` You can chose a different view block to write to (defaults to "script" one).
 	 *
 	 * @param string $script
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 *
 	 * @return void
 	 */

+ 3 - 0
src/View/Helper/TypographyHelper.php

@@ -156,6 +156,9 @@ class TypographyHelper extends Helper {
 		//		Etc...
 		//	}
 		$chunks = preg_split('/(<(?:[^<>]+(?:"[^"]*"|\'[^\']*\')?)+>)/', $str, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+		if ($chunks === false) {
+			return $str;
+		}
 
 		// Build our finalized string. We cycle through the array, skipping tags, and processing the contained text
 		$str = '';

+ 2 - 2
src/View/Helper/UrlTrait.php

@@ -32,7 +32,7 @@ trait UrlTrait {
 	 * Can only add defaults for array URLs.
 	 *
 	 * @param array|string|null $url URL.
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return string Full translated URL with base path.
 	 */
 	public function buildReset($url, array $options = []): string {
@@ -49,7 +49,7 @@ trait UrlTrait {
 	 * Can only add query strings for array URLs.
 	 *
 	 * @param array|string|null $url URL.
-	 * @param array $options
+	 * @param array<string, mixed> $options
 	 * @return string Full translated URL with base path.
 	 */
 	public function buildComplete($url, array $options = []): string {