Updated Rector to commit e8bba86e071c527045951c0ed5edd852d80776af

e8bba86e07 Fix string type on StrictArrayParamDimFetchRector (#4623)
This commit is contained in:
Tomas Votruba 2023-07-30 17:06:57 +00:00
parent 3598a7608a
commit d9fb45c92e
29 changed files with 326 additions and 253 deletions

View File

@ -107,6 +107,11 @@ CODE_SAMPLE
if (!$this->isName($node->var, $paramName)) {
return null;
}
// skip possible strings
$variableType = $this->getType($node->var);
if ($variableType->isString()->yes()) {
return null;
}
$isParamAccessedArrayDimFetch = \true;
return null;
});

View File

@ -19,12 +19,12 @@ final class VersionResolver
* @api
* @var string
*/
public const PACKAGE_VERSION = '8c623868b748427ce186a1b3b892a7bca9860483';
public const PACKAGE_VERSION = 'e8bba86e071c527045951c0ed5edd852d80776af';
/**
* @api
* @var string
*/
public const RELEASE_DATE = '2023-07-30 00:28:40';
public const RELEASE_DATE = '2023-07-30 18:03:23';
/**
* @var int
*/

2
vendor/autoload.php vendored
View File

@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) {
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInitdfa948fbfb833ea04df2eda35fb17026::getLoader();
return ComposerAutoloaderInit1fdf8cda4f009d53c0b435a0a24d5641::getLoader();

View File

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInitdfa948fbfb833ea04df2eda35fb17026
class ComposerAutoloaderInit1fdf8cda4f009d53c0b435a0a24d5641
{
private static $loader;
@ -22,17 +22,17 @@ class ComposerAutoloaderInitdfa948fbfb833ea04df2eda35fb17026
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInitdfa948fbfb833ea04df2eda35fb17026', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInit1fdf8cda4f009d53c0b435a0a24d5641', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInitdfa948fbfb833ea04df2eda35fb17026', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInit1fdf8cda4f009d53c0b435a0a24d5641', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitdfa948fbfb833ea04df2eda35fb17026::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInit1fdf8cda4f009d53c0b435a0a24d5641::getInitializer($loader));
$loader->setClassMapAuthoritative(true);
$loader->register(true);
$filesToLoad = \Composer\Autoload\ComposerStaticInitdfa948fbfb833ea04df2eda35fb17026::$files;
$filesToLoad = \Composer\Autoload\ComposerStaticInit1fdf8cda4f009d53c0b435a0a24d5641::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;

View File

@ -4,7 +4,7 @@
namespace Composer\Autoload;
class ComposerStaticInitdfa948fbfb833ea04df2eda35fb17026
class ComposerStaticInit1fdf8cda4f009d53c0b435a0a24d5641
{
public static $files = array (
'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php',
@ -3023,9 +3023,9 @@ class ComposerStaticInitdfa948fbfb833ea04df2eda35fb17026
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInitdfa948fbfb833ea04df2eda35fb17026::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitdfa948fbfb833ea04df2eda35fb17026::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitdfa948fbfb833ea04df2eda35fb17026::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInit1fdf8cda4f009d53c0b435a0a24d5641::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit1fdf8cda4f009d53c0b435a0a24d5641::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit1fdf8cda4f009d53c0b435a0a24d5641::$classMap;
}, null, ClassLoader::class);
}

View File

@ -680,21 +680,21 @@
},
{
"name": "nette\/utils",
"version": "v3.2.9",
"version_normalized": "3.2.9.0",
"version": "v3.2.10",
"version_normalized": "3.2.10.0",
"source": {
"type": "git",
"url": "https:\/\/github.com\/nette\/utils.git",
"reference": "c91bac3470c34b2ecd5400f6e6fdf0b64a836a5c"
"reference": "a4175c62652f2300c8017fb7e640f9ccb11648d2"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/nette\/utils\/zipball\/c91bac3470c34b2ecd5400f6e6fdf0b64a836a5c",
"reference": "c91bac3470c34b2ecd5400f6e6fdf0b64a836a5c",
"url": "https:\/\/api.github.com\/repos\/nette\/utils\/zipball\/a4175c62652f2300c8017fb7e640f9ccb11648d2",
"reference": "a4175c62652f2300c8017fb7e640f9ccb11648d2",
"shasum": ""
},
"require": {
"php": ">=7.2 <8.3"
"php": ">=7.2 <8.4"
},
"conflict": {
"nette\/di": "<3.0.6"
@ -714,7 +714,7 @@
"ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()",
"ext-xml": "to use Strings::length() etc. when mbstring is not available"
},
"time": "2023-01-18T03:26:20+00:00",
"time": "2023-07-30T15:38:18+00:00",
"type": "library",
"extra": {
"branch-alias": {
@ -763,7 +763,7 @@
],
"support": {
"issues": "https:\/\/github.com\/nette\/utils\/issues",
"source": "https:\/\/github.com\/nette\/utils\/tree\/v3.2.9"
"source": "https:\/\/github.com\/nette\/utils\/tree\/v3.2.10"
},
"install-path": "..\/nette\/utils"
},
@ -2267,17 +2267,17 @@
},
{
"name": "symfony\/config",
"version": "v6.3.0",
"version_normalized": "6.3.0.0",
"version": "v6.3.2",
"version_normalized": "6.3.2.0",
"source": {
"type": "git",
"url": "https:\/\/github.com\/symfony\/config.git",
"reference": "a5e00dec161b08c946a2c16eed02adbeedf827ae"
"reference": "b47ca238b03e7b0d7880ffd1cf06e8d637ca1467"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/symfony\/config\/zipball\/a5e00dec161b08c946a2c16eed02adbeedf827ae",
"reference": "a5e00dec161b08c946a2c16eed02adbeedf827ae",
"url": "https:\/\/api.github.com\/repos\/symfony\/config\/zipball\/b47ca238b03e7b0d7880ffd1cf06e8d637ca1467",
"reference": "b47ca238b03e7b0d7880ffd1cf06e8d637ca1467",
"shasum": ""
},
"require": {
@ -2297,7 +2297,7 @@
"symfony\/service-contracts": "^2.5|^3",
"symfony\/yaml": "^5.4|^6.0"
},
"time": "2023-04-25T10:46:17+00:00",
"time": "2023-07-19T20:22:16+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -2325,7 +2325,7 @@
"description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
"homepage": "https:\/\/symfony.com",
"support": {
"source": "https:\/\/github.com\/symfony\/config\/tree\/v6.3.0"
"source": "https:\/\/github.com\/symfony\/config\/tree\/v6.3.2"
},
"funding": [
{
@ -2345,17 +2345,17 @@
},
{
"name": "symfony\/console",
"version": "v6.3.0",
"version_normalized": "6.3.0.0",
"version": "v6.3.2",
"version_normalized": "6.3.2.0",
"source": {
"type": "git",
"url": "https:\/\/github.com\/symfony\/console.git",
"reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7"
"reference": "aa5d64ad3f63f2e48964fc81ee45cb318a723898"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/symfony\/console\/zipball\/8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7",
"reference": "8788808b07cf0bdd6e4b7fdd23d8ddb1470c83b7",
"url": "https:\/\/api.github.com\/repos\/symfony\/console\/zipball\/aa5d64ad3f63f2e48964fc81ee45cb318a723898",
"reference": "aa5d64ad3f63f2e48964fc81ee45cb318a723898",
"shasum": ""
},
"require": {
@ -2384,7 +2384,7 @@
"symfony\/process": "^5.4|^6.0",
"symfony\/var-dumper": "^5.4|^6.0"
},
"time": "2023-05-29T12:49:39+00:00",
"time": "2023-07-19T20:17:28+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -2418,7 +2418,7 @@
"terminal"
],
"support": {
"source": "https:\/\/github.com\/symfony\/console\/tree\/v6.3.0"
"source": "https:\/\/github.com\/symfony\/console\/tree\/v6.3.2"
},
"funding": [
{
@ -2696,17 +2696,17 @@
},
{
"name": "symfony\/finder",
"version": "v6.3.0",
"version_normalized": "6.3.0.0",
"version": "v6.3.2",
"version_normalized": "6.3.2.0",
"source": {
"type": "git",
"url": "https:\/\/github.com\/symfony\/finder.git",
"reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2"
"reference": "78ce4c29757d657d2b41a91c328923b9a0d6b43d"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/symfony\/finder\/zipball\/d9b01ba073c44cef617c7907ce2419f8d00d75e2",
"reference": "d9b01ba073c44cef617c7907ce2419f8d00d75e2",
"url": "https:\/\/api.github.com\/repos\/symfony\/finder\/zipball\/78ce4c29757d657d2b41a91c328923b9a0d6b43d",
"reference": "78ce4c29757d657d2b41a91c328923b9a0d6b43d",
"shasum": ""
},
"require": {
@ -2715,7 +2715,7 @@
"require-dev": {
"symfony\/filesystem": "^6.0"
},
"time": "2023-04-02T01:25:41+00:00",
"time": "2023-07-13T14:29:38+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -2743,7 +2743,7 @@
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https:\/\/symfony.com",
"support": {
"source": "https:\/\/github.com\/symfony\/finder\/tree\/v6.3.0"
"source": "https:\/\/github.com\/symfony\/finder\/tree\/v6.3.2"
},
"funding": [
{
@ -2936,23 +2936,23 @@
},
{
"name": "symfony\/process",
"version": "v6.3.0",
"version_normalized": "6.3.0.0",
"version": "v6.3.2",
"version_normalized": "6.3.2.0",
"source": {
"type": "git",
"url": "https:\/\/github.com\/symfony\/process.git",
"reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628"
"reference": "c5ce962db0d9b6e80247ca5eb9af6472bd4d7b5d"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/symfony\/process\/zipball\/8741e3ed7fe2e91ec099e02446fb86667a0f1628",
"reference": "8741e3ed7fe2e91ec099e02446fb86667a0f1628",
"url": "https:\/\/api.github.com\/repos\/symfony\/process\/zipball\/c5ce962db0d9b6e80247ca5eb9af6472bd4d7b5d",
"reference": "c5ce962db0d9b6e80247ca5eb9af6472bd4d7b5d",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"time": "2023-05-19T08:06:44+00:00",
"time": "2023-07-12T16:00:22+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -2980,7 +2980,7 @@
"description": "Executes commands in sub-processes",
"homepage": "https:\/\/symfony.com",
"support": {
"source": "https:\/\/github.com\/symfony\/process\/tree\/v6.3.0"
"source": "https:\/\/github.com\/symfony\/process\/tree\/v6.3.2"
},
"funding": [
{
@ -3000,17 +3000,17 @@
},
{
"name": "symfony\/string",
"version": "v6.3.0",
"version_normalized": "6.3.0.0",
"version": "v6.3.2",
"version_normalized": "6.3.2.0",
"source": {
"type": "git",
"url": "https:\/\/github.com\/symfony\/string.git",
"reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f"
"reference": "53d1a83225002635bca3482fcbf963001313fb68"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/symfony\/string\/zipball\/f2e190ee75ff0f5eced645ec0be5c66fac81f51f",
"reference": "f2e190ee75ff0f5eced645ec0be5c66fac81f51f",
"url": "https:\/\/api.github.com\/repos\/symfony\/string\/zipball\/53d1a83225002635bca3482fcbf963001313fb68",
"reference": "53d1a83225002635bca3482fcbf963001313fb68",
"shasum": ""
},
"require": {
@ -3030,7 +3030,7 @@
"symfony\/translation-contracts": "^2.5|^3.0",
"symfony\/var-exporter": "^5.4|^6.0"
},
"time": "2023-03-21T21:06:29+00:00",
"time": "2023-07-05T08:41:27+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -3069,7 +3069,7 @@
"utf8"
],
"support": {
"source": "https:\/\/github.com\/symfony\/string\/tree\/v6.3.0"
"source": "https:\/\/github.com\/symfony\/string\/tree\/v6.3.2"
},
"funding": [
{
@ -3222,23 +3222,23 @@
},
{
"name": "tracy\/tracy",
"version": "v2.10.2",
"version_normalized": "2.10.2.0",
"version": "v2.10.3",
"version_normalized": "2.10.3.0",
"source": {
"type": "git",
"url": "https:\/\/github.com\/nette\/tracy.git",
"reference": "882fee7cf4258a602ad4a37461e837ed2ca1406b"
"reference": "ec6637866d6836ef6f8de2bab63ae7708b23bcd7"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/nette\/tracy\/zipball\/882fee7cf4258a602ad4a37461e837ed2ca1406b",
"reference": "882fee7cf4258a602ad4a37461e837ed2ca1406b",
"url": "https:\/\/api.github.com\/repos\/nette\/tracy\/zipball\/ec6637866d6836ef6f8de2bab63ae7708b23bcd7",
"reference": "ec6637866d6836ef6f8de2bab63ae7708b23bcd7",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-session": "*",
"php": ">=8.0 <8.3"
"php": ">=8.0 <8.4"
},
"conflict": {
"nette\/di": "<3.0"
@ -3246,13 +3246,14 @@
"require-dev": {
"latte\/latte": "^2.5",
"nette\/di": "^3.0",
"nette\/http": "^3.0",
"nette\/mail": "^3.0",
"nette\/tester": "^2.2",
"nette\/utils": "^3.0",
"phpstan\/phpstan": "^1.0",
"psr\/log": "^1.0 || ^2.0 || ^3.0"
},
"time": "2023-03-29T12:34:53+00:00",
"time": "2023-07-30T13:56:20+00:00",
"type": "library",
"extra": {
"branch-alias": {
@ -3293,7 +3294,7 @@
],
"support": {
"issues": "https:\/\/github.com\/nette\/tracy\/issues",
"source": "https:\/\/github.com\/nette\/tracy\/tree\/v2.10.2"
"source": "https:\/\/github.com\/nette\/tracy\/tree\/v2.10.3"
},
"install-path": "..\/tracy\/tracy"
},

File diff suppressed because one or more lines are too long

View File

@ -34,7 +34,7 @@
}
],
"require": {
"php": ">=7.2 <8.3"
"php": ">=7.2 <8.4"
},
"require-dev": {
"nette\/tester": "~2.0",

View File

@ -39,7 +39,7 @@ The recommended way to install is via Composer:
composer require nette/utils
```
- Nette Utils 3.2 is compatible with PHP 7.2 to 8.2
- Nette Utils 3.2 is compatible with PHP 7.2 to 8.3
- Nette Utils 3.1 is compatible with PHP 7.1 to 8.0
- Nette Utils 3.0 is compatible with PHP 7.1 to 8.0
- Nette Utils 2.5 is compatible with PHP 5.6 to 8.0

View File

@ -11,6 +11,8 @@ use RectorPrefix202307\Nette;
/**
* Provides objects to work as array.
* @template T
* @implements \RecursiveArrayIterator<array-key, T>
* @implements \ArrayAccess<array-key, T>
*/
class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \IteratorAggregate
{
@ -44,7 +46,7 @@ class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \Iterator
}
/**
* Replaces or appends a item.
* @param string|int $key
* @param array-key $key
* @param T $value
*/
public function offsetSet($key, $value) : void
@ -57,7 +59,7 @@ class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \Iterator
}
/**
* Returns a item.
* @param string|int $key
* @param array-key $key
* @return T
*/
#[\ReturnTypeWillChange]
@ -67,7 +69,7 @@ class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \Iterator
}
/**
* Determines whether a item exists.
* @param string|int $key
* @param array-key $key
*/
public function offsetExists($key) : bool
{
@ -75,7 +77,7 @@ class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \Iterator
}
/**
* Removes the element from this list.
* @param string|int $key
* @param array-key $key
*/
public function offsetUnset($key) : void
{

View File

@ -11,6 +11,8 @@ use RectorPrefix202307\Nette;
/**
* Provides the base class for a generic list (items can be accessed by index).
* @template T
* @implements \IteratorAggregate<int, T>
* @implements \ArrayAccess<int, T>
*/
class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
{
@ -19,7 +21,7 @@ class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
private $list = [];
/**
* Transforms array to ArrayList.
* @param array<T> $array
* @param list<T> $array
* @return static
*/
public static function from(array $array)

View File

@ -137,11 +137,12 @@ final class Callback
public static function unwrap(\Closure $closure)
{
$r = new \ReflectionFunction($closure);
$class = $r->getClosureScopeClass();
if (\substr($r->name, -1) === '}') {
return $closure;
} elseif ($obj = $r->getClosureThis()) {
} elseif (($obj = $r->getClosureThis()) && $class && \get_class($obj) === $class->name) {
return [$obj, $r->name];
} elseif ($class = $r->getClosureScopeClass()) {
} elseif ($class) {
return [$class->name, $r->name];
} else {
return $r->name;

View File

@ -15,7 +15,7 @@ final class FileSystem
{
use Nette\StaticClass;
/**
* Creates a directory if it doesn't exist.
* Creates a directory if it does not exist, including parent directories.
* @throws Nette\IOException on error occurred
*/
public static function createDir(string $dir, int $mode = 0777) : void
@ -26,7 +26,7 @@ final class FileSystem
}
}
/**
* Copies a file or a directory. Overwrites existing files and directories by default.
* Copies a file or an entire directory. Overwrites existing files and directories by default.
* @throws Nette\IOException on error occurred
* @throws Nette\InvalidStateException if $overwrite is set to false and destination already exists
*/
@ -57,7 +57,7 @@ final class FileSystem
}
}
/**
* Deletes a file or directory if exists.
* Deletes a file or an entire directory if exists. If the directory is not empty, it deletes its contents first.
* @throws Nette\IOException on error occurred
*/
public static function delete(string $path) : void
@ -130,7 +130,8 @@ final class FileSystem
}
}
/**
* Fixes permissions to a specific file or directory. Directories can be fixed recursively.
* Sets file permissions to `$fileMode` or directory permissions to `$dirMode`.
* Recursively traverses and sets permissions on the entire contents of the directory as well.
* @throws Nette\IOException on error occurred
*/
public static function makeWritable(string $path, int $dirMode = 0777, int $fileMode = 0666) : void

View File

@ -86,22 +86,22 @@ use RectorPrefix202307\Nette;
* @method void stringUp($font, $x, $y, string $s, $col)
* @method void trueColorToPalette(bool $dither, $ncolors)
* @method array ttfText($size, $angle, $x, $y, $color, string $fontfile, string $text)
* @property-read int $width
* @property-read int $height
* @property-read positive-int $width
* @property-read positive-int $height
* @property-read resource|\GdImage $imageResource
*/
class Image
{
use Nette\SmartObject;
/** {@link resize()} only shrinks images */
/** Prevent from getting resized to a bigger size than the original */
public const SHRINK_ONLY = 0b1;
/** {@link resize()} will ignore aspect ratio */
/** Resizes to a specified width and height without keeping aspect ratio */
public const STRETCH = 0b10;
/** {@link resize()} fits in given area so its dimensions are less than or equal to the required dimensions */
/** Resizes to fit into a specified width and height and preserves aspect ratio */
public const FIT = 0b0;
/** {@link resize()} fills given area so its dimensions are greater than or equal to the required dimensions */
/** Resizes while bounding the smaller dimension to the specified width or height and preserves aspect ratio */
public const FILL = 0b100;
/** {@link resize()} fills given area exactly */
/** Resizes to the smallest possible size to completely cover specified width and height and reserves aspect ratio */
public const EXACT = 0b1000;
/** image types */
public const JPEG = \IMAGETYPE_JPEG, PNG = \IMAGETYPE_PNG, GIF = \IMAGETYPE_GIF, WEBP = \IMAGETYPE_WEBP, AVIF = 19, BMP = \IMAGETYPE_BMP;
@ -165,6 +165,8 @@ class Image
}
/**
* Creates a new true color image of the given dimensions. The default color is black.
* @param positive-int $width
* @param positive-int $height
* @return static
* @throws Nette\NotSupportedException if gd extension is not loaded
*/
@ -244,6 +246,7 @@ class Image
}
/**
* Returns image width.
* @return positive-int
*/
public function getWidth() : int
{
@ -251,6 +254,7 @@ class Image
}
/**
* Returns image height.
* @return positive-int
*/
public function getHeight() : int
{
@ -428,7 +432,7 @@ class Image
* Puts another image into this image.
* @param int|string $left in pixels or percent
* @param int|string $top in pixels or percent
* @param int $opacity 0..100
* @param int<0, 100> $opacity 0..100
* @return static
*/
public function place(self $image, $left = 0, $top = 0, int $opacity = 100)

View File

@ -11,6 +11,7 @@ use RectorPrefix202307\Nette;
use RectorPrefix202307\Nette\MemberAccessException;
/**
* Nette\SmartObject helpers.
* @internal
*/
final class ObjectHelpers
{

View File

@ -14,17 +14,17 @@ use RectorPrefix202307\Nette;
* @property int $page
* @property-read int $firstPage
* @property-read int|null $lastPage
* @property-read int $firstItemOnPage
* @property-read int $lastItemOnPage
* @property-read int<0,max> $firstItemOnPage
* @property-read int<0,max> $lastItemOnPage
* @property int $base
* @property-read bool $first
* @property-read bool $last
* @property-read int|null $pageCount
* @property int $itemsPerPage
* @property int|null $itemCount
* @property-read int $offset
* @property-read int|null $countdownOffset
* @property-read int $length
* @property-read int<0,max>|null $pageCount
* @property positive-int $itemsPerPage
* @property int<0,max>|null $itemCount
* @property-read int<0,max> $offset
* @property-read int<0,max>|null $countdownOffset
* @property-read int<0,max> $length
*/
class Paginator
{
@ -69,6 +69,7 @@ class Paginator
}
/**
* Returns the sequence number of the first element on the page
* @return int<0, max>
*/
public function getFirstItemOnPage() : int
{
@ -76,6 +77,7 @@ class Paginator
}
/**
* Returns the sequence number of the last element on the page
* @return int<0, max>
*/
public function getLastItemOnPage() : int
{
@ -99,6 +101,7 @@ class Paginator
}
/**
* Returns zero-based page number.
* @return int<0, max>
*/
protected function getPageIndex() : int
{
@ -121,6 +124,7 @@ class Paginator
}
/**
* Returns the total number of pages.
* @return int<0, max>|null
*/
public function getPageCount() : ?int
{
@ -137,6 +141,7 @@ class Paginator
}
/**
* Returns the number of items to display on a single page.
* @return positive-int
*/
public function getItemsPerPage() : int
{
@ -153,6 +158,7 @@ class Paginator
}
/**
* Returns the total number of items.
* @return int<0, max>|null
*/
public function getItemCount() : ?int
{
@ -160,6 +166,7 @@ class Paginator
}
/**
* Returns the absolute index of the first item on current page.
* @return int<0, max>
*/
public function getOffset() : int
{
@ -167,6 +174,7 @@ class Paginator
}
/**
* Returns the absolute index of the first item on current page in countdown paging.
* @return int<0, max>|null
*/
public function getCountdownOffset() : ?int
{
@ -174,6 +182,7 @@ class Paginator
}
/**
* Returns the number of items on current page.
* @return int<0, max>
*/
public function getLength() : int
{

View File

@ -32,6 +32,7 @@ final class Reflection
* Returns the type of return value of given function or method and normalizes `self`, `static`, and `parent` to actual class names.
* If the function does not have a return type, it returns null.
* If the function has union or intersection type, it throws Nette\InvalidStateException.
* @deprecated use Nette\Utils\Type::fromReflection()
*/
public static function getReturnType(\ReflectionFunctionAbstract $func) : ?string
{
@ -50,6 +51,7 @@ final class Reflection
* Returns the type of given parameter and normalizes `self` and `parent` to the actual class names.
* If the parameter does not have a type, it returns null.
* If the parameter has union or intersection type, it throws Nette\InvalidStateException.
* @deprecated use Nette\Utils\Type::fromReflection()
*/
public static function getParameterType(\ReflectionParameter $param) : ?string
{
@ -67,6 +69,7 @@ final class Reflection
* Returns the type of given property and normalizes `self` and `parent` to the actual class names.
* If the property does not have a type, it returns null.
* If the property has union or intersection type, it throws Nette\InvalidStateException.
* @deprecated use Nette\Utils\Type::fromReflection()
*/
public static function getPropertyType(\ReflectionProperty $prop) : ?string
{
@ -214,7 +217,7 @@ final class Reflection
return $name;
}
}
/** @return array of [alias => class] */
/** @return array<string, class-string> of [alias => class] */
public static function getUseStatements(\ReflectionClass $class) : array
{
if ($class->isAnonymous()) {
@ -242,7 +245,8 @@ final class Reflection
\trigger_error($e->getMessage(), \E_USER_NOTICE);
$tokens = [];
}
$namespace = $class = $classLevel = $level = null;
$namespace = $class = null;
$classLevel = $level = 0;
$res = $uses = [];
$nameTokens = \PHP_VERSION_ID < 80000 ? [\T_STRING, \T_NS_SEPARATOR] : [\T_STRING, \T_NS_SEPARATOR, \T_NAME_QUALIFIED, \T_NAME_FULLY_QUALIFIED];
while ($token = \current($tokens)) {
@ -298,7 +302,7 @@ final class Reflection
break;
case '}':
if ($level === $classLevel) {
$class = $classLevel = null;
$class = $classLevel = 0;
}
$level--;
}

View File

@ -309,6 +309,7 @@ class Strings
}
/**
* Pads a UTF-8 string to given length by prepending the $pad string to the beginning.
* @param non-empty-string $pad
*/
public static function padLeft(string $s, int $length, string $pad = ' ') : string
{
@ -318,6 +319,7 @@ class Strings
}
/**
* Pads UTF-8 string to given length by appending the $pad string to the end.
* @param non-empty-string $pad
*/
public static function padRight(string $s, int $length, string $pad = ' ') : string
{

View File

@ -218,6 +218,7 @@ class Validators
* Checks if a variable is a zero-based integer indexed array.
* @param mixed $value
* @deprecated use Nette\Utils\Arrays::isList
* @return ($value is list ? true : false)
*/
public static function isList($value) : bool
{

View File

@ -208,8 +208,8 @@ class Finder implements \IteratorAggregate, \Countable
*
* You can use patterns (delimited with / sign), globs or simple strings.
*
* $finder->name('*.php')
* $finder->name('/\.php$/') // same as above
* $finder->name('/\.php$/')
* $finder->name('*.php') // same as above, without dot files
* $finder->name('test.php')
* $finder->name(['test.py', 'test.php'])
*

View File

@ -63,7 +63,7 @@ class ExcludeDirectoryFilterIterator extends \FilterIterator implements \Recursi
*/
public function accept() : bool
{
if ($this->isRecursive && isset($this->excludedDirs[$this->getFilename()]) && $this->isDir()) {
if (isset($this->excludedDirs[$this->getFilename()]) && $this->hasChildren()) {
return \false;
}
if ($this->excludedPattern) {

View File

@ -26,9 +26,9 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
*/
private $ignoreUnreadableDirs;
/**
* @var bool|null
* @var bool
*/
private $rewindable;
private $ignoreFirstRewind = \true;
// these 3 properties take part of the performance optimization to avoid redoing the same work in all iterations
/**
* @var string
@ -101,7 +101,6 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
// parent method will call the constructor with default arguments, so unreadable dirs won't be ignored anymore
$children->ignoreUnreadableDirs = $this->ignoreUnreadableDirs;
// performance optimization to avoid redoing the same work in all children
$children->rewindable =& $this->rewindable;
$children->rootPath = $this->rootPath;
}
return $children;
@ -109,31 +108,19 @@ class RecursiveDirectoryIterator extends \RecursiveDirectoryIterator
throw new AccessDeniedException($e->getMessage(), $e->getCode(), $e);
}
}
/**
* Do nothing for non rewindable stream.
*/
public function next() : void
{
$this->ignoreFirstRewind = \false;
parent::next();
}
public function rewind() : void
{
if (\false === $this->isRewindable()) {
// some streams like FTP are not rewindable, ignore the first rewind after creation,
// as newly created DirectoryIterator does not need to be rewound
if ($this->ignoreFirstRewind) {
$this->ignoreFirstRewind = \false;
return;
}
parent::rewind();
}
/**
* Checks if the stream is rewindable.
*/
public function isRewindable() : bool
{
if (null !== $this->rewindable) {
return $this->rewindable;
}
if (\false !== ($stream = @\opendir($this->getPath()))) {
$infos = \stream_get_meta_data($stream);
\closedir($stream);
if ($infos['seekable']) {
return $this->rewindable = \true;
}
}
return $this->rewindable = \false;
}
}

View File

@ -69,6 +69,8 @@ final class EnglishInflector implements InflectorInterface
['sei', 3, \false, \true, 'y'],
// accesses (access), addresses (address), kisses (kiss)
['sess', 4, \true, \false, 'ss'],
// statuses (status)
['sesutats', 8, \true, \true, 'status'],
// analyses (analysis), ellipses (ellipsis), fungi (fungus),
// neuroses (neurosis), theses (thesis), emphases (emphasis),
// oases (oasis), crises (crisis), houses (house), bases (base),
@ -100,6 +102,8 @@ final class EnglishInflector implements InflectorInterface
// waltzes (waltz), heroes (hero), bushes (bush), arches (arch),
// shoes (shoe)
['se', 2, \true, \true, ['', 'e']],
// status (status)
['sutats', 6, \true, \true, 'status'],
// tags (tag)
['s', 1, \true, \true, ''],
// chateaux (chateau)
@ -198,6 +202,8 @@ final class EnglishInflector implements InflectorInterface
['sub', 3, \true, \true, 'buses'],
// circuses (circus)
['suc', 3, \true, \true, 'cuses'],
// status (status)
['sutats', 6, \true, \true, ['status', 'statuses']],
// conspectuses (conspectus), prospectuses (prospectus)
['sutcep', 6, \true, \true, 'pectuses'],
// fungi (fungus), alumni (alumnus), syllabi (syllabus), radii (radius)

View File

@ -23,13 +23,14 @@
}
],
"require": {
"php": ">=8.0 <8.3",
"php": ">=8.0 <8.4",
"ext-session": "*",
"ext-json": "*"
},
"require-dev": {
"nette\/utils": "^3.0",
"nette\/di": "^3.0",
"nette\/http": "^3.0",
"nette\/mail": "^3.0",
"nette\/tester": "^2.2",
"latte\/latte": "^2.5",

View File

@ -21,6 +21,7 @@ Tracy library is a useful helper for everyday PHP programmers. It helps you to:
PHP is a perfect language for making hardly detectable errors because it gives great flexibility to programmers. Tracy\Debugger is more valuable because of that. It is an ultimate tool among the diagnostic ones.
If you are meeting Tracy for the first time, believe me, your life starts to be divided into one before the Tracy and the one with her. Welcome to the good part!
Documentation can be found on the [website](https://tracy.nette.org).
@ -36,7 +37,7 @@ Do you like Tracy? Are you looking forward to the new features?
Thank you!
Installation and requirements
Installation and Requirements
-----------------------------
The recommended way to is via Composer:
@ -49,51 +50,44 @@ Alternatively, you can download the whole package or [tracy.phar](https://github
| Tracy | compatible with PHP | compatible with browsers
|-----------|---------------|----------
| Tracy 2.10| PHP 8.0 8.2 | Chrome 64+, Firefox 69+, Safari 15.4+ and iOS Safari 15.4+
| Tracy 2.10| PHP 8.0 8.3 | Chrome 64+, Firefox 69+, Safari 15.4+ and iOS Safari 15.4+
| Tracy 2.9 | PHP 7.2 8.2 | Chrome 64+, Firefox 69+, Safari 13.1+ and iOS Safari 13.4+
| Tracy 2.8 | PHP 7.2 8.1 | Chrome 55+, Firefox 53+, Safari 11+ and iOS Safari 11+
| Tracy 2.7 | PHP 7.1 8.0 | Chrome 55+, Firefox 53+, MS Edge 16+, Safari 11+ and iOS Safari 11+
| Tracy 2.6 | PHP 7.1 8.0 | Chrome 49+, Firefox 45+, MS Edge 14+, Safari 10+ and iOS Safari 10.2+
| Tracy 2.5 | PHP 5.4 7.4 | Chrome 49+, Firefox 45+, MS Edge 12+, Safari 10+ and iOS Safari 10.2+
| Tracy 2.4 | PHP 5.4 7.2 | Chrome 29+, Firefox 28+, IE 11+ (except AJAX), MS Edge 12+, Safari 9+ and iOS Safari 9.2+
Usage
-----
Activating Tracy is easy. Simply add these two lines of code, preferably just after library loading (like `require 'vendor/autoload.php'`) and before any output is sent to browser:
Tracy is activated by calling the `Tracy\Debugger::enable()' method as soon as possible at the beginning of the program, before any output is sent:
```php
use Tracy\Debugger;
require 'vendor/autoload.php'; // alternatively tracy.phar
Debugger::enable();
```
The first thing you will notice on the website is a Debugger Bar.
(If you do not see anything, it means that Tracy is running in production mode. For security reasons, Tracy is visible only on localhost.
You may force Tracy to run in development mode by passing the `Debugger::Development` as the first parameter of `enable()` method.)
The `enable()` involves changing the error reporting level to E_ALL.
The first thing you'll notice on the page is the Tracy Bar in the bottom right corner. If you don't see it, it may mean that Tracy is running in production mode.
This is because Tracy is only visible on localhost for security reasons. To test if it works, you can temporarily put it into development mode using the `Debugger::enable(Debugger::Development)` parameter.
Debugger Bar
------------
Tracy Bar
---------
The Debugger Bar is a floating panel. It is displayed in the bottom right corner of a page. You can move it using the mouse. It will remember its position after the page reloading.
The Tracy Bar is a floating panel. It is displayed in the bottom right corner of a page. You can move it using the mouse. It will remember its position after the page reloading.
[![Debugger-Bar](https://nette.github.io/tracy/images/tracy-bar.png)](https://nette.github.io/tracy/tracy-debug-bar.html)
[![Debugger-Bar](https://nette.github.io/tracy/images/tracy-bar.webp)](https://nette.github.io/tracy/tracy-debug-bar.html)
You can add other useful panels to the Debugger Bar. You can find interesting ones in [addons](https://componette.org) or you can [create your own](https://tracy.nette.org/en/extensions).
You can add other useful panels to the Tracy Bar. You can find interesting ones in [addons](https://componette.org) or you can [create your own](https://tracy.nette.org/en/extensions).
If you do not want to show Debugger Bar, set:
If you do not want to show Tracy Bar, set:
```php
Debugger::$showBar = false;
```
Visualization of errors and exceptions
Visualization of Errors and Exceptions
--------------------------------------
Surely, you know how PHP reports errors: there is something like this in the page source code:
@ -127,7 +121,7 @@ And you know what? Fatal errors are captured and displayed in the same way. No n
Errors like a typo in a variable name or an attempt to open a nonexistent file generate reports of E_NOTICE or E_WARNING level. These can be easily overlooked and/or can be completely hidden in a web page graphic layout. Let Tracy manage them:
[![Notice rendered by Tracy](https://nette.github.io/tracy/images/tracy-notice2.png)](https://nette.github.io/tracy/tracy-debug-bar.html)
[![Notice rendered by Tracy](https://nette.github.io/tracy/images/tracy-notice2.webp)](https://nette.github.io/tracy/tracy-debug-bar.html)
Or they may be displayed like errors:
@ -138,94 +132,47 @@ Debugger::$strictMode = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED; // all error
[![Notice rendered by Tracy](https://nette.github.io/tracy/images/tracy-notice.png)](https://nette.github.io/tracy/tracy-notice.html)
In order to detect misspellings when assigning to an object, we use [trait Nette\SmartObject](https://doc.nette.org/en/3.0/smartobject).
Note: Tracy when activated changes the error reporting level to E_ALL. If you want to change this, do so after calling `enable()`.
Content Security Policy
-----------------------
Development vs Production Mode
------------------------------
If your site uses Content Security Policy, you'll need to add `'nonce-<value>'` and `'strict-dynamic'` to `script-src` for Tracy to work properly. Some 3rd plugins may require additional directives.
Nonce is not supported in the `style-src` directive, if you use this directive you need to add `'unsafe-inline'`, but this should be avoided in production mode.
Configuration example for [Nette Framework](https://nette.org):
```neon
http:
csp:
script-src: [nonce, strict-dynamic]
```
Example in pure PHP:
```php
$nonce = base64_encode(random_bytes(20));
header("Content-Security-Policy: script-src 'nonce-$nonce' 'strict-dynamic';");
```
Faster loading
--------------
The basic integration is straightforward, however if you have slow blocking scripts in web page, they can slow the Tracy loading.
The solution is to place `<?php Tracy\Debugger::renderLoader() ?>` into your template before
any scripts:
```html
<!DOCTYPE html>
<html>
<head>
<title>...<title>
<?php Tracy\Debugger::renderLoader() ?>
<link rel="stylesheet" href="assets/style.css">
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
</head>
```
AJAX and redirected requests
----------------------------
Tracy is able to show Debug bar and Bluescreens for AJAX and redirected requests. Tracy keeps the data in a temporary files and uses the `tracy-session` cookie. Tracy can be configured to use a standard PHP session:
```php
Debugger::setSessionStorage(new Tracy\NativeSession);
Debugger::enable();
```
In case you use non-standard session handler, you can start Tracy immediately (in order to handle any errors), then initialize your session handler
and then inform Tracy that session is ready to use via `dispatch()`:
```php
Debugger::setSessionStorage(new Tracy\NativeSession);
Debugger::enable();
// initialize session handler
session_start();
Debugger::dispatch();
```
Opening files in the editor
---------------------------
When the error page is displayed, you can click on file names and they will open in your editor with the cursor on the corresponding line. Files can also be created (action `create file`) or bug fixed in them (action `fix it`). In order to do this, you need to [configure the browser and the system](https://tracy.nette.org/cs/open-files-in-ide).
Production mode and error logging
---------------------------------
As you can see, Tracy is quite eloquent. It is appreciated in a development environment, but on a production server, it would cause a disaster. Any debugging information cannot be listed there. Therefore Tracy has an environment autodetection and logging functionality. Instead of showing herself, Tracy stores information into a log file and shows the visitor a user-comprehensible server error message:
As you can see, Tracy is quite talkative, which can be appreciated in the development environment, while on the production server it would cause a disaster. That's because no debugging information should be displayed there. Tracy therefore has **environment auto-detection** and if the example is run on a live server, the error will be logged instead of displayed, and the visitor will only see a user-friendly message:
[![Server Error 500](https://nette.github.io/tracy/images/tracy-error2.png)](https://nette.github.io/tracy/tracy-production.html)
Production output mode suppresses all debugging information which is sent out via `dump()` or `Debugger::fireLog()`, and of course all error messages generated by PHP. So, even if you forget `dump($obj)` in the source code, you do not have to worry about it on your production server. Nothing will be seen.
Production mode suppresses the display of all debugging information sent out using [dump() |dumper], and of course also all error messages generated by PHP. So if you have forgotten some `dump($obj)` in the code, you don't have to worry, nothing will be displayed on the production server.
The output mode is set by the first parameter of `Debugger::enable()`. You can specify either a constant `Debugger::Production` or `Debugger::Development`. Other option is to set it up in a way, that development mode will be on when the application is accessed from a defined IP address with a defined value of `tracy-debug` cookie. The syntax used to achieve this is `cookie-value@ip-address`.
How does mode auto-detection work? The mode is development if the application is running on localhost (i.e., IP address `127.0.0.1` or `::1`) and there is no proxy (i.e., its HTTP header). Otherwise, it runs in production mode.
If it is not specified, the default value `Debugger::Detect` is used. In this case, the system detects a server by IP address. The production mode is chosen if an application is accessed via a public IP address. A local IP address leads to development mode. It is not necessary to set the mode in most cases. The mode is correctly recognized when you are launching the application on your local server or in production.
If you want to enable development mode in other cases, for example for developers accessing from a specific IP address, you can specify it as a parameter of the `enable()` method:
In the production mode, Tracy automatically captures all errors and exceptions into a text log. Unless you specify otherwise, it will be stored in log/error.log. This error logging is extremely useful. Imagine, that all users of your application are actually betatesters. They are doing cutting-edge work for free when hunting bugs and you would be silly if you threw away their valuable reports to a recycle bin unnoticed.
```php
Debugger::enable('23.75.345.200'); // you can also provide an array of IP addresses
```
We definitely recommend combining the IP address with a cookie. Store a secret token, e.g., `secret1234`, in the `tracy-debug` cookie, and in this way, activate the development mode only for developers accessing from a specific IP address who have the mentioned token in the cookie:
```php
Debugger::enable('secret1234@23.75.345.200');
```
You can also directly set the development/production mode using the `Debugger::Development` or `Debugger::Production` constants as a parameter of the `enable()` method.
(If you use the Nette Framework, take a look at how to set the mode for it, and it will then also be used for Tracy.)
Error Logging
-------------
In production mode, Tracy automatically logs all errors and exceptions to a text log. In order for logging to take place, you need to set the absolute path to the log directory to the `$logDirectory` variable or pass it as the second parameter to `enable()` method:
```php
Debugger::$logDirectory = __DIR__ . '/log';
```
Error logging is extremely useful. Imagine that all users of your application are actually beta testers who do top-notch work in finding errors for free, and you would be foolish to throw their valuable reports away unnoticed into the trash bin.
If you need to log your own messages or caught exceptions, use the method `log()`:
@ -241,12 +188,6 @@ try {
}
```
A directory for errors logging can be set by the second parameter of the enable() method:
```php
Debugger::enable(Debugger::Detect, __DIR__ . '/mylog');
```
If you want Tracy to log PHP errors like `E_NOTICE` or `E_WARNING` with detailed information (HTML report), set `Debugger::$logSeverity`:
```php
@ -259,12 +200,18 @@ For a real professional the error log is a crucial source of information and he
Debugger::$email = 'admin@example.com';
```
If you use the Nette Framework, you can set this and others in the configuration file.
(If you use the Nette Framework, you can set this and others in the configuration file.)
To protect your e-mail box from flood, Tracy sends **only one message** and creates a file `email-sent`. When a developer receives the e-mail notification, he checks the log, corrects his application and deletes the `email-sent` monitoring file. This activates the e-mail sending again.
Variable dumping
Opening Files in the Editor
---------------------------
When the error page is displayed, you can click on file names and they will open in your editor with the cursor on the corresponding line. Files can also be created (action `create file`) or bug fixed in them (action `fix it`). In order to do this, you need to [configure the browser and the system](https://tracy.nette.org/cs/open-files-in-ide).
Variable Dumping
----------------
Every debugging developer is a good friend with the function `var_dump`, which lists all contents of any variable in detail. Unfortunately, its output is without HTML formatting and outputs the dump into a single line of HTML code, not to mention context escaping. It is necessary to replace the `var_dump` with a more handy function. That is just what `dump()` is.
@ -273,41 +220,50 @@ Every debugging developer is a good friend with the function `var_dump`, which l
$arr = [10, 20.2, true, null, 'hello'];
dump($arr);
// or Tracy\Debugger::dump($arr);
// or Debugger::dump($arr);
```
generates the output:
![dump](https://nette.github.io/tracy/images/tracy-dump.png)
![dump](https://nette.github.io/tracy/images/dump-basic.webp)
You can change the default light theme to dark:
```php
Debugger::$dumpTheme = 'dark';
```
![dump](https://nette.github.io/tracy/images/dump-dark.webp)
You can also change the nesting depth by `Debugger::$maxDepth` and displayed strings length by `Debugger::$maxLength`. Naturally, lower values accelerate Tracy rendering.
```php
Debugger::$maxDepth = 2; // default: 7
Debugger::$maxDepth = 2; // default: 3
Debugger::$maxLength = 50; // default: 150
Debugger::$dumpTheme = 'dark'; // default: light
```
The `dump()` function can display useful location information:
The `dump()` function can display other useful information. `Tracy\Dumper::LOCATION_SOURCE` adds a tooltip with path to the file, where the function was called. `Tracy\Dumper::LOCATION_LINK` adds a link to the file. `Tracy\Dumper::LOCATION_CLASS` adds a tooltip to every dumped object containing path to the file, in which the object's class is defined. All these constants can be set in `Debugger::$showLocation` variable before calling the `dump()`. You can set multiple values at once using the `|` operator.
```php
Debugger::$showLocation = true; // shows tooltip with path to the file, where the dump() was called, and tooltips for every dumped objects
Debugger::$showLocation = Tracy\Dumper::LOCATION_CLASS; // shows only tooltips for every dumped object containing path to the file
Debugger::$showLocation = false; // hides all location information
Debugger::$showLocation = Tracy\Dumper::LOCATION_SOURCE; // Shows path to where the dump() was called
Debugger::$showLocation = Tracy\Dumper::LOCATION_CLASS | Tracy\Dumper::LOCATION_LINK; // Shows both paths to the classes and link to where the dump() was called
Debugger::$showLocation = false; // Hides additional location information
Debugger::$showLocation = true; // Shows all additional location information
```
Very handy alternative to `dump()` is `dumpe()` (ie. dump and exit) and `bdump()`. This allows us to dump variables in Debugger Bar. This is useful, because dumps don't mess up the output and we can also add a title to the dump.
Very handy alternative to `dump()` is `dumpe()` (ie. dump and exit) and `bdump()`. This allows us to dump variables in Tracy Bar. This is useful, because dumps don't mess up the output and we can also add a title to the dump.
```php
bdump([2, 4, 6, 8], 'even numbers up to ten');
bdump([1, 3, 5, 7, 9], 'odd numbers up to ten');
```
![bar dump](https://nette.github.io/tracy/images/tracy-bardump.png)
![bar dump](https://nette.github.io/tracy/images/bardump-en.webp)
Timing
------
Stopwatch
---------
Another useful tool is the debugger stopwatch with a precision of microseconds:
@ -374,6 +330,93 @@ services:
```
Monolog Integration
-------------------
This package provides a PSR-3 adapter, allowing for integration of [monolog/monolog](https://github.com/Seldaek/monolog).
```php
$monolog = new Monolog\Logger('main-channel');
$monolog->pushHandler(new Monolog\Handler\StreamHandler($logFilePath, Monolog\Logger::DEBUG));
$tracyLogger = new Tracy\Bridges\Psr\PsrToTracyLoggerAdapter($monolog);
Debugger::setLogger($tracyLogger);
Debugger::enable();
Debugger::log('info'); // writes: [<TIMESTAMP>] main-channel.INFO: info [] []
Debugger::log('warning', Debugger::WARNING); // writes: [<TIMESTAMP>] main-channel.WARNING: warning [] []
```
Faster Loading
--------------
The basic integration is straightforward, however if you have slow blocking scripts in web page, they can slow the Tracy loading.
The solution is to place `<?php Tracy\Debugger::renderLoader() ?>` into your template before any scripts:
```html
<!DOCTYPE html>
<html>
<head>
<title>...<title>
<?php Tracy\Debugger::renderLoader() ?>
<link rel="stylesheet" href="assets/style.css">
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
</head>
```
AJAX and Redirected Requests
----------------------------
Tracy can display Debug bar and Bluescreens for AJAX requests and redirects. Tracy creates its own sessions, stores data in its own temporary files, and uses a `tracy-session` cookie.
Tracy can also be configured to use a native PHP session, which is started before Tracy is turned on:
```php
session_start();
Debugger::setSessionStorage(new Tracy\NativeSession);
Debugger::enable();
```
In case starting a session requires more complex initialization, you can start Tracy immediately (so that it can handle any errors that occur) and then initialize the session handler and finally inform Tracy that the session is ready to be used using the `dispatch()` function:
```php
Debugger::setSessionStorage(new Tracy\NativeSession);
Debugger::enable();
// followed by session initialization
// and start the session
session_start();
Debugger::dispatch();
```
The `setSessionStorage()` function has existed since version 2.9, before that Tracy always used the native PHP session.
Content Security Policy
-----------------------
If your site uses Content Security Policy, you'll need to add `'nonce-<value>'` and `'strict-dynamic'` to `script-src` for Tracy to work properly. Some 3rd plugins may require additional directives.
Nonce is not supported in the `style-src` directive, if you use this directive you need to add `'unsafe-inline'`, but this should be avoided in production mode.
Configuration example for [Nette Framework](https://nette.org):
```neon
http:
csp:
script-src: [nonce, strict-dynamic]
```
Example in pure PHP:
```php
$nonce = base64_encode(random_bytes(20));
header("Content-Security-Policy: script-src 'nonce-$nonce' 'strict-dynamic';");
```
nginx
-----

View File

@ -18,15 +18,19 @@ if (!Helpers::isCli()) {
<div class="tracy-section-panel tracy-collapsed">
<h3>Process ID <?= Helpers::escapeHtml(getmypid()) ?></h3>
<pre>php<?= Helpers::escapeHtml(explode('):', $source, 2)[1]) ?></pre>
<?php if (count($tmp = explode('):', $source, 2)) === 2): ?>
<pre>php<?= Helpers::escapeHtml($tmp[1]) ?></pre>
<?php endif; ?>
<?php if (isset($_SERVER['argv'])): ?>
<h3>Arguments</h3>
<div class="tracy-pane">
<table>
<?php foreach ($_SERVER['argv'] as $k => $v): ?>
<?php foreach ($_SERVER['argv'] as $k => $v): ?>
<tr><th><?= Helpers::escapeHtml($k) ?></th><td><?= $dump($v, $k) ?></td></tr>
<?php endforeach ?>
<?php endforeach ?>
</table>
</div>
<?php endif; ?>
</div>
</section>

View File

@ -13,7 +13,7 @@ use ErrorException;
*/
class Debugger
{
public const VERSION = '2.10.2';
public const VERSION = '2.10.3';
/** server modes for Debugger::enable() */
public const Development = \false, Production = \true, Detect = null;
public const DEVELOPMENT = self::Development, PRODUCTION = self::Production, DETECT = self::Detect;
@ -399,7 +399,7 @@ class Debugger
if (!$panel) {
self::getBar()->addPanel($panel = new DefaultBarPanel('dumps'), 'Tracy:dumps');
}
$panel->data[] = ['title' => $title, 'dump' => Dumper::toHtml($var, $options + [Dumper::DEPTH => self::$maxDepth, Dumper::TRUNCATE => self::$maxLength, Dumper::LOCATION => self::$showLocation ?: Dumper::LOCATION_CLASS | Dumper::LOCATION_SOURCE, Dumper::LAZY => \true])];
$panel->data[] = ['title' => $title, 'dump' => Dumper::toHtml($var, $options + [Dumper::DEPTH => self::$maxDepth, Dumper::ITEMS => self::$maxItems, Dumper::TRUNCATE => self::$maxLength, Dumper::LOCATION => self::$showLocation ?: Dumper::LOCATION_CLASS | Dumper::LOCATION_SOURCE, Dumper::LAZY => \true])];
}
return $var;
}

View File

@ -34,9 +34,8 @@ final class ProductionStrategy
(function ($logged) {
return require Debugger::$errorTemplate ?: __DIR__ . '/assets/error.500.phtml';
})(empty($e));
} elseif (Helpers::isCli()) {
// @ triggers E_NOTICE when strerr is closed since PHP 7.4
@\fwrite(\STDERR, "ERROR: {$exception->getMessage()}\n" . (isset($e) ? 'Unable to log error. You may try enable debug mode to inspect the problem.' : 'Check log to see more info.') . "\n");
} elseif (Helpers::isCli() && \is_resource(\STDERR)) {
\fwrite(\STDERR, "ERROR: {$exception->getMessage()}\n" . (isset($e) ? 'Unable to log error. You may try enable debug mode to inspect the problem.' : 'Check log to see more info.') . "\n");
}
}
public function handleError(int $severity, string $message, string $file, int $line) : void

View File

@ -169,7 +169,7 @@ final class Exposer
}
public static function exposeDsCollection(Ds\Collection $obj, Value $value, Describer $describer) : void
{
foreach ($obj as $k => $v) {
foreach (clone $obj as $k => $v) {
$describer->addPropertyTo($value, (string) $k, $v);
}
}