diff --git a/CHANGELOG.md b/CHANGELOG.md index 50130f4..5d42b4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
+## 1.1.4 - 2023-11-28 +- Minor fixes and added some tests. +### Added +- Support for PHP 8.3 +- Added PHPStan to build pipeline +### Fixed +- Fixed the case when non-intersecting pointers were considered intersecting (#106). Thanks @XedinUnknown + +
+ ## 1.1.3 - 2022-10-12 ### Fixed - Fix the parsing of nested sub-trees that use wildcards (#83). Thanks @cerbero90 diff --git a/Makefile b/Makefile index 5a9c675..a4f6d5d 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,8 @@ define PHP_VERSIONS "7.4 3.1.1"\ "8.0 3.1.1"\ "8.1 3.1.1"\ -"8.2-rc 3.2.0alpha3" +"8.2 3.2.0"\ +"8.3-rc 3.3.0alpha3" endef define DOCKER_RUN @@ -29,16 +30,15 @@ help: @grep -E '^[-a-zA-Z0-9_\.\/]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[32m%-15s\033[0m\t%s\n", $$1, $$2}' -build: tests-all cs-check ## Run all necessary stuff before commit. +build: composer-validate cs-check phpstan tests-all ## Run all necessary stuff before commit. -tests: CMD=composer tests -- $(ARGS) -tests: docker-run ## Run tests on recent PHP version. Pass args to phpunit via ARGS="" +tests: ## Run tests on recent PHP version. Pass args to phpunit via ARGS="" + @$(call DOCKER_RUN,$(COVERAGE_PHP),composer tests -- $(ARGS)) -tests-coverage: CMD=composer tests-coverage -- $(ARGS) tests-coverage: ## Runs tests and creates ./clover.xml. Pass args to phpunit via ARGS="" - @$(call DOCKER_RUN,$(COVERAGE_PHP),$(CMD)) + @$(call DOCKER_RUN,$(COVERAGE_PHP),composer tests-coverage -- $(ARGS)) tests-all: ## Run tests on all supported PHP versions. Pass args to phpunit via ARGS="" @@ -51,16 +51,24 @@ tests-all: ## Run tests on all supported PHP versions. Pass args to phpunit via done -cs-check: CMD=composer cs-check -cs-check: docker-run ## Check code style +cs-check: ## Check code style + @$(call DOCKER_RUN,$(LATEST_PHP),composer cs-check) -cs-fix: CMD=composer cs-fix -cs-fix: docker-run ## Fix code style +phpstan: ## Run phpstan + @$(call DOCKER_RUN,$(LATEST_PHP),composer phpstan) -performance-tests: CMD=composer performance-tests -performance-tests: docker-run ## Run performance tests +cs-fix: ## Fix code style + @$(call DOCKER_RUN,$(LATEST_PHP),composer cs-fix) + + +performance-tests: ## Run performance tests + @$(call DOCKER_RUN,$(LATEST_PHP),composer performance-tests) + + +composer-validate: ## Validate composer.json contents + @$(call DOCKER_RUN,$(LATEST_PHP),composer validate) release: .env build diff --git a/README.md b/README.md index c252163..65168d3 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ - - -(README in sync with the code) +# JSON Machine Very easy to use and memory efficient drop-in replacement for inefficient iteration of big JSON files or streams -for PHP >=7.0. See [TL;DR](#tl-dr). No dependencies in production except optional `ext-json`. +for PHP >=7.0. See [TL;DR](#tl-dr). No dependencies in production except optional `ext-json`. README in sync with the code [![Build Status](https://github.com/halaxa/json-machine/actions/workflows/makefile.yml/badge.svg)](https://github.com/halaxa/json-machine/actions) [![codecov](https://img.shields.io/codecov/c/gh/halaxa/json-machine?label=phpunit%20%40covers)](https://codecov.io/gh/halaxa/json-machine) diff --git a/build/build-image.sh b/build/build-image.sh index 223552e..bef1785 100755 --- a/build/build-image.sh +++ b/build/build-image.sh @@ -21,7 +21,12 @@ docker ps --all --format "{{.Names}}" | grep "$CONTAINER_NAME" && docker rm -f " printf " FROM $FROM_IMAGE - RUN apk add --update \ + RUN apk update && apk upgrade + # https://stackoverflow.com/questions/76507083/pecl-install-no-releases-available#comment136513209_76651916 + RUN rm /etc/ssl/certs/ca-cert-DST_Root_CA_X3.pem || true \ + && cat /etc/ssl/certs/*.pem > /etc/ssl/certs/ca-certificates.crt \ + && cat /etc/ssl/certs/*.pem > /etc/ssl/cert.pem + RUN apk add \ autoconf \ g++ \ libtool \ @@ -31,6 +36,7 @@ printf " && wget http://pear.php.net/go-pear.phar && php go-pear.phar \ && pecl install xdebug-$XDEBUG_VERSION \ && docker-php-ext-enable xdebug \ + && docker-php-ext-enable opcache \ && wget https://getcomposer.org/download/2.2.18/composer.phar -O /usr/local/bin/composer \ && chmod +x /usr/local/bin/composer " | docker build --quiet --tag "$CONTAINER_NAME" - > /dev/null diff --git a/composer.json b/composer.json index c50c538..aba62e7 100644 --- a/composer.json +++ b/composer.json @@ -13,18 +13,20 @@ "tests-coverage": "build/composer-update.sh && XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-clover clover.xml", "cs-check": "build/composer-update.sh && vendor/bin/php-cs-fixer fix --dry-run --verbose --allow-risky=yes", "cs-fix": "build/composer-update.sh && vendor/bin/php-cs-fixer fix --verbose --allow-risky=yes", - "performance-tests": "php -n test/performance/testPerformance.php" + "phpstan": "build/composer-update.sh && vendor/bin/phpstan analyse", + "performance-tests": "php -d xdebug.mode=off -d opcache.enable_cli=1 -d opcache.jit_buffer_size=100M test/performance/testPerformance.php" }, "config": { "lock": false, "sort-packages": true }, "require": { - "php": ">=7.0" + "php": "7.0 - 8.3" }, "require-dev": { "ext-json": "*", "friendsofphp/php-cs-fixer": "^3.0", + "phpstan/phpstan": "^1.10", "phpunit/phpunit": "^8.0" }, "suggest": { diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..628eda7 --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,21 @@ +parameters: + level: 5 + paths: + - src + - test + bootstrapFiles: + - %rootDir%/../../../test/bootstrap.php + treatPhpDocTypesAsCertain: false + exceptions: + implicitThrows: false + check: + missingCheckedExceptionInThrows: true + tooWideThrowType: true + reportUncheckedExceptionDeadCatch: true + checkedExceptionClasses: + - JsonMachine\Exception\PathNotFoundException + - JsonMachine\Exception\SyntaxErrorException + - JsonMachine\Exception\UnexpectedEndSyntaxErrorException + uncheckedExceptionClasses: + - JsonMachine\Exception\InvalidArgumentException + - JsonMachine\Exception\JsonMachineException diff --git a/src/Exception/SyntaxErrorException.php b/src/Exception/SyntaxErrorException.php index 9593e54..5f8be95 100644 --- a/src/Exception/SyntaxErrorException.php +++ b/src/Exception/SyntaxErrorException.php @@ -6,7 +6,7 @@ namespace JsonMachine\Exception; class SyntaxErrorException extends JsonMachineException { - public function __construct($message, $position) + public function __construct(string $message, int $position) { parent::__construct($message." At position $position."); } diff --git a/src/FileChunks.php b/src/FileChunks.php index 26d170f..dbcbf3e 100644 --- a/src/FileChunks.php +++ b/src/FileChunks.php @@ -4,6 +4,9 @@ declare(strict_types=1); namespace JsonMachine; +/** + * @implements \IteratorAggregate + */ class FileChunks implements \IteratorAggregate { /** @var string */ @@ -24,6 +27,8 @@ class FileChunks implements \IteratorAggregate /** * @return \Generator + * + * @throws Exception\InvalidArgumentException */ #[\ReturnTypeWillChange] public function getIterator() diff --git a/src/Items.php b/src/Items.php index 6c932ad..37f2d10 100644 --- a/src/Items.php +++ b/src/Items.php @@ -118,6 +118,8 @@ final class Items implements \IteratorAggregate, PositionAware /** * @return \Generator + * + * @throws Exception\PathNotFoundException */ #[\ReturnTypeWillChange] public function getIterator() @@ -125,6 +127,9 @@ final class Items implements \IteratorAggregate, PositionAware return $this->parser->getIterator(); } + /** + * @throws Exception\JsonMachineException + */ public function getPosition() { return $this->parser->getPosition(); @@ -135,11 +140,17 @@ final class Items implements \IteratorAggregate, PositionAware return $this->parser->getJsonPointers(); } + /** + * @throws Exception\JsonMachineException + */ public function getCurrentJsonPointer(): string { return $this->parser->getCurrentJsonPointer(); } + /** + * @throws Exception\JsonMachineException + */ public function getMatchedJsonPointer(): string { return $this->parser->getMatchedJsonPointer(); diff --git a/src/ItemsOptions.php b/src/ItemsOptions.php index 8dc9c0c..eb201f1 100644 --- a/src/ItemsOptions.php +++ b/src/ItemsOptions.php @@ -12,6 +12,9 @@ class ItemsOptions extends \ArrayObject { private $options = []; + /** + * @throws InvalidArgumentException + */ public function __construct(array $options = []) { $this->validateOptions($options); diff --git a/src/Parser.php b/src/Parser.php index 8b1bef0..fbc3595 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -44,13 +44,13 @@ class Parser implements \IteratorAggregate, PositionAware /** @var ItemDecoder */ private $jsonDecoder; - /** @var string */ + /** @var string|null */ private $matchedJsonPointer; /** @var array */ private $paths; - /** @var array */ + /** @var array|null */ private $currentPath; /** @var array */ @@ -276,7 +276,7 @@ class Parser implements \IteratorAggregate, PositionAware } if ($token === null) { - $this->error('Cannot iterate empty JSON', $token); + $this->error('Cannot iterate empty JSON', ''); } if ($currentLevel > -1 && ! $subtreeEnded) { @@ -371,6 +371,9 @@ class Parser implements \IteratorAggregate, PositionAware return array_values($this->jsonPointers); } + /** + * @throws JsonMachineException + */ public function getCurrentJsonPointer(): string { if ($this->currentPath === null) { @@ -380,6 +383,9 @@ class Parser implements \IteratorAggregate, PositionAware return self::pathToJsonPointer($this->currentPath); } + /** + * @throws JsonMachineException + */ public function getMatchedJsonPointer(): string { if ($this->matchedJsonPointer === null) { diff --git a/src/StreamChunks.php b/src/StreamChunks.php index d6c6e72..5248c1c 100644 --- a/src/StreamChunks.php +++ b/src/StreamChunks.php @@ -6,6 +6,9 @@ namespace JsonMachine; use JsonMachine\Exception\InvalidArgumentException; +/** + * @implements \IteratorAggregate + */ class StreamChunks implements \IteratorAggregate { /** @var resource */ @@ -17,6 +20,8 @@ class StreamChunks implements \IteratorAggregate /** * @param resource $stream * @param int $chunkSize + * + * @throws InvalidArgumentException */ public function __construct($stream, $chunkSize = 1024 * 8) { diff --git a/src/TokensWithDebugging.php b/src/TokensWithDebugging.php index 59b9400..acd3d36 100644 --- a/src/TokensWithDebugging.php +++ b/src/TokensWithDebugging.php @@ -4,17 +4,25 @@ declare(strict_types=1); namespace JsonMachine; +/** + * @implements \IteratorAggregate + */ class TokensWithDebugging implements \IteratorAggregate, PositionAware { - /** @var iterable */ + /** @var iterable */ private $jsonChunks; + /** @var int */ private $position = 0; + + /** @var int */ private $line = 1; + + /** @var int */ private $column = 0; /** - * @param iterable $jsonChunks + * @param iterable $jsonChunks */ public function __construct($jsonChunks) { @@ -53,6 +61,7 @@ class TokensWithDebugging implements \IteratorAggregate, PositionAware foreach ($this->jsonChunks as $bytes) { $bytesLength = strlen($bytes); for ($i = 0; $i < $bytesLength; ++$i) { + /** @var string $byte */ $byte = $bytes[$i]; if ($inString) { if ($byte == '"' && ! $escaping) { diff --git a/src/ValidJsonPointers.php b/src/ValidJsonPointers.php index 9bece75..334c8a2 100644 --- a/src/ValidJsonPointers.php +++ b/src/ValidJsonPointers.php @@ -8,16 +8,23 @@ use JsonMachine\Exception\InvalidArgumentException; final class ValidJsonPointers { + /** @var string[] */ private $jsonPointers = []; + /** @var bool */ private $validated = false; + /** + * @param string[] $jsonPointers + */ public function __construct(array $jsonPointers) { $this->jsonPointers = array_values($jsonPointers); } /** + * @return string[] + * * @throws InvalidArgumentException */ public function toArray(): array @@ -30,6 +37,8 @@ final class ValidJsonPointers } /** + * @return void + * * @throws InvalidArgumentException */ private function validate() @@ -40,6 +49,8 @@ final class ValidJsonPointers } /** + * @return void + * * @throws InvalidArgumentException */ private function validateFormat() @@ -54,6 +65,8 @@ final class ValidJsonPointers } /** + * @return void + * * @throws InvalidArgumentException */ private function validateJsonPointersDoNotIntersect() @@ -64,8 +77,8 @@ final class ValidJsonPointers continue; } if ($jsonPointerA === $jsonPointerB - || self::str_contains($jsonPointerA, $jsonPointerB) - || self::str_contains($jsonPointerA, self::wildcardify($jsonPointerB)) + || self::str_contains("$jsonPointerA/", "$jsonPointerB/") + || self::str_contains("$jsonPointerA/", self::wildcardify("$jsonPointerB/")) ) { throw new InvalidArgumentException( sprintf( diff --git a/src/autoloader.php b/src/autoloader.php index fa3ddc1..7ae1079 100644 --- a/src/autoloader.php +++ b/src/autoloader.php @@ -19,7 +19,10 @@ namespace JsonMachine; */ class Autoloading { - static public function autoloader($class) + /** + * @return void + */ + static public function autoloader(string $class) { $prefix = 'JsonMachine\\'; $baseDir = __DIR__.DIRECTORY_SEPARATOR; diff --git a/test/JsonMachineTest/Exception/SyntaxErrorExceptionTest.php b/test/JsonMachineTest/Exception/SyntaxErrorExceptionTest.php index baae02c..2be2902 100644 --- a/test/JsonMachineTest/Exception/SyntaxErrorExceptionTest.php +++ b/test/JsonMachineTest/Exception/SyntaxErrorExceptionTest.php @@ -14,7 +14,7 @@ class SyntaxErrorExceptionTest extends TestCase { public function testMessageContainsDataFromConstructor() { - $exception = new SyntaxErrorException('msg 42', '24'); + $exception = new SyntaxErrorException('msg 42', 24); $this->assertContains('msg 42', $exception->getMessage()); $this->assertContains('24', $exception->getMessage()); diff --git a/test/JsonMachineTest/ItemsTest.php b/test/JsonMachineTest/ItemsTest.php index 842c13c..ebe009c 100644 --- a/test/JsonMachineTest/ItemsTest.php +++ b/test/JsonMachineTest/ItemsTest.php @@ -141,6 +141,13 @@ class ItemsTest extends \PHPUnit_Framework_TestCase $this->assertSame(['/one', '/two'], $items->getJsonPointers()); } + public function testCountViaIteratorCount() + { + $items = Items::fromIterable(['{"results":', '[1,2,3]}'], ['pointer' => ['/results']]); + + $this->assertSame(3, iterator_count($items)); + } + public function testRecursiveIteration() { $items = Items::fromString('[[":)"]]', ['recursive' => true]); diff --git a/test/JsonMachineTest/JsonDecoder/DecodingErrorTest.php b/test/JsonMachineTest/JsonDecoder/DecodingErrorTest.php index 2dcbdaa..c9a0e8f 100644 --- a/test/JsonMachineTest/JsonDecoder/DecodingErrorTest.php +++ b/test/JsonMachineTest/JsonDecoder/DecodingErrorTest.php @@ -14,7 +14,7 @@ class DecodingErrorTest extends TestCase { public function testGetMalformedJson() { - $decodingError = new DecodingError('"json\"', null); + $decodingError = new DecodingError('"json\"', ''); $this->assertSame('"json\"', $decodingError->getMalformedJson()); } diff --git a/test/JsonMachineTest/ParserTest.php b/test/JsonMachineTest/ParserTest.php index a0d7722..62983c4 100644 --- a/test/JsonMachineTest/ParserTest.php +++ b/test/JsonMachineTest/ParserTest.php @@ -49,6 +49,11 @@ class ParserTest extends \PHPUnit_Framework_TestCase ['', '[{"c":1},"string",{"d":2},false]', [[0 => ['c' => 1]], [1 => 'string'], [2 => ['d' => 2]], [3 => false]]], ['', '[false,{"c":1},"string",{"d":2}]', [[0 => false], [1 => ['c' => 1]], [2 => 'string'], [3 => ['d' => 2]]]], ['', '[{"c":1,"d":2}]', [[['c' => 1, 'd' => 2]]]], + 'ISSUE-108' => [ + '', + '["https://click.justwatch.com/a?cx=eyJzY2hlbWEiOiJpZ2x1OmNvbS5zbm93cGxvd2FuYWx5dGljcy5zbm93cGxvdy9jb250ZXh0cy9qc29uc2NoZW1hLzEtMC0wIiwiZGF0YSI6W3sic2NoZW1hIjoiaWdsdTpjb20uanVzdHdhdGNoL2NsaWNrb3V0X2NvbnRleHQvanNvbnNjaGVtYS8xLTItMCIsImRhdGEiOnsicHJvdmlkZXIiOiJBcHBsZSBUViIsIm1vbmV0aXphdGlvblR5cGUiOiJidXkiLCJwcmVzZW50YXRpb25UeXBlIjoiaGQiLCJjdXJyZW5jeSI6IlVTRCIsInByaWNlIjo1MTkuNzQsIm9yaWdpbmFsUHJpY2UiOjAsImF1ZGlvTGFuZ3VhZ2UiOiIiLCJzdWJ0aXRsZUxhbmd1YWdlIjoiIiwiY2luZW1hSWQiOjAsInNob3d0aW1lIjoiIiwiaXNGYXZvcml0ZUNpbmVtYSI6ZmFsc2UsInBhcnRuZXJJZCI6MTI3MCwicHJvdmlkZXJJZCI6MiwiY2xpY2tvdXRUeXBlIjoianctY29udGVudC1wYXJ0bmVyLWFwaSJ9fSx7InNjaGVtYSI6ImlnbHU6Y29tLmp1c3R3YXRjaC90aXRsZV9jb250ZXh0L2pzb25zY2hlbWEvMS0wLTAiLCJkYXRhIjp7InRpdGxlSWQiOjIwOTgxLCJvYmplY3RUeXBlIjoic2hvdyIsImp3RW50aXR5SWQiOiJ0czIwOTgxIn19XX0\u0026r=https%3A%2F%2Ftv.apple.com%2Fus%2Fshow%2Fsurvivor%2Fumc.cmc.6ozd0mt09a86bpa19l885jv4z\u0026uct_country=us"]', + [['https://click.justwatch.com/a?cx=eyJzY2hlbWEiOiJpZ2x1OmNvbS5zbm93cGxvd2FuYWx5dGljcy5zbm93cGxvdy9jb250ZXh0cy9qc29uc2NoZW1hLzEtMC0wIiwiZGF0YSI6W3sic2NoZW1hIjoiaWdsdTpjb20uanVzdHdhdGNoL2NsaWNrb3V0X2NvbnRleHQvanNvbnNjaGVtYS8xLTItMCIsImRhdGEiOnsicHJvdmlkZXIiOiJBcHBsZSBUViIsIm1vbmV0aXphdGlvblR5cGUiOiJidXkiLCJwcmVzZW50YXRpb25UeXBlIjoiaGQiLCJjdXJyZW5jeSI6IlVTRCIsInByaWNlIjo1MTkuNzQsIm9yaWdpbmFsUHJpY2UiOjAsImF1ZGlvTGFuZ3VhZ2UiOiIiLCJzdWJ0aXRsZUxhbmd1YWdlIjoiIiwiY2luZW1hSWQiOjAsInNob3d0aW1lIjoiIiwiaXNGYXZvcml0ZUNpbmVtYSI6ZmFsc2UsInBhcnRuZXJJZCI6MTI3MCwicHJvdmlkZXJJZCI6MiwiY2xpY2tvdXRUeXBlIjoianctY29udGVudC1wYXJ0bmVyLWFwaSJ9fSx7InNjaGVtYSI6ImlnbHU6Y29tLmp1c3R3YXRjaC90aXRsZV9jb250ZXh0L2pzb25zY2hlbWEvMS0wLTAiLCJkYXRhIjp7InRpdGxlSWQiOjIwOTgxLCJvYmplY3RUeXBlIjoic2hvdyIsImp3RW50aXR5SWQiOiJ0czIwOTgxIn19XX0&r=https%3A%2F%2Ftv.apple.com%2Fus%2Fshow%2Fsurvivor%2Fumc.cmc.6ozd0mt09a86bpa19l885jv4z&uct_country=us']], + ], ['/', '{"":{"c":1,"d":2}}', [['c' => 1], ['d' => 2]]], ['/~0', '{"~":{"c":1,"d":2}}', [['c' => 1], ['d' => 2]]], ['/~1', '{"/":{"c":1,"d":2}}', [['c' => 1], ['d' => 2]]], diff --git a/test/JsonMachineTest/StreamChunksTest.php b/test/JsonMachineTest/StreamChunksTest.php index 2344e0c..1c495ee 100644 --- a/test/JsonMachineTest/StreamChunksTest.php +++ b/test/JsonMachineTest/StreamChunksTest.php @@ -15,6 +15,7 @@ class StreamChunksTest extends \PHPUnit_Framework_TestCase public function testThrowsIfNoResource() { $this->expectException(InvalidArgumentException::class); + /* @phpstan-ignore-next-line */ new StreamChunks(false); } diff --git a/test/JsonMachineTest/ValidJsonPointersTest.php b/test/JsonMachineTest/ValidJsonPointersTest.php index f92a78b..def210f 100644 --- a/test/JsonMachineTest/ValidJsonPointersTest.php +++ b/test/JsonMachineTest/ValidJsonPointersTest.php @@ -16,7 +16,6 @@ class ValidJsonPointersTest extends \PHPUnit_Framework_TestCase * @dataProvider data_testThrowsOnIntersectingPaths * * @param $jsonPointers - * @param ParserTest $parserTest */ public function testThrowsOnIntersectingPaths($jsonPointers) { @@ -70,11 +69,23 @@ class ValidJsonPointersTest extends \PHPUnit_Framework_TestCase (new ValidJsonPointers(['/one', '/one']))->toArray(); } - public function testToArrayReturnsJsonPointers() + /** + * @dataProvider data_testToArrayReturnsJsonPointers + */ + public function testToArrayReturnsJsonPointers(string $pointerA, string $pointerB) { $this->assertSame( - ['/one', '/two'], - (new ValidJsonPointers(['/one', '/two']))->toArray() + [$pointerA, $pointerB], + (new ValidJsonPointers([$pointerA, $pointerB]))->toArray() ); } + + public function data_testToArrayReturnsJsonPointers() + { + return [ + ['/one', '/two'], + ['/companies/-/id', '/companies/0/idempotency_key'], + ['/companies/1/id', '/companies/1/idempotency_key'], + ]; + } } diff --git a/test/performance/testPerformance.php b/test/performance/testPerformance.php index a851487..fc7c50f 100644 --- a/test/performance/testPerformance.php +++ b/test/performance/testPerformance.php @@ -6,8 +6,22 @@ use JsonMachine\Items; require_once __DIR__.'/../../vendor/autoload.php'; -if (in_array('xdebug', get_loaded_extensions())) { - trigger_error('Xdebug enabled. Results may be affected.', E_USER_WARNING); +if ( ! ini_get('xdebug.mode')) { + echo "Xdebug disabled\n"; +} else { + echo "Xdebug enabled\n"; +} + +if ( ! function_exists('opcache_get_status')) { + echo "Opcache disabled\n"; + echo "JIT disabled\n"; +} else { + echo "Opcache enabled\n"; + if (opcache_get_status()['jit']['enabled']) { + echo "JIT enabled\n"; + } else { + echo "JIT disabled\n"; + } } ini_set('memory_limit', '-1'); // for json_decode use case