浏览代码

phpstan fixes.

mscherer 7 年之前
父节点
当前提交
09eb1e6b8f

+ 3 - 0
composer.json

@@ -33,6 +33,7 @@
 		"psr-4": {
 			"Tools\\Test\\": "tests/",
 			"Cake\\Test\\": "vendor/cakephp/cakephp/tests/",
+			"Cake\\PHPStan\\": "vendor/cakephp/cakephp/tests/PHPStan/",
 			"App\\": "tests/test_app/"
 		}
 	},
@@ -44,6 +45,8 @@
 		"issues": "https://github.com/dereuromark/cakephp-tools/issues"
 	},
 	"scripts": {
+		"phpstan": "phpstan analyse -c tests/phpstan.neon -l 3 src/",
+		"phpstan-setup": "cp composer.json composer.backup && composer require --dev phpstan/phpstan:^0.10.1 && mv composer.backup composer.json",
 		"test": "php phpunit.phar",
 		"test-setup": "[ ! -f phpunit.phar ] && wget https://phar.phpunit.de/phpunit-5.7.20.phar && mv phpunit-5.7.20.phar phpunit.phar || true",
 		"test-coverage": "php phpunit.phar --log-junit webroot/coverage/unitreport.xml --coverage-html webroot/coverage --coverage-clover webroot/coverage/coverage.xml",

+ 4 - 4
src/Controller/Component/CommonComponent.php

@@ -25,11 +25,11 @@ class CommonComponent extends Component {
 	 */
 	public function startup(Event $event) {
 		// Data preparation
-		if (!empty($this->Controller->request->data) && !Configure::read('DataPreparation.notrim')) {
-			$this->Controller->request->data = Utility::trimDeep($this->Controller->request->data);
+		if ($this->Controller->request->getData() && !Configure::read('DataPreparation.notrim')) {
+			$this->Controller->request->data = Utility::trimDeep($this->Controller->request->getData());
 		}
-		if (!empty($this->Controller->request->query) && !Configure::read('DataPreparation.notrim')) {
-			$this->Controller->request->query = Utility::trimDeep($this->Controller->request->query);
+		if ($this->Controller->request->getQuery() && !Configure::read('DataPreparation.notrim')) {
+			$this->Controller->request->query = Utility::trimDeep($this->Controller->request->getQuery());
 		}
 		if (!empty($this->Controller->request->params['pass']) && !Configure::read('DataPreparation.notrim')) {
 			$this->Controller->request->params['pass'] = Utility::trimDeep($this->Controller->request->params['pass']);

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

@@ -111,7 +111,7 @@ class UrlComponent extends Component {
 		if (!isset($url['?'])) {
 			$url['?'] = [];
 		}
-		$url['?'] += $this->request->query;
+		$url['?'] += $this->request->getQuery();
 
 		return $url;
 	}

+ 1 - 1
src/Controller/Controller.php

@@ -20,7 +20,7 @@ class Controller extends ShimController {
 	 *   (e.g: Table instance, 'TableName' or a Query object)
 	 * @param array $settings Settings
 	 *
-	 * @return \Cake\ORM\ResultSet Query results
+	 * @return \Cake\ORM\ResultSet|\Cake\Datasource\ResultSetInterface Query results
 	 */
 	public function paginate($object = null, array $settings = []) {
 		$defaultSettings = (array)Configure::read('Paginator');

+ 9 - 4
src/Model/Behavior/BitmaskedBehavior.php

@@ -117,6 +117,10 @@ class BitmaskedBehavior extends Behavior {
 		}
 
 		$mapper = function ($row, $key, $mr) use ($field, $mappedField) {
+			/**
+			 * @var \Cake\Collection\Iterator\MapReduce $mr
+			 * @var \Cake\Datasource\EntityInterface|array $row
+			 */
 			if (!is_object($row)) {
 				if (isset($row[$field])) {
 					$row[$mappedField] = $this->decodeBitmask($row[$field]);
@@ -125,10 +129,11 @@ class BitmaskedBehavior extends Behavior {
 				return;
 			}
 
-			/** @var \Cake\Datasource\EntityInterface $row */
-			$row->set($mappedField, $this->decodeBitmask($row->get($field)));
-			$row->setDirty($mappedField, false);
-			$mr->emit($row);
+			/** @var \Cake\Datasource\EntityInterface $entity */
+			$entity = $row;
+			$entity->set($mappedField, $this->decodeBitmask($entity->get($field)));
+			$entity->setDirty($mappedField, false);
+			$mr->emit($entity);
 		};
 		$query->mapReduce($mapper);
 	}

+ 3 - 2
src/Model/Behavior/TypographicBehavior.php

@@ -98,10 +98,11 @@ class TypographicBehavior extends Behavior {
 	 */
 	public function initialize(array $config = []) {
 		if (empty($this->_config['fields'])) {
-			$schema = $this->getTable()->schema();
+			$schema = $this->getTable()->getSchema();
 
+			$fields = [];
 			foreach ($schema->columns() as $field) {
-				$v = $schema->column($field);
+				$v = $schema->getColumn($field);
 				if (!in_array($v['type'], ['string', 'text'])) {
 					continue;
 				}

+ 18 - 12
src/Model/Table/Table.php

@@ -105,6 +105,8 @@ class Table extends ShimTable {
 	/**
 	 * Get all related entries that have been used so far
 	 *
+	 * @deprecated Must be refactored.
+	 *
 	 * @param string $tableName The related model
 	 * @param string|null $groupField Field to group by
 	 * @param string $type Find type
@@ -287,19 +289,21 @@ class Table extends ShimTable {
 		}
 		$format = !empty($options['dateFormat']) ? $options['dateFormat'] : 'ymd';
 
+		/** @var \Cake\I18n\Time $time */
+		$time = $value;
 		if (!is_object($value)) {
-			$value = new Time($value);
+			$time = new Time($value);
 		}
-		$pieces = $value->format(FORMAT_DB_DATETIME);
+		$pieces = $time->format(FORMAT_DB_DATETIME);
 		$dateTime = explode(' ', $pieces, 2);
-		$date = $dateTime[0];
-		$time = (!empty($dateTime[1]) ? $dateTime[1] : '');
+		$datePart = $dateTime[0];
+		$timePart = (!empty($dateTime[1]) ? $dateTime[1] : '');
 
 		if (!empty($options['allowEmpty']) && (empty($date) && empty($time))) {
 			return true;
 		}
 
-		if (Validation::date($date, $format) && Validation::time($time)) {
+		if (Validation::date($datePart, $format) && Validation::time($timePart)) {
 			// after/before?
 			$seconds = isset($options['min']) ? $options['min'] : 1;
 			if (!empty($options['after'])) {
@@ -324,7 +328,7 @@ class Table extends ShimTable {
 			}
 
 			// We need this for those not using immutable objects just yet
-			$compareValue = clone $value;
+			$compareValue = clone $time;
 
 			if (!empty($options['after'])) {
 				$compare = $compareValue->subSeconds($seconds);
@@ -333,7 +337,7 @@ class Table extends ShimTable {
 				}
 				if (!empty($options['max'])) {
 					$after = $options['after']->addSeconds($options['max']);
-					if ($value->gt($after)) {
+					if ($time->gt($after)) {
 						return false;
 					}
 				}
@@ -345,7 +349,7 @@ class Table extends ShimTable {
 				}
 				if (!empty($options['max'])) {
 					$after = $options['before']->subSeconds($options['max']);
-					if ($value->lt($after)) {
+					if ($time->lt($after)) {
 						return false;
 					}
 				}
@@ -375,10 +379,12 @@ class Table extends ShimTable {
 			return false;
 		}
 		$format = !empty($options['format']) ? $options['format'] : 'ymd';
+
+		$dateTime = $value;
 		if (!is_object($value)) {
-			$value = new Time($value);
+			$dateTime = new Time($value);
 		}
-		$date = $value->format(FORMAT_DB_DATE);
+		$date = $dateTime->format(FORMAT_DB_DATE);
 
 		if (!empty($options['allowEmpty']) && empty($date)) {
 			return true;
@@ -387,7 +393,7 @@ class Table extends ShimTable {
 			// after/before?
 			$days = !empty($options['min']) ? $options['min'] : 0;
 			if (!empty($options['after']) && isset($context['data'][$options['after']])) {
-				$compare = $value->subDays($days);
+				$compare = $dateTime->subDays($days);
 				/** @var \Cake\I18n\Time $after */
 				$after = $context['data'][$options['after']];
 				if (!is_object($after)) {
@@ -398,7 +404,7 @@ class Table extends ShimTable {
 				}
 			}
 			if (!empty($options['before']) && isset($context['data'][$options['before']])) {
-				$compare = $value->addDays($days);
+				$compare = $dateTime->addDays($days);
 				/** @var \Cake\I18n\Time $before */
 				$before = $context['data'][$options['before']];
 				if (!is_object($before)) {

+ 1 - 1
src/Utility/Random.php

@@ -113,7 +113,7 @@ class Random {
 	 * @return string Dob a db (ISO) format datetime string
 	 */
 	public static function dob($min = 18, $max = 100) {
-		$dobYear = date('Y') - (static::int($min, $max));
+		$dobYear = (int)date('Y') - (static::int($min, $max));
 
 		$dobMonth = static::int(1, 12);
 

+ 27 - 75
src/Utility/Time.php

@@ -68,29 +68,6 @@ class Time extends CakeTime {
 	}
 
 	/**
-	 * Gets the timezone that is closest to the given coordinates
-	 *
-	 * @param float $lat
-	 * @param float $lng
-	 * @return \DateTimeZone Timezone object
-	 * @deprecated Would need Geo plugin to work
-	 */
-	public static function timezoneByCoordinates($lat, $lng) {
-		$current = ['timezone' => null, 'distance' => 0];
-		$identifiers = DateTimeZone::listIdentifiers();
-		foreach ($identifiers as $identifier) {
-			$timezone = new DateTimeZone($identifier);
-			$location = $timezone->getLocation();
-			$point = ['lat' => $location['latitude'], 'lng' => $location['longitude']];
-			$distance = (int)Calculator::calculateDistance(compact('lat', 'lng'), $point);
-			if (!$current['distance'] || $distance < $current['distance']) {
-				$current = ['timezone' => $identifier, 'distance' => $distance];
-			}
-		}
-		return $current['timezone'];
-	}
-
-	/**
 	 * Calculate the difference between two dates
 	 *
 	 * TODO: deprecate in favor of DateTime::diff() etc which will be more precise
@@ -177,33 +154,6 @@ class Time extends CakeTime {
 	}
 
 	/**
-	 * Returns age by horoscope info.
-	 *
-	 * @param int $year Year
-	 * @param int $sign Sign
-	 * @return int|array Age
-	 */
-	public static function ageByHoroscope($year, $sign) {
-		App::uses('ZodiacLib', 'Tools.Misc');
-		$Zodiac = new ZodiacLib();
-		$range = $Zodiac->getRange($sign);
-
-		if ($sign == ZodiacLib::SIGN_CAPRICORN) {
-			// undefined
-			return [date('Y') - $year - 1, date('Y') - $year];
-		}
-		if ($range[0][0] > date('m') || ($range[0][0] == date('m') && $range[0][1] > date('d'))) {
-			// not over
-			return date('Y') - $year - 1;
-		}
-		if ($range[1][0] < date('m') || ($range[1][0] == date('m') && $range[1][1] <= date('d'))) {
-			// over
-			return date('Y') - $year;
-		}
-		return [date('Y') - $year - 1, date('Y') - $year];
-	}
-
-	/**
 	 * Rounded age depended on steps (e.g. age 16 with steps = 10 => "11-20")
 	 * //FIXME
 	 * //TODO: move to helper?
@@ -216,18 +166,18 @@ class Time extends CakeTime {
 	 */
 	public static function ageRange($year, $month = null, $day = null, $steps = 1) {
 		if ($month == null && $day == null) {
-			$age = date('Y') - $year - 1;
+			$age = (int)date('Y') - $year - 1;
 		} elseif ($day == null) {
-			if ($month >= date('m')) {
-				$age = date('Y') - $year - 1;
+			if ($month >= (int)date('m')) {
+				$age = (int)date('Y') - $year - 1;
 			} else {
-				$age = date('Y') - $year;
+				$age = (int)date('Y') - $year;
 			}
 		} else {
-			if ($month > date('m') || ($month == date('m') && $day > date('d'))) {
-				$age = date('Y') - $year - 1;
+			if ($month > (int)date('m') || ($month == (int)date('m') && $day > (int)date('d'))) {
+				$age = (int)date('Y') - $year - 1;
 			} else {
-				$age = date('Y') - $year;
+				$age = (int)date('Y') - $year;
 			}
 		}
 		if ($age % $steps == 0) {
@@ -273,8 +223,6 @@ class Time extends CakeTime {
 			list($y, $m, $d) = explode('-', $date[0]);
 			$t = mktime(0, 0, 0, $m, $d, $y);
 		} else {
-			$d = date('d');
-			$m = date('m');
 			$y = date('Y');
 			$t = time();
 		}
@@ -284,12 +232,13 @@ class Time extends CakeTime {
 			$t += WEEK * $relative;	// 1day * 7 * relativeWeeks
 		}
 
-		if (($kw = date('W', $t)) === 0) {
-			$kw = 1 + date($t - DAY * date('w', $t), 'W');
+		$kw = (int)date('W', $t);
+		if ($kw === 0) {
+			$kw = 1 + (int)date($t - DAY * (int)date('w', $t), 'W');
 			$y--;
 		}
 
-		return $kw . '/' . $y;
+		return str_pad($kw, 2, '0', STR_PAD_LEFT) . '/' . $y;
 	}
 
 	/**
@@ -301,7 +250,7 @@ class Time extends CakeTime {
 	 */
 	public static function cWeekMod($num) {
 		$base = 6;
-		return $num - $base * floor($num / $base);
+		return (int)($num - $base * floor($num / $base));
 	}
 
 	/**
@@ -315,7 +264,7 @@ class Time extends CakeTime {
 	public static function cWeekBeginning($year, $cWeek = 0) {
 		if ($cWeek <= 1 || $cWeek > static::cWeeks($year)) {
 			$first = mktime(0, 0, 0, 1, 1, $year);
-			$wtag = date('w', $first);
+			$wtag = (int)date('w', $first);
 
 			if ($wtag <= 4) {
 				/* Thursday or less: back to Monday */
@@ -357,7 +306,7 @@ class Time extends CakeTime {
 		if ($year === null) {
 			$year = date('Y');
 		}
-		return date('W', mktime(23, 59, 59, 12, 28, $year));
+		return (int)date('W', mktime(23, 59, 59, 12, 28, $year));
 	}
 
 	/**
@@ -371,13 +320,14 @@ class Time extends CakeTime {
 	 * @return object DateTime with incremented/decremented month/year values.
 	 */
 	public function incrementDate($startDate, $years = 0, $months = 0, $days = 0, $timezone = null) {
+		$dateTime = $startDate;
 		if (!is_object($startDate)) {
-			$startDate = new CakeTime($startDate);
+			$dateTime = new CakeTime($startDate);
 			if ($timezone) {
-				$startDate->setTimezone($this->safeCreateDateTimeZone($timezone));
+				$dateTime->setTimezone($this->safeCreateDateTimeZone($timezone));
 			}
 		}
-		$startingTimeStamp = $startDate->getTimestamp();
+		$startingTimeStamp = $dateTime->getTimestamp();
 		// Get the month value of the given date:
 		$monthString = date('Y-m', $startingTimeStamp);
 		// Create a date string corresponding to the 1st of the give month,
@@ -410,8 +360,8 @@ class Time extends CakeTime {
 		//TODO: other relative time then today should work as well
 		$Date = new CakeTime($relativeTime !== null ? $relativeTime : 'now');
 
-		$max = mktime(23, 23, 59, $Date->format('m'), $Date->format('d'), $Date->format('Y') - $firstAge);
-		$min = mktime(0, 0, 1, $Date->format('m'), (int)$Date->format('d') + 1, $Date->format('Y') - $secondAge - 1);
+		$max = mktime(23, 23, 59, $Date->format('m'), $Date->format('d'), (int)$Date->format('Y') - $firstAge);
+		$min = mktime(0, 0, 1, $Date->format('m'), (int)$Date->format('d') + 1, (int)$Date->format('Y') - $secondAge - 1);
 
 		if ($returnAsString) {
 			$max = date(FORMAT_DB_DATE, $max);
@@ -935,14 +885,16 @@ class Time extends CakeTime {
 	 * - default, separator
 	 * - boolean zero: if false: 0 days 5 hours => 5 hours etc.
 	 * - verbose/past/future: string with %s or boolean true/false
-	 * @return string
+	 * @return string|array
 	 */
 	public static function relLengthOfTime($date, $format = null, array $options = []) {
+		$dateTime = $date;
 		if ($date !== null && !is_object($date)) {
-			$date = static::parse($date);
+			$dateTime = static::parse($date);
 		}
-		if ($date !== null) {
-			$date = $date->format('U');
+
+		if ($dateTime !== null) {
+			$date = $dateTime->format('U');
 			$sec = time() - $date;
 			$type = ($sec > 0) ? -1 : (($sec < 0) ? 1 : 0);
 			$sec = abs($sec);
@@ -1206,7 +1158,7 @@ class Time extends CakeTime {
 
 		$value = $base + $tmp;
 		if ($pad === null) {
-			return $value;
+			return (string)$value;
 		}
 		return number_format($value, $pad, $decPoint, '');
 	}

+ 1 - 1
src/Utility/Utility.php

@@ -455,7 +455,7 @@ class Utility {
 	/**
 	 * Main deep method
 	 *
-	 * @param callable $function
+	 * @param string|callable $function Callable or function name.
 	 * @param mixed $value
 	 * @return array|string
 	 */

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

@@ -348,8 +348,8 @@ class FormatHelper extends Helper {
 		$translate = isset($options['translate']) ? $options['translate'] : true;
 
 		$type = pathinfo($icon, PATHINFO_FILENAME);
-		$title = isset($t) ? $t : ucfirst($type);
-		$alt = (isset($a) ? $a : Inflector::slug($title));
+		$title = ucfirst($type);
+		$alt = Inflector::slug($title);
 		if ($translate !== false) {
 			$title = __($title);
 			$alt = __($alt);

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

@@ -4,6 +4,7 @@ namespace Tools\View\Helper;
 
 use Cake\Validation\Validation;
 use Cake\View\Helper;
+use Cake\View\View;
 
 /**
  * CakePHP Gravatar Helper
@@ -62,10 +63,10 @@ class GravatarHelper extends Helper {
 	public $helpers = ['Html'];
 
 	/**
-	 * @param \Cake\View\View|null $View
+	 * @param \Cake\View\View $View
 	 * @param array $config
 	 */
-	public function __construct($View = null, $config = []) {
+	public function __construct(View $View, array $config = []) {
 		// Default the secure option to match the current URL.
 		$this->_defaultConfig['secure'] = (bool)env('HTTPS');
 
@@ -79,7 +80,7 @@ class GravatarHelper extends Helper {
 	 * @param array $options Array of options, keyed from default settings
 	 * @return string Gravatar image string
 	 */
-	public function image($email, $options = []) {
+	public function image($email, array $options = []) {
 		$imageUrl = $this->url($email, $options);
 		unset($options['default'], $options['size'], $options['rating'], $options['ext']);
 		return $this->Html->image($imageUrl, $options);
@@ -90,10 +91,10 @@ class GravatarHelper extends Helper {
 	 * TODO: rename to avoid E_STRICT errors here
 	 *
 	 * @param string $email Email address
-	 * @param string|array $options Array of options, keyed from default settings
+	 * @param array $options Array of options, keyed from default settings
 	 * @return string Gravatar Image URL
 	 */
-	public function url($email, $options = []) {
+	public function url($email, array $options = []) {
 		$options = $this->_cleanOptions($options + $this->_config);
 		$ext = $options['ext'];
 		$secure = $options['secure'];

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

@@ -95,7 +95,7 @@ class HtmlHelper extends CoreHtmlHelper {
 			if (!isset($url['?'])) {
 				$url['?'] = [];
 			}
-			$url['?'] += $this->request->query;
+			$url['?'] += $this->request->getQuery();
 		}
 		return parent::link($title, $url, $options);
 	}

+ 6 - 5
src/View/Helper/NumberHelper.php

@@ -4,6 +4,7 @@ namespace Tools\View\Helper;
 
 use Cake\Utility\Hash;
 use Cake\View\Helper\NumberHelper as CakeNumberHelper;
+use Cake\View\View;
 
 /**
  * Ovewrite to allow usage of own Number class.
@@ -20,13 +21,13 @@ class NumberHelper extends CakeNumberHelper {
 	 * - `engine` Class name to use to replace Number functionality.
 	 *            The class needs to be placed in the `Utility` directory.
 	 *
-	 * @param \Cake\View\View|null $View The View this helper is being attached to.
-	 * @param array $options Configuration settings for the helper
+	 * @param \Cake\View\View $View The View this helper is being attached to.
+	 * @param array $config Configuration settings for the helper
 	 * @throws \Cake\Core\Exception\Exception When the engine class could not be found.
 	 */
-	public function __construct($View = null, $options = []) {
-		$options = Hash::merge(['engine' => 'Tools.Number'], $options);
-		parent::__construct($View, $options);
+	public function __construct(View $View, array $config = []) {
+		$config = Hash::merge(['engine' => 'Tools.Number'], $config);
+		parent::__construct($View, $config);
 	}
 
 }

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

@@ -4,10 +4,10 @@ namespace Tools\View\Helper;
 
 use Cake\Core\App;
 use Cake\Core\Configure;
+use Cake\Core\Exception\Exception;
 use Cake\View\Helper\TimeHelper as CakeTimeHelper;
 use Cake\View\View;
 use DateTime;
-use RuntimeException;
 
 /**
  * Wrapper for TimeHelper and TimeLib
@@ -60,7 +60,7 @@ class TimeHelper extends CakeTimeHelper {
 
 		$engineClass = App::className($config['engine'], 'Utility');
 		if (!$engineClass) {
-			throw new RuntimeException(sprintf('Class for %s could not be found', $config['engine']));
+			throw new Exception(sprintf('Class for %s could not be found', $config['engine']));
 		}
 
 		$this->_engine = new $engineClass($config);

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

@@ -216,7 +216,7 @@ JS;
 		$datePieces = [];
 		$datePieces[] = $date->format('Y');
 		// JavaScript uses 0-indexed months, so we need to subtract 1 month from PHP's output
-		$datePieces[] = (int)($date->format('m') - 1);
+		$datePieces[] = (int)($date->format('m')) - 1;
 		$datePieces[] = (int)$date->format('d');
 		$datePieces[] = (int)$date->format('H');
 		$datePieces[] = (int)$date->format('i');

+ 12 - 6
src/View/Helper/TypographyHelper.php

@@ -81,6 +81,13 @@ class TypographyHelper extends Helper {
 	];
 
 	/**
+	 * Cache definitions.
+	 *
+	 * @var array|null
+	 */
+	protected $table;
+
+	/**
 	 * Automatically uses the typography specified.
 	 * By default, uses Configure::read('App.language') to determine locale preference.
 	 * It will then try to match the language to the type of characters used.
@@ -268,7 +275,6 @@ class TypographyHelper extends Helper {
 	 * @return string
 	 */
 	public function formatCharacters($str, $locale = null) {
-		//static $table;
 		if ($locale === null) {
 			$locale = Configure::read('Typography.locale');
 		}
@@ -301,8 +307,8 @@ class TypographyHelper extends Helper {
 			],
 		];
 
-		if (!isset($table)) {
-			$table = [
+		if (!isset($this->table)) {
+			$this->table = [
 				// nested smart quotes, opening and closing
 				// note that rules for grammar (English) allow only for two levels deep
 				// and that single quotes are _supposed_ to always be on the outside
@@ -345,13 +351,13 @@ class TypographyHelper extends Helper {
 				'/&(?!#?[a-zA-Z0-9]{2,};)/'		=> '&amp;'
 			];
 			if ($locale && !empty($locales[$locale])) {
-				foreach ($table as $key => $val) {
-					$table[$key] = str_replace($locales['default'], $locales[$locale], $val);
+				foreach ($this->table as $key => $val) {
+					$this->table[$key] = str_replace($locales['default'], $locales[$locale], $val);
 				}
 			}
 		}
 
-		return preg_replace(array_keys($table), $table, $str);
+		return preg_replace(array_keys($this->table), $this->table, $str);
 	}
 
 	/**

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

@@ -99,7 +99,7 @@ class UrlHelper extends CoreUrlHelper {
 		if (!isset($url['?'])) {
 			$url['?'] = [];
 		}
-		$url['?'] += $this->request->query;
+		$url['?'] += $this->request->getQuery();
 
 		return $url;
 	}

+ 7 - 2
tests/phpstan.neon

@@ -4,7 +4,12 @@ parameters:
 	excludes_analyse:
 		- %rootDir%/../../../src/TestSuite/*
 		- %rootDir%/../../../src/View/Helper/TreeHelper
+		- %rootDir%/../../../src/Utility/Mime
 	ignoreErrors:
-		- '#Call to an undefined method Cake\\Controller\\Component\\FlashComponent::success\(\)#'
+		- '#Access to an undefined property .+Table::\$belongsTo#'
+		- '#Call to an undefined method .+TimeHelper::.+\(\)#'
 		- '#Tools\\Utility\\Mime::\_\_construct\(\) does not call parent constructor from Cake\\Http\\Response#'
-		- '#Undefined variable: \$i#'
+
+services:
+    -
+        class: Cake\PHPStan\AssociationTableMixinClassReflectionExtension