chore: add phpunit dependency
This commit is contained in:
parent
e01b7e1701
commit
5cc1e0af02
1569 changed files with 127551 additions and 4 deletions
|
@ -10,7 +10,8 @@
|
|||
},
|
||||
"require-dev": {
|
||||
"dealerdirect/phpcodesniffer-composer-installer": "^1.0",
|
||||
"wp-coding-standards/wpcs": "^3.0"
|
||||
"wp-coding-standards/wpcs": "^3.0",
|
||||
"phpunit/phpunit": "10"
|
||||
},
|
||||
"config": {
|
||||
"allow-plugins": {
|
||||
|
|
1614
composer.lock
generated
1614
composer.lock
generated
File diff suppressed because it is too large
Load diff
1088
vendor/composer/autoload_classmap.php
vendored
1088
vendor/composer/autoload_classmap.php
vendored
File diff suppressed because it is too large
Load diff
2
vendor/composer/autoload_files.php
vendored
2
vendor/composer/autoload_files.php
vendored
|
@ -6,7 +6,9 @@ $vendorDir = dirname(__DIR__);
|
|||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'09fc349b549513bf7f4291502426f919' => $vendorDir . '/embed/embed/src/functions.php',
|
||||
'ec07570ca5a812141189b1fa81503674' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert/Functions.php',
|
||||
);
|
||||
|
|
2
vendor/composer/autoload_psr4.php
vendored
2
vendor/composer/autoload_psr4.php
vendored
|
@ -11,6 +11,7 @@ return array(
|
|||
'Qferrer\\Mjml\\' => array($vendorDir . '/qferr/mjml-php/src'),
|
||||
'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
|
||||
'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
|
||||
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
|
||||
'PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\' => array($vendorDir . '/dealerdirect/phpcodesniffer-composer-installer/src'),
|
||||
'ML\\JsonLD\\' => array($vendorDir . '/ml/json-ld'),
|
||||
'Jaybizzle\\CrawlerDetect\\' => array($vendorDir . '/jaybizzle/crawler-detect/src'),
|
||||
|
@ -20,5 +21,6 @@ return array(
|
|||
'Gettext\\' => array($vendorDir . '/gettext/gettext/src', $vendorDir . '/gettext/translator/src'),
|
||||
'Fig\\Http\\Message\\' => array($vendorDir . '/fig/http-message-util/src'),
|
||||
'Embed\\' => array($vendorDir . '/embed/embed/src'),
|
||||
'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'),
|
||||
'Composer\\CaBundle\\' => array($vendorDir . '/composer/ca-bundle/src'),
|
||||
);
|
||||
|
|
1103
vendor/composer/autoload_static.php
vendored
1103
vendor/composer/autoload_static.php
vendored
File diff suppressed because it is too large
Load diff
1716
vendor/composer/installed.json
vendored
1716
vendor/composer/installed.json
vendored
File diff suppressed because it is too large
Load diff
238
vendor/composer/installed.php
vendored
238
vendor/composer/installed.php
vendored
|
@ -3,7 +3,7 @@
|
|||
'name' => '__root__',
|
||||
'pretty_version' => 'dev-develop',
|
||||
'version' => 'dev-develop',
|
||||
'reference' => 'f79071d77a00dcf2cb86e6e7011443c8b50bac7c',
|
||||
'reference' => 'e01b7e170146c4ec435ab28dada918ff171996e4',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
|
@ -13,7 +13,7 @@
|
|||
'__root__' => array(
|
||||
'pretty_version' => 'dev-develop',
|
||||
'version' => 'dev-develop',
|
||||
'reference' => 'f79071d77a00dcf2cb86e6e7011443c8b50bac7c',
|
||||
'reference' => 'e01b7e170146c4ec435ab28dada918ff171996e4',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
|
@ -118,6 +118,24 @@
|
|||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'myclabs/deep-copy' => array(
|
||||
'pretty_version' => '1.11.1',
|
||||
'version' => '1.11.1.0',
|
||||
'reference' => '7284c22080590fb39f2ffa3e9057f10a4ddd0e0c',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../myclabs/deep-copy',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'nikic/php-parser' => array(
|
||||
'pretty_version' => 'v4.17.1',
|
||||
'version' => '4.17.1.0',
|
||||
'reference' => 'a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../nikic/php-parser',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'oscarotero/html-parser' => array(
|
||||
'pretty_version' => 'v0.1.7',
|
||||
'version' => '0.1.7.0',
|
||||
|
@ -127,6 +145,24 @@
|
|||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'phar-io/manifest' => array(
|
||||
'pretty_version' => '2.0.3',
|
||||
'version' => '2.0.3.0',
|
||||
'reference' => '97803eca37d319dfa7826cc2437fc020857acb53',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phar-io/manifest',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phar-io/version' => array(
|
||||
'pretty_version' => '3.2.1',
|
||||
'version' => '3.2.1.0',
|
||||
'reference' => '4f7fd7836c6f332bb2933569e566a0d6c4cbed74',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phar-io/version',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpcsstandards/phpcsextra' => array(
|
||||
'pretty_version' => '1.1.1',
|
||||
'version' => '1.1.1.0',
|
||||
|
@ -145,6 +181,60 @@
|
|||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpunit/php-code-coverage' => array(
|
||||
'pretty_version' => '10.1.6',
|
||||
'version' => '10.1.6.0',
|
||||
'reference' => '56f33548fe522c8d82da7ff3824b42829d324364',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpunit/php-code-coverage',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpunit/php-file-iterator' => array(
|
||||
'pretty_version' => '4.1.0',
|
||||
'version' => '4.1.0.0',
|
||||
'reference' => 'a95037b6d9e608ba092da1b23931e537cadc3c3c',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpunit/php-file-iterator',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpunit/php-invoker' => array(
|
||||
'pretty_version' => '4.0.0',
|
||||
'version' => '4.0.0.0',
|
||||
'reference' => 'f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpunit/php-invoker',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpunit/php-text-template' => array(
|
||||
'pretty_version' => '3.0.1',
|
||||
'version' => '3.0.1.0',
|
||||
'reference' => '0c7b06ff49e3d5072f057eb1fa59258bf287a748',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpunit/php-text-template',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpunit/php-timer' => array(
|
||||
'pretty_version' => '6.0.0',
|
||||
'version' => '6.0.0.0',
|
||||
'reference' => 'e2a2d67966e740530f4a3343fe2e030ffdc1161d',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpunit/php-timer',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'phpunit/phpunit' => array(
|
||||
'pretty_version' => '10.0.0',
|
||||
'version' => '10.0.0.0',
|
||||
'reference' => 'c4c60c34c0e92045afce938510f8878d7cd84f8a',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../phpunit/phpunit',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'psr/http-client' => array(
|
||||
'pretty_version' => '1.0.2',
|
||||
'version' => '1.0.2.0',
|
||||
|
@ -202,6 +292,141 @@
|
|||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'sebastian/cli-parser' => array(
|
||||
'pretty_version' => '2.0.0',
|
||||
'version' => '2.0.0.0',
|
||||
'reference' => 'efdc130dbbbb8ef0b545a994fd811725c5282cae',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/cli-parser',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/code-unit' => array(
|
||||
'pretty_version' => '2.0.0',
|
||||
'version' => '2.0.0.0',
|
||||
'reference' => 'a81fee9eef0b7a76af11d121767abc44c104e503',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/code-unit',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/code-unit-reverse-lookup' => array(
|
||||
'pretty_version' => '3.0.0',
|
||||
'version' => '3.0.0.0',
|
||||
'reference' => '5e3a687f7d8ae33fb362c5c0743794bbb2420a1d',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/code-unit-reverse-lookup',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/comparator' => array(
|
||||
'pretty_version' => '5.0.1',
|
||||
'version' => '5.0.1.0',
|
||||
'reference' => '2db5010a484d53ebf536087a70b4a5423c102372',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/comparator',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/complexity' => array(
|
||||
'pretty_version' => '3.0.1',
|
||||
'version' => '3.0.1.0',
|
||||
'reference' => 'c70b73893e10757af9c6a48929fa6a333b56a97a',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/complexity',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/diff' => array(
|
||||
'pretty_version' => '5.0.3',
|
||||
'version' => '5.0.3.0',
|
||||
'reference' => '912dc2fbe3e3c1e7873313cc801b100b6c68c87b',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/diff',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/environment' => array(
|
||||
'pretty_version' => '6.0.1',
|
||||
'version' => '6.0.1.0',
|
||||
'reference' => '43c751b41d74f96cbbd4e07b7aec9675651e2951',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/environment',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/exporter' => array(
|
||||
'pretty_version' => '5.1.0',
|
||||
'version' => '5.1.0.0',
|
||||
'reference' => 'c3fa8483f9539b190f7cd4bfc4a07631dd1df344',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/exporter',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/global-state' => array(
|
||||
'pretty_version' => '6.0.1',
|
||||
'version' => '6.0.1.0',
|
||||
'reference' => '7ea9ead78f6d380d2a667864c132c2f7b83055e4',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/global-state',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/lines-of-code' => array(
|
||||
'pretty_version' => '2.0.1',
|
||||
'version' => '2.0.1.0',
|
||||
'reference' => '649e40d279e243d985aa8fb6e74dd5bb28dc185d',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/lines-of-code',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/object-enumerator' => array(
|
||||
'pretty_version' => '5.0.0',
|
||||
'version' => '5.0.0.0',
|
||||
'reference' => '202d0e344a580d7f7d04b3fafce6933e59dae906',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/object-enumerator',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/object-reflector' => array(
|
||||
'pretty_version' => '3.0.0',
|
||||
'version' => '3.0.0.0',
|
||||
'reference' => '24ed13d98130f0e7122df55d06c5c4942a577957',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/object-reflector',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/recursion-context' => array(
|
||||
'pretty_version' => '5.0.0',
|
||||
'version' => '5.0.0.0',
|
||||
'reference' => '05909fb5bc7df4c52992396d0116aed689f93712',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/recursion-context',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/type' => array(
|
||||
'pretty_version' => '4.0.0',
|
||||
'version' => '4.0.0.0',
|
||||
'reference' => '462699a16464c3944eefc02ebdd77882bd3925bf',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/type',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'sebastian/version' => array(
|
||||
'pretty_version' => '4.0.1',
|
||||
'version' => '4.0.1.0',
|
||||
'reference' => 'c51fa83a5d8f43f1402e3f32a005e6262244ef17',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../sebastian/version',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'slim/psr7' => array(
|
||||
'pretty_version' => '1.6.1',
|
||||
'version' => '1.6.1.0',
|
||||
|
@ -229,6 +454,15 @@
|
|||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'theseer/tokenizer' => array(
|
||||
'pretty_version' => '1.2.1',
|
||||
'version' => '1.2.1.0',
|
||||
'reference' => '34a41e998c2183e22995f158c581e7b5e755ab9e',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../theseer/tokenizer',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => true,
|
||||
),
|
||||
'wp-coding-standards/wpcs' => array(
|
||||
'pretty_version' => '3.0.1',
|
||||
'version' => '3.0.1.0',
|
||||
|
|
12
vendor/myclabs/deep-copy/.github/FUNDING.yml
vendored
Normal file
12
vendor/myclabs/deep-copy/.github/FUNDING.yml
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: "packagist/myclabs/deep-copy"
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
101
vendor/myclabs/deep-copy/.github/workflows/ci.yaml
vendored
Normal file
101
vendor/myclabs/deep-copy/.github/workflows/ci.yaml
vendored
Normal file
|
@ -0,0 +1,101 @@
|
|||
name: "Continuous Integration"
|
||||
|
||||
on:
|
||||
- pull_request
|
||||
- push
|
||||
|
||||
env:
|
||||
COMPOSER_ROOT_VERSION: 1.99
|
||||
|
||||
jobs:
|
||||
composer-json-lint:
|
||||
name: "Lint composer.json"
|
||||
|
||||
runs-on: "ubuntu-latest"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "8.1"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
coverage: "none"
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
tools: composer-normalize
|
||||
|
||||
- name: "Get composer cache directory"
|
||||
id: composercache
|
||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
|
||||
- name: "Cache dependencies"
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.composercache.outputs.dir }}
|
||||
key: ${{ runner.os }}-php-${{ matrix.php-version }}-${{ matrix.dependencies }}-composer-${{ hashFiles('**/composer.json') }}
|
||||
restore-keys: ${{ runner.os }}-php-${{ matrix.php-version }}-${{ matrix.dependencies }}-composer-
|
||||
|
||||
- name: "Install dependencies"
|
||||
run: "composer update --no-interaction --no-progress"
|
||||
|
||||
- name: "Validate composer.json"
|
||||
run: "composer validate --strict"
|
||||
|
||||
- name: "Normalize composer.json"
|
||||
run: "composer-normalize --dry-run"
|
||||
|
||||
tests:
|
||||
name: "Tests"
|
||||
|
||||
runs-on: "ubuntu-latest"
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
php-version:
|
||||
- "7.1"
|
||||
- "7.2"
|
||||
- "7.3"
|
||||
- "7.4"
|
||||
- "8.0"
|
||||
- "8.1"
|
||||
dependencies:
|
||||
- "lowest"
|
||||
- "highest"
|
||||
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: "actions/checkout@v2"
|
||||
|
||||
- name: "Install PHP"
|
||||
uses: "shivammathur/setup-php@v2"
|
||||
with:
|
||||
php-version: "${{ matrix.php-version }}"
|
||||
ini-values: zend.assertions=1
|
||||
|
||||
- name: "Get composer cache directory"
|
||||
id: composercache
|
||||
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
|
||||
|
||||
- name: "Cache dependencies"
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ steps.composercache.outputs.dir }}
|
||||
key: ${{ runner.os }}-php-${{ matrix.php-version }}-${{ matrix.dependencies }}-composer-${{ hashFiles('**/composer.json') }}
|
||||
restore-keys: ${{ runner.os }}-php-${{ matrix.php-version }}-${{ matrix.dependencies }}-composer-
|
||||
|
||||
- name: "Install lowest dependencies"
|
||||
if: ${{ matrix.dependencies == 'lowest' }}
|
||||
run: "composer update --no-interaction --no-progress --prefer-lowest"
|
||||
|
||||
- name: "Install highest dependencies"
|
||||
if: ${{ matrix.dependencies == 'highest' }}
|
||||
run: "composer update --no-interaction --no-progress"
|
||||
|
||||
- name: "Run tests"
|
||||
timeout-minutes: 3
|
||||
run: "vendor/bin/phpunit"
|
20
vendor/myclabs/deep-copy/LICENSE
vendored
Normal file
20
vendor/myclabs/deep-copy/LICENSE
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 My C-Sense
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
406
vendor/myclabs/deep-copy/README.md
vendored
Normal file
406
vendor/myclabs/deep-copy/README.md
vendored
Normal file
|
@ -0,0 +1,406 @@
|
|||
# DeepCopy
|
||||
|
||||
DeepCopy helps you create deep copies (clones) of your objects. It is designed to handle cycles in the association graph.
|
||||
|
||||
[![Total Downloads](https://poser.pugx.org/myclabs/deep-copy/downloads.svg)](https://packagist.org/packages/myclabs/deep-copy)
|
||||
[![Integrate](https://github.com/myclabs/DeepCopy/workflows/ci/badge.svg?branch=1.x)](https://github.com/myclabs/DeepCopy/actions)
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [How](#how)
|
||||
1. [Why](#why)
|
||||
1. [Using simply `clone`](#using-simply-clone)
|
||||
1. [Overriding `__clone()`](#overriding-__clone)
|
||||
1. [With `DeepCopy`](#with-deepcopy)
|
||||
1. [How it works](#how-it-works)
|
||||
1. [Going further](#going-further)
|
||||
1. [Matchers](#matchers)
|
||||
1. [Property name](#property-name)
|
||||
1. [Specific property](#specific-property)
|
||||
1. [Type](#type)
|
||||
1. [Filters](#filters)
|
||||
1. [`SetNullFilter`](#setnullfilter-filter)
|
||||
1. [`KeepFilter`](#keepfilter-filter)
|
||||
1. [`DoctrineCollectionFilter`](#doctrinecollectionfilter-filter)
|
||||
1. [`DoctrineEmptyCollectionFilter`](#doctrineemptycollectionfilter-filter)
|
||||
1. [`DoctrineProxyFilter`](#doctrineproxyfilter-filter)
|
||||
1. [`ReplaceFilter`](#replacefilter-type-filter)
|
||||
1. [`ShallowCopyFilter`](#shallowcopyfilter-type-filter)
|
||||
1. [Edge cases](#edge-cases)
|
||||
1. [Contributing](#contributing)
|
||||
1. [Tests](#tests)
|
||||
|
||||
|
||||
## How?
|
||||
|
||||
Install with Composer:
|
||||
|
||||
```
|
||||
composer require myclabs/deep-copy
|
||||
```
|
||||
|
||||
Use it:
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
|
||||
$copier = new DeepCopy();
|
||||
$myCopy = $copier->copy($myObject);
|
||||
```
|
||||
|
||||
|
||||
## Why?
|
||||
|
||||
- How do you create copies of your objects?
|
||||
|
||||
```php
|
||||
$myCopy = clone $myObject;
|
||||
```
|
||||
|
||||
- How do you create **deep** copies of your objects (i.e. copying also all the objects referenced in the properties)?
|
||||
|
||||
You use [`__clone()`](http://www.php.net/manual/en/language.oop5.cloning.php#object.clone) and implement the behavior
|
||||
yourself.
|
||||
|
||||
- But how do you handle **cycles** in the association graph?
|
||||
|
||||
Now you're in for a big mess :(
|
||||
|
||||
![association graph](doc/graph.png)
|
||||
|
||||
|
||||
### Using simply `clone`
|
||||
|
||||
![Using clone](doc/clone.png)
|
||||
|
||||
|
||||
### Overriding `__clone()`
|
||||
|
||||
![Overriding __clone](doc/deep-clone.png)
|
||||
|
||||
|
||||
### With `DeepCopy`
|
||||
|
||||
![With DeepCopy](doc/deep-copy.png)
|
||||
|
||||
|
||||
## How it works
|
||||
|
||||
DeepCopy recursively traverses all the object's properties and clones them. To avoid cloning the same object twice it
|
||||
keeps a hash map of all instances and thus preserves the object graph.
|
||||
|
||||
To use it:
|
||||
|
||||
```php
|
||||
use function DeepCopy\deep_copy;
|
||||
|
||||
$copy = deep_copy($var);
|
||||
```
|
||||
|
||||
Alternatively, you can create your own `DeepCopy` instance to configure it differently for example:
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
|
||||
$copier = new DeepCopy(true);
|
||||
|
||||
$copy = $copier->copy($var);
|
||||
```
|
||||
|
||||
You may want to roll your own deep copy function:
|
||||
|
||||
```php
|
||||
namespace Acme;
|
||||
|
||||
use DeepCopy\DeepCopy;
|
||||
|
||||
function deep_copy($var)
|
||||
{
|
||||
static $copier = null;
|
||||
|
||||
if (null === $copier) {
|
||||
$copier = new DeepCopy(true);
|
||||
}
|
||||
|
||||
return $copier->copy($var);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Going further
|
||||
|
||||
You can add filters to customize the copy process.
|
||||
|
||||
The method to add a filter is `DeepCopy\DeepCopy::addFilter($filter, $matcher)`,
|
||||
with `$filter` implementing `DeepCopy\Filter\Filter`
|
||||
and `$matcher` implementing `DeepCopy\Matcher\Matcher`.
|
||||
|
||||
We provide some generic filters and matchers.
|
||||
|
||||
|
||||
### Matchers
|
||||
|
||||
- `DeepCopy\Matcher` applies on a object attribute.
|
||||
- `DeepCopy\TypeMatcher` applies on any element found in graph, including array elements.
|
||||
|
||||
|
||||
#### Property name
|
||||
|
||||
The `PropertyNameMatcher` will match a property by its name:
|
||||
|
||||
```php
|
||||
use DeepCopy\Matcher\PropertyNameMatcher;
|
||||
|
||||
// Will apply a filter to any property of any objects named "id"
|
||||
$matcher = new PropertyNameMatcher('id');
|
||||
```
|
||||
|
||||
|
||||
#### Specific property
|
||||
|
||||
The `PropertyMatcher` will match a specific property of a specific class:
|
||||
|
||||
```php
|
||||
use DeepCopy\Matcher\PropertyMatcher;
|
||||
|
||||
// Will apply a filter to the property "id" of any objects of the class "MyClass"
|
||||
$matcher = new PropertyMatcher('MyClass', 'id');
|
||||
```
|
||||
|
||||
|
||||
#### Type
|
||||
|
||||
The `TypeMatcher` will match any element by its type (instance of a class or any value that could be parameter of
|
||||
[gettype()](http://php.net/manual/en/function.gettype.php) function):
|
||||
|
||||
```php
|
||||
use DeepCopy\TypeMatcher\TypeMatcher;
|
||||
|
||||
// Will apply a filter to any object that is an instance of Doctrine\Common\Collections\Collection
|
||||
$matcher = new TypeMatcher('Doctrine\Common\Collections\Collection');
|
||||
```
|
||||
|
||||
|
||||
### Filters
|
||||
|
||||
- `DeepCopy\Filter` applies a transformation to the object attribute matched by `DeepCopy\Matcher`
|
||||
- `DeepCopy\TypeFilter` applies a transformation to any element matched by `DeepCopy\TypeMatcher`
|
||||
|
||||
By design, matching a filter will stop the chain of filters (i.e. the next ones will not be applied).
|
||||
Using the ([`ChainableFilter`](#chainablefilter-filter)) won't stop the chain of filters.
|
||||
|
||||
|
||||
#### `SetNullFilter` (filter)
|
||||
|
||||
Let's say for example that you are copying a database record (or a Doctrine entity), so you want the copy not to have
|
||||
any ID:
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\Filter\SetNullFilter;
|
||||
use DeepCopy\Matcher\PropertyNameMatcher;
|
||||
|
||||
$object = MyClass::load(123);
|
||||
echo $object->id; // 123
|
||||
|
||||
$copier = new DeepCopy();
|
||||
$copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id'));
|
||||
|
||||
$copy = $copier->copy($object);
|
||||
|
||||
echo $copy->id; // null
|
||||
```
|
||||
|
||||
|
||||
#### `KeepFilter` (filter)
|
||||
|
||||
If you want a property to remain untouched (for example, an association to an object):
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\Filter\KeepFilter;
|
||||
use DeepCopy\Matcher\PropertyMatcher;
|
||||
|
||||
$copier = new DeepCopy();
|
||||
$copier->addFilter(new KeepFilter(), new PropertyMatcher('MyClass', 'category'));
|
||||
|
||||
$copy = $copier->copy($object);
|
||||
// $copy->category has not been touched
|
||||
```
|
||||
|
||||
|
||||
#### `ChainableFilter` (filter)
|
||||
|
||||
If you use cloning on proxy classes, you might want to apply two filters for:
|
||||
1. loading the data
|
||||
2. applying a transformation
|
||||
|
||||
You can use the `ChainableFilter` as a decorator of the proxy loader filter, which won't stop the chain of filters (i.e.
|
||||
the next ones may be applied).
|
||||
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\Filter\ChainableFilter;
|
||||
use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
|
||||
use DeepCopy\Filter\SetNullFilter;
|
||||
use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
|
||||
use DeepCopy\Matcher\PropertyNameMatcher;
|
||||
|
||||
$copier = new DeepCopy();
|
||||
$copier->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher());
|
||||
$copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id'));
|
||||
|
||||
$copy = $copier->copy($object);
|
||||
|
||||
echo $copy->id; // null
|
||||
```
|
||||
|
||||
|
||||
#### `DoctrineCollectionFilter` (filter)
|
||||
|
||||
If you use Doctrine and want to copy an entity, you will need to use the `DoctrineCollectionFilter`:
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\Filter\Doctrine\DoctrineCollectionFilter;
|
||||
use DeepCopy\Matcher\PropertyTypeMatcher;
|
||||
|
||||
$copier = new DeepCopy();
|
||||
$copier->addFilter(new DoctrineCollectionFilter(), new PropertyTypeMatcher('Doctrine\Common\Collections\Collection'));
|
||||
|
||||
$copy = $copier->copy($object);
|
||||
```
|
||||
|
||||
|
||||
#### `DoctrineEmptyCollectionFilter` (filter)
|
||||
|
||||
If you use Doctrine and want to copy an entity who contains a `Collection` that you want to be reset, you can use the
|
||||
`DoctrineEmptyCollectionFilter`
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\Filter\Doctrine\DoctrineEmptyCollectionFilter;
|
||||
use DeepCopy\Matcher\PropertyMatcher;
|
||||
|
||||
$copier = new DeepCopy();
|
||||
$copier->addFilter(new DoctrineEmptyCollectionFilter(), new PropertyMatcher('MyClass', 'myProperty'));
|
||||
|
||||
$copy = $copier->copy($object);
|
||||
|
||||
// $copy->myProperty will return an empty collection
|
||||
```
|
||||
|
||||
|
||||
#### `DoctrineProxyFilter` (filter)
|
||||
|
||||
If you use Doctrine and use cloning on lazy loaded entities, you might encounter errors mentioning missing fields on a
|
||||
Doctrine proxy class (...\\\_\_CG\_\_\Proxy).
|
||||
You can use the `DoctrineProxyFilter` to load the actual entity behind the Doctrine proxy class.
|
||||
**Make sure, though, to put this as one of your very first filters in the filter chain so that the entity is loaded
|
||||
before other filters are applied!**
|
||||
We recommend to decorate the `DoctrineProxyFilter` with the `ChainableFilter` to allow applying other filters to the
|
||||
cloned lazy loaded entities.
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
|
||||
use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
|
||||
|
||||
$copier = new DeepCopy();
|
||||
$copier->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher());
|
||||
|
||||
$copy = $copier->copy($object);
|
||||
|
||||
// $copy should now contain a clone of all entities, including those that were not yet fully loaded.
|
||||
```
|
||||
|
||||
|
||||
#### `ReplaceFilter` (type filter)
|
||||
|
||||
1. If you want to replace the value of a property:
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\Filter\ReplaceFilter;
|
||||
use DeepCopy\Matcher\PropertyMatcher;
|
||||
|
||||
$copier = new DeepCopy();
|
||||
$callback = function ($currentValue) {
|
||||
return $currentValue . ' (copy)'
|
||||
};
|
||||
$copier->addFilter(new ReplaceFilter($callback), new PropertyMatcher('MyClass', 'title'));
|
||||
|
||||
$copy = $copier->copy($object);
|
||||
|
||||
// $copy->title will contain the data returned by the callback, e.g. 'The title (copy)'
|
||||
```
|
||||
|
||||
2. If you want to replace whole element:
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\TypeFilter\ReplaceFilter;
|
||||
use DeepCopy\TypeMatcher\TypeMatcher;
|
||||
|
||||
$copier = new DeepCopy();
|
||||
$callback = function (MyClass $myClass) {
|
||||
return get_class($myClass);
|
||||
};
|
||||
$copier->addTypeFilter(new ReplaceFilter($callback), new TypeMatcher('MyClass'));
|
||||
|
||||
$copy = $copier->copy([new MyClass, 'some string', new MyClass]);
|
||||
|
||||
// $copy will contain ['MyClass', 'some string', 'MyClass']
|
||||
```
|
||||
|
||||
|
||||
The `$callback` parameter of the `ReplaceFilter` constructor accepts any PHP callable.
|
||||
|
||||
|
||||
#### `ShallowCopyFilter` (type filter)
|
||||
|
||||
Stop *DeepCopy* from recursively copying element, using standard `clone` instead:
|
||||
|
||||
```php
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\TypeFilter\ShallowCopyFilter;
|
||||
use DeepCopy\TypeMatcher\TypeMatcher;
|
||||
use Mockery as m;
|
||||
|
||||
$this->deepCopy = new DeepCopy();
|
||||
$this->deepCopy->addTypeFilter(
|
||||
new ShallowCopyFilter,
|
||||
new TypeMatcher(m\MockInterface::class)
|
||||
);
|
||||
|
||||
$myServiceWithMocks = new MyService(m::mock(MyDependency1::class), m::mock(MyDependency2::class));
|
||||
// All mocks will be just cloned, not deep copied
|
||||
```
|
||||
|
||||
|
||||
## Edge cases
|
||||
|
||||
The following structures cannot be deep-copied with PHP Reflection. As a result they are shallow cloned and filters are
|
||||
not applied. There is two ways for you to handle them:
|
||||
|
||||
- Implement your own `__clone()` method
|
||||
- Use a filter with a type matcher
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
DeepCopy is distributed under the MIT license.
|
||||
|
||||
|
||||
### Tests
|
||||
|
||||
Running the tests is simple:
|
||||
|
||||
```php
|
||||
vendor/bin/phpunit
|
||||
```
|
||||
|
||||
### Support
|
||||
|
||||
Get professional support via [the Tidelift Subscription](https://tidelift.com/subscription/pkg/packagist-myclabs-deep-copy?utm_source=packagist-myclabs-deep-copy&utm_medium=referral&utm_campaign=readme).
|
42
vendor/myclabs/deep-copy/composer.json
vendored
Normal file
42
vendor/myclabs/deep-copy/composer.json
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
"description": "Create deep copies (clones) of your objects",
|
||||
"license": "MIT",
|
||||
"type": "library",
|
||||
"keywords": [
|
||||
"clone",
|
||||
"copy",
|
||||
"duplicate",
|
||||
"object",
|
||||
"object graph"
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.1 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/collections": "^1.6.8",
|
||||
"doctrine/common": "^2.13.3 || ^3.2.2",
|
||||
"phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
|
||||
},
|
||||
"conflict": {
|
||||
"doctrine/collections": "<1.6.8",
|
||||
"doctrine/common": "<2.13.3 || >=3,<3.2.2"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"DeepCopy\\": "src/DeepCopy/"
|
||||
},
|
||||
"files": [
|
||||
"src/DeepCopy/deep_copy.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"DeepCopy\\": "fixtures/",
|
||||
"DeepCopyTest\\": "tests/DeepCopyTest/"
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
}
|
||||
}
|
308
vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php
vendored
Normal file
308
vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php
vendored
Normal file
|
@ -0,0 +1,308 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy;
|
||||
|
||||
use ArrayObject;
|
||||
use DateInterval;
|
||||
use DateTimeInterface;
|
||||
use DateTimeZone;
|
||||
use DeepCopy\Exception\CloneException;
|
||||
use DeepCopy\Filter\ChainableFilter;
|
||||
use DeepCopy\Filter\Filter;
|
||||
use DeepCopy\Matcher\Matcher;
|
||||
use DeepCopy\Reflection\ReflectionHelper;
|
||||
use DeepCopy\TypeFilter\Date\DateIntervalFilter;
|
||||
use DeepCopy\TypeFilter\Spl\ArrayObjectFilter;
|
||||
use DeepCopy\TypeFilter\Spl\SplDoublyLinkedListFilter;
|
||||
use DeepCopy\TypeFilter\TypeFilter;
|
||||
use DeepCopy\TypeMatcher\TypeMatcher;
|
||||
use ReflectionObject;
|
||||
use ReflectionProperty;
|
||||
use SplDoublyLinkedList;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class DeepCopy
|
||||
{
|
||||
/**
|
||||
* @var object[] List of objects copied.
|
||||
*/
|
||||
private $hashMap = [];
|
||||
|
||||
/**
|
||||
* Filters to apply.
|
||||
*
|
||||
* @var array Array of ['filter' => Filter, 'matcher' => Matcher] pairs.
|
||||
*/
|
||||
private $filters = [];
|
||||
|
||||
/**
|
||||
* Type Filters to apply.
|
||||
*
|
||||
* @var array Array of ['filter' => Filter, 'matcher' => Matcher] pairs.
|
||||
*/
|
||||
private $typeFilters = [];
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $skipUncloneable = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $useCloneMethod;
|
||||
|
||||
/**
|
||||
* @param bool $useCloneMethod If set to true, when an object implements the __clone() function, it will be used
|
||||
* instead of the regular deep cloning.
|
||||
*/
|
||||
public function __construct($useCloneMethod = false)
|
||||
{
|
||||
$this->useCloneMethod = $useCloneMethod;
|
||||
|
||||
$this->addTypeFilter(new ArrayObjectFilter($this), new TypeMatcher(ArrayObject::class));
|
||||
$this->addTypeFilter(new DateIntervalFilter(), new TypeMatcher(DateInterval::class));
|
||||
$this->addTypeFilter(new SplDoublyLinkedListFilter($this), new TypeMatcher(SplDoublyLinkedList::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* If enabled, will not throw an exception when coming across an uncloneable property.
|
||||
*
|
||||
* @param $skipUncloneable
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function skipUncloneable($skipUncloneable = true)
|
||||
{
|
||||
$this->skipUncloneable = $skipUncloneable;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deep copies the given object.
|
||||
*
|
||||
* @param mixed $object
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function copy($object)
|
||||
{
|
||||
$this->hashMap = [];
|
||||
|
||||
return $this->recursiveCopy($object);
|
||||
}
|
||||
|
||||
public function addFilter(Filter $filter, Matcher $matcher)
|
||||
{
|
||||
$this->filters[] = [
|
||||
'matcher' => $matcher,
|
||||
'filter' => $filter,
|
||||
];
|
||||
}
|
||||
|
||||
public function prependFilter(Filter $filter, Matcher $matcher)
|
||||
{
|
||||
array_unshift($this->filters, [
|
||||
'matcher' => $matcher,
|
||||
'filter' => $filter,
|
||||
]);
|
||||
}
|
||||
|
||||
public function addTypeFilter(TypeFilter $filter, TypeMatcher $matcher)
|
||||
{
|
||||
$this->typeFilters[] = [
|
||||
'matcher' => $matcher,
|
||||
'filter' => $filter,
|
||||
];
|
||||
}
|
||||
|
||||
private function recursiveCopy($var)
|
||||
{
|
||||
// Matches Type Filter
|
||||
if ($filter = $this->getFirstMatchedTypeFilter($this->typeFilters, $var)) {
|
||||
return $filter->apply($var);
|
||||
}
|
||||
|
||||
// Resource
|
||||
if (is_resource($var)) {
|
||||
return $var;
|
||||
}
|
||||
|
||||
// Array
|
||||
if (is_array($var)) {
|
||||
return $this->copyArray($var);
|
||||
}
|
||||
|
||||
// Scalar
|
||||
if (! is_object($var)) {
|
||||
return $var;
|
||||
}
|
||||
|
||||
// Enum
|
||||
if (PHP_VERSION_ID >= 80100 && enum_exists(get_class($var))) {
|
||||
return $var;
|
||||
}
|
||||
|
||||
// Object
|
||||
return $this->copyObject($var);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an array
|
||||
* @param array $array
|
||||
* @return array
|
||||
*/
|
||||
private function copyArray(array $array)
|
||||
{
|
||||
foreach ($array as $key => $value) {
|
||||
$array[$key] = $this->recursiveCopy($value);
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies an object.
|
||||
*
|
||||
* @param object $object
|
||||
*
|
||||
* @throws CloneException
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
private function copyObject($object)
|
||||
{
|
||||
$objectHash = spl_object_hash($object);
|
||||
|
||||
if (isset($this->hashMap[$objectHash])) {
|
||||
return $this->hashMap[$objectHash];
|
||||
}
|
||||
|
||||
$reflectedObject = new ReflectionObject($object);
|
||||
$isCloneable = $reflectedObject->isCloneable();
|
||||
|
||||
if (false === $isCloneable) {
|
||||
if ($this->skipUncloneable) {
|
||||
$this->hashMap[$objectHash] = $object;
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
throw new CloneException(
|
||||
sprintf(
|
||||
'The class "%s" is not cloneable.',
|
||||
$reflectedObject->getName()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$newObject = clone $object;
|
||||
$this->hashMap[$objectHash] = $newObject;
|
||||
|
||||
if ($this->useCloneMethod && $reflectedObject->hasMethod('__clone')) {
|
||||
return $newObject;
|
||||
}
|
||||
|
||||
if ($newObject instanceof DateTimeInterface || $newObject instanceof DateTimeZone) {
|
||||
return $newObject;
|
||||
}
|
||||
|
||||
foreach (ReflectionHelper::getProperties($reflectedObject) as $property) {
|
||||
$this->copyObjectProperty($newObject, $property);
|
||||
}
|
||||
|
||||
return $newObject;
|
||||
}
|
||||
|
||||
private function copyObjectProperty($object, ReflectionProperty $property)
|
||||
{
|
||||
// Ignore static properties
|
||||
if ($property->isStatic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply the filters
|
||||
foreach ($this->filters as $item) {
|
||||
/** @var Matcher $matcher */
|
||||
$matcher = $item['matcher'];
|
||||
/** @var Filter $filter */
|
||||
$filter = $item['filter'];
|
||||
|
||||
if ($matcher->matches($object, $property->getName())) {
|
||||
$filter->apply(
|
||||
$object,
|
||||
$property->getName(),
|
||||
function ($object) {
|
||||
return $this->recursiveCopy($object);
|
||||
}
|
||||
);
|
||||
|
||||
if ($filter instanceof ChainableFilter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If a filter matches, we stop processing this property
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$property->setAccessible(true);
|
||||
|
||||
// Ignore uninitialized properties (for PHP >7.4)
|
||||
if (method_exists($property, 'isInitialized') && !$property->isInitialized($object)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$propertyValue = $property->getValue($object);
|
||||
|
||||
// Copy the property
|
||||
$property->setValue($object, $this->recursiveCopy($propertyValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns first filter that matches variable, `null` if no such filter found.
|
||||
*
|
||||
* @param array $filterRecords Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and
|
||||
* 'matcher' with value of type {@see TypeMatcher}
|
||||
* @param mixed $var
|
||||
*
|
||||
* @return TypeFilter|null
|
||||
*/
|
||||
private function getFirstMatchedTypeFilter(array $filterRecords, $var)
|
||||
{
|
||||
$matched = $this->first(
|
||||
$filterRecords,
|
||||
function (array $record) use ($var) {
|
||||
/* @var TypeMatcher $matcher */
|
||||
$matcher = $record['matcher'];
|
||||
|
||||
return $matcher->matches($var);
|
||||
}
|
||||
);
|
||||
|
||||
return isset($matched) ? $matched['filter'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns first element that matches predicate, `null` if no such element found.
|
||||
*
|
||||
* @param array $elements Array of ['filter' => Filter, 'matcher' => Matcher] pairs.
|
||||
* @param callable $predicate Predicate arguments are: element.
|
||||
*
|
||||
* @return array|null Associative array with 2 members: 'filter' with value of type {@see TypeFilter} and 'matcher'
|
||||
* with value of type {@see TypeMatcher} or `null`.
|
||||
*/
|
||||
private function first(array $elements, callable $predicate)
|
||||
{
|
||||
foreach ($elements as $element) {
|
||||
if (call_user_func($predicate, $element)) {
|
||||
return $element;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
9
vendor/myclabs/deep-copy/src/DeepCopy/Exception/CloneException.php
vendored
Normal file
9
vendor/myclabs/deep-copy/src/DeepCopy/Exception/CloneException.php
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\Exception;
|
||||
|
||||
use UnexpectedValueException;
|
||||
|
||||
class CloneException extends UnexpectedValueException
|
||||
{
|
||||
}
|
9
vendor/myclabs/deep-copy/src/DeepCopy/Exception/PropertyException.php
vendored
Normal file
9
vendor/myclabs/deep-copy/src/DeepCopy/Exception/PropertyException.php
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\Exception;
|
||||
|
||||
use ReflectionException;
|
||||
|
||||
class PropertyException extends ReflectionException
|
||||
{
|
||||
}
|
24
vendor/myclabs/deep-copy/src/DeepCopy/Filter/ChainableFilter.php
vendored
Normal file
24
vendor/myclabs/deep-copy/src/DeepCopy/Filter/ChainableFilter.php
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\Filter;
|
||||
|
||||
/**
|
||||
* Defines a decorator filter that will not stop the chain of filters.
|
||||
*/
|
||||
class ChainableFilter implements Filter
|
||||
{
|
||||
/**
|
||||
* @var Filter
|
||||
*/
|
||||
protected $filter;
|
||||
|
||||
public function __construct(Filter $filter)
|
||||
{
|
||||
$this->filter = $filter;
|
||||
}
|
||||
|
||||
public function apply($object, $property, $objectCopier)
|
||||
{
|
||||
$this->filter->apply($object, $property, $objectCopier);
|
||||
}
|
||||
}
|
33
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php
vendored
Normal file
33
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineCollectionFilter.php
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\Filter\Doctrine;
|
||||
|
||||
use DeepCopy\Filter\Filter;
|
||||
use DeepCopy\Reflection\ReflectionHelper;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class DoctrineCollectionFilter implements Filter
|
||||
{
|
||||
/**
|
||||
* Copies the object property doctrine collection.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply($object, $property, $objectCopier)
|
||||
{
|
||||
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
|
||||
|
||||
$reflectionProperty->setAccessible(true);
|
||||
$oldCollection = $reflectionProperty->getValue($object);
|
||||
|
||||
$newCollection = $oldCollection->map(
|
||||
function ($item) use ($objectCopier) {
|
||||
return $objectCopier($item);
|
||||
}
|
||||
);
|
||||
|
||||
$reflectionProperty->setValue($object, $newCollection);
|
||||
}
|
||||
}
|
28
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php
vendored
Normal file
28
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineEmptyCollectionFilter.php
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\Filter\Doctrine;
|
||||
|
||||
use DeepCopy\Filter\Filter;
|
||||
use DeepCopy\Reflection\ReflectionHelper;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class DoctrineEmptyCollectionFilter implements Filter
|
||||
{
|
||||
/**
|
||||
* Sets the object property to an empty doctrine collection.
|
||||
*
|
||||
* @param object $object
|
||||
* @param string $property
|
||||
* @param callable $objectCopier
|
||||
*/
|
||||
public function apply($object, $property, $objectCopier)
|
||||
{
|
||||
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
|
||||
$reflectionProperty->setAccessible(true);
|
||||
|
||||
$reflectionProperty->setValue($object, new ArrayCollection());
|
||||
}
|
||||
}
|
22
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php
vendored
Normal file
22
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Doctrine/DoctrineProxyFilter.php
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\Filter\Doctrine;
|
||||
|
||||
use DeepCopy\Filter\Filter;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class DoctrineProxyFilter implements Filter
|
||||
{
|
||||
/**
|
||||
* Triggers the magic method __load() on a Doctrine Proxy class to load the
|
||||
* actual entity from the database.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply($object, $property, $objectCopier)
|
||||
{
|
||||
$object->__load();
|
||||
}
|
||||
}
|
18
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Filter.php
vendored
Normal file
18
vendor/myclabs/deep-copy/src/DeepCopy/Filter/Filter.php
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\Filter;
|
||||
|
||||
/**
|
||||
* Filter to apply to a property while copying an object
|
||||
*/
|
||||
interface Filter
|
||||
{
|
||||
/**
|
||||
* Applies the filter to the object.
|
||||
*
|
||||
* @param object $object
|
||||
* @param string $property
|
||||
* @param callable $objectCopier
|
||||
*/
|
||||
public function apply($object, $property, $objectCopier);
|
||||
}
|
16
vendor/myclabs/deep-copy/src/DeepCopy/Filter/KeepFilter.php
vendored
Normal file
16
vendor/myclabs/deep-copy/src/DeepCopy/Filter/KeepFilter.php
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\Filter;
|
||||
|
||||
class KeepFilter implements Filter
|
||||
{
|
||||
/**
|
||||
* Keeps the value of the object property.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply($object, $property, $objectCopier)
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
||||
}
|
39
vendor/myclabs/deep-copy/src/DeepCopy/Filter/ReplaceFilter.php
vendored
Normal file
39
vendor/myclabs/deep-copy/src/DeepCopy/Filter/ReplaceFilter.php
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\Filter;
|
||||
|
||||
use DeepCopy\Reflection\ReflectionHelper;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class ReplaceFilter implements Filter
|
||||
{
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
protected $callback;
|
||||
|
||||
/**
|
||||
* @param callable $callable Will be called to get the new value for each property to replace
|
||||
*/
|
||||
public function __construct(callable $callable)
|
||||
{
|
||||
$this->callback = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the object property by the result of the callback called with the object property.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply($object, $property, $objectCopier)
|
||||
{
|
||||
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
|
||||
$reflectionProperty->setAccessible(true);
|
||||
|
||||
$value = call_user_func($this->callback, $reflectionProperty->getValue($object));
|
||||
|
||||
$reflectionProperty->setValue($object, $value);
|
||||
}
|
||||
}
|
24
vendor/myclabs/deep-copy/src/DeepCopy/Filter/SetNullFilter.php
vendored
Normal file
24
vendor/myclabs/deep-copy/src/DeepCopy/Filter/SetNullFilter.php
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\Filter;
|
||||
|
||||
use DeepCopy\Reflection\ReflectionHelper;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class SetNullFilter implements Filter
|
||||
{
|
||||
/**
|
||||
* Sets the object property to null.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply($object, $property, $objectCopier)
|
||||
{
|
||||
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
|
||||
|
||||
$reflectionProperty->setAccessible(true);
|
||||
$reflectionProperty->setValue($object, null);
|
||||
}
|
||||
}
|
22
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php
vendored
Normal file
22
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Doctrine/DoctrineProxyMatcher.php
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\Matcher\Doctrine;
|
||||
|
||||
use DeepCopy\Matcher\Matcher;
|
||||
use Doctrine\Persistence\Proxy;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class DoctrineProxyMatcher implements Matcher
|
||||
{
|
||||
/**
|
||||
* Matches a Doctrine Proxy class.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function matches($object, $property)
|
||||
{
|
||||
return $object instanceof Proxy;
|
||||
}
|
||||
}
|
14
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Matcher.php
vendored
Normal file
14
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/Matcher.php
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\Matcher;
|
||||
|
||||
interface Matcher
|
||||
{
|
||||
/**
|
||||
* @param object $object
|
||||
* @param string $property
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function matches($object, $property);
|
||||
}
|
39
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyMatcher.php
vendored
Normal file
39
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyMatcher.php
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\Matcher;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class PropertyMatcher implements Matcher
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $class;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $property;
|
||||
|
||||
/**
|
||||
* @param string $class Class name
|
||||
* @param string $property Property name
|
||||
*/
|
||||
public function __construct($class, $property)
|
||||
{
|
||||
$this->class = $class;
|
||||
$this->property = $property;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches a specific property of a specific class.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function matches($object, $property)
|
||||
{
|
||||
return ($object instanceof $this->class) && $property == $this->property;
|
||||
}
|
||||
}
|
32
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyNameMatcher.php
vendored
Normal file
32
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyNameMatcher.php
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\Matcher;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class PropertyNameMatcher implements Matcher
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $property;
|
||||
|
||||
/**
|
||||
* @param string $property Property name
|
||||
*/
|
||||
public function __construct($property)
|
||||
{
|
||||
$this->property = $property;
|
||||
}
|
||||
|
||||
/**
|
||||
* Matches a property by its name.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function matches($object, $property)
|
||||
{
|
||||
return $property == $this->property;
|
||||
}
|
||||
}
|
52
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyTypeMatcher.php
vendored
Normal file
52
vendor/myclabs/deep-copy/src/DeepCopy/Matcher/PropertyTypeMatcher.php
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\Matcher;
|
||||
|
||||
use DeepCopy\Reflection\ReflectionHelper;
|
||||
use ReflectionException;
|
||||
|
||||
/**
|
||||
* Matches a property by its type.
|
||||
*
|
||||
* It is recommended to use {@see DeepCopy\TypeFilter\TypeFilter} instead, as it applies on all occurrences
|
||||
* of given type in copied context (eg. array elements), not just on object properties.
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class PropertyTypeMatcher implements Matcher
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $propertyType;
|
||||
|
||||
/**
|
||||
* @param string $propertyType Property type
|
||||
*/
|
||||
public function __construct($propertyType)
|
||||
{
|
||||
$this->propertyType = $propertyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function matches($object, $property)
|
||||
{
|
||||
try {
|
||||
$reflectionProperty = ReflectionHelper::getProperty($object, $property);
|
||||
} catch (ReflectionException $exception) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$reflectionProperty->setAccessible(true);
|
||||
|
||||
// Uninitialized properties (for PHP >7.4)
|
||||
if (method_exists($reflectionProperty, 'isInitialized') && !$reflectionProperty->isInitialized($object)) {
|
||||
// null instanceof $this->propertyType
|
||||
return false;
|
||||
}
|
||||
|
||||
return $reflectionProperty->getValue($object) instanceof $this->propertyType;
|
||||
}
|
||||
}
|
78
vendor/myclabs/deep-copy/src/DeepCopy/Reflection/ReflectionHelper.php
vendored
Normal file
78
vendor/myclabs/deep-copy/src/DeepCopy/Reflection/ReflectionHelper.php
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\Reflection;
|
||||
|
||||
use DeepCopy\Exception\PropertyException;
|
||||
use ReflectionClass;
|
||||
use ReflectionException;
|
||||
use ReflectionObject;
|
||||
use ReflectionProperty;
|
||||
|
||||
class ReflectionHelper
|
||||
{
|
||||
/**
|
||||
* Retrieves all properties (including private ones), from object and all its ancestors.
|
||||
*
|
||||
* Standard \ReflectionClass->getProperties() does not return private properties from ancestor classes.
|
||||
*
|
||||
* @author muratyaman@gmail.com
|
||||
* @see http://php.net/manual/en/reflectionclass.getproperties.php
|
||||
*
|
||||
* @param ReflectionClass $ref
|
||||
*
|
||||
* @return ReflectionProperty[]
|
||||
*/
|
||||
public static function getProperties(ReflectionClass $ref)
|
||||
{
|
||||
$props = $ref->getProperties();
|
||||
$propsArr = array();
|
||||
|
||||
foreach ($props as $prop) {
|
||||
$propertyName = $prop->getName();
|
||||
$propsArr[$propertyName] = $prop;
|
||||
}
|
||||
|
||||
if ($parentClass = $ref->getParentClass()) {
|
||||
$parentPropsArr = self::getProperties($parentClass);
|
||||
foreach ($propsArr as $key => $property) {
|
||||
$parentPropsArr[$key] = $property;
|
||||
}
|
||||
|
||||
return $parentPropsArr;
|
||||
}
|
||||
|
||||
return $propsArr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves property by name from object and all its ancestors.
|
||||
*
|
||||
* @param object|string $object
|
||||
* @param string $name
|
||||
*
|
||||
* @throws PropertyException
|
||||
* @throws ReflectionException
|
||||
*
|
||||
* @return ReflectionProperty
|
||||
*/
|
||||
public static function getProperty($object, $name)
|
||||
{
|
||||
$reflection = is_object($object) ? new ReflectionObject($object) : new ReflectionClass($object);
|
||||
|
||||
if ($reflection->hasProperty($name)) {
|
||||
return $reflection->getProperty($name);
|
||||
}
|
||||
|
||||
if ($parentClass = $reflection->getParentClass()) {
|
||||
return self::getProperty($parentClass->getName(), $name);
|
||||
}
|
||||
|
||||
throw new PropertyException(
|
||||
sprintf(
|
||||
'The class "%s" doesn\'t have a property with the given name: "%s".',
|
||||
is_object($object) ? get_class($object) : $object,
|
||||
$name
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
33
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DateIntervalFilter.php
vendored
Normal file
33
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Date/DateIntervalFilter.php
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\TypeFilter\Date;
|
||||
|
||||
use DateInterval;
|
||||
use DeepCopy\TypeFilter\TypeFilter;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*
|
||||
* @deprecated Will be removed in 2.0. This filter will no longer be necessary in PHP 7.1+.
|
||||
*/
|
||||
class DateIntervalFilter implements TypeFilter
|
||||
{
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param DateInterval $element
|
||||
*
|
||||
* @see http://news.php.net/php.bugs/205076
|
||||
*/
|
||||
public function apply($element)
|
||||
{
|
||||
$copy = new DateInterval('P0D');
|
||||
|
||||
foreach ($element as $propertyName => $propertyValue) {
|
||||
$copy->{$propertyName} = $propertyValue;
|
||||
}
|
||||
|
||||
return $copy;
|
||||
}
|
||||
}
|
30
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ReplaceFilter.php
vendored
Normal file
30
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ReplaceFilter.php
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\TypeFilter;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class ReplaceFilter implements TypeFilter
|
||||
{
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
protected $callback;
|
||||
|
||||
/**
|
||||
* @param callable $callable Will be called to get the new value for each element to replace
|
||||
*/
|
||||
public function __construct(callable $callable)
|
||||
{
|
||||
$this->callback = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply($element)
|
||||
{
|
||||
return call_user_func($this->callback, $element);
|
||||
}
|
||||
}
|
17
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ShallowCopyFilter.php
vendored
Normal file
17
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/ShallowCopyFilter.php
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\TypeFilter;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class ShallowCopyFilter implements TypeFilter
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply($element)
|
||||
{
|
||||
return clone $element;
|
||||
}
|
||||
}
|
36
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php
vendored
Normal file
36
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/ArrayObjectFilter.php
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
namespace DeepCopy\TypeFilter\Spl;
|
||||
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\TypeFilter\TypeFilter;
|
||||
|
||||
/**
|
||||
* In PHP 7.4 the storage of an ArrayObject isn't returned as
|
||||
* ReflectionProperty. So we deep copy its array copy.
|
||||
*/
|
||||
final class ArrayObjectFilter implements TypeFilter
|
||||
{
|
||||
/**
|
||||
* @var DeepCopy
|
||||
*/
|
||||
private $copier;
|
||||
|
||||
public function __construct(DeepCopy $copier)
|
||||
{
|
||||
$this->copier = $copier;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply($arrayObject)
|
||||
{
|
||||
$clone = clone $arrayObject;
|
||||
foreach ($arrayObject->getArrayCopy() as $k => $v) {
|
||||
$clone->offsetSet($k, $this->copier->copy($v));
|
||||
}
|
||||
|
||||
return $clone;
|
||||
}
|
||||
}
|
||||
|
10
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php
vendored
Normal file
10
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedList.php
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\TypeFilter\Spl;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@see SplDoublyLinkedListFilter} instead.
|
||||
*/
|
||||
class SplDoublyLinkedList extends SplDoublyLinkedListFilter
|
||||
{
|
||||
}
|
51
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php
vendored
Normal file
51
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\TypeFilter\Spl;
|
||||
|
||||
use Closure;
|
||||
use DeepCopy\DeepCopy;
|
||||
use DeepCopy\TypeFilter\TypeFilter;
|
||||
use SplDoublyLinkedList;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class SplDoublyLinkedListFilter implements TypeFilter
|
||||
{
|
||||
private $copier;
|
||||
|
||||
public function __construct(DeepCopy $copier)
|
||||
{
|
||||
$this->copier = $copier;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apply($element)
|
||||
{
|
||||
$newElement = clone $element;
|
||||
|
||||
$copy = $this->createCopyClosure();
|
||||
|
||||
return $copy($newElement);
|
||||
}
|
||||
|
||||
private function createCopyClosure()
|
||||
{
|
||||
$copier = $this->copier;
|
||||
|
||||
$copy = function (SplDoublyLinkedList $list) use ($copier) {
|
||||
// Replace each element in the list with a deep copy of itself
|
||||
for ($i = 1; $i <= $list->count(); $i++) {
|
||||
$copy = $copier->recursiveCopy($list->shift());
|
||||
|
||||
$list->push($copy);
|
||||
}
|
||||
|
||||
return $list;
|
||||
};
|
||||
|
||||
return Closure::bind($copy, null, DeepCopy::class);
|
||||
}
|
||||
}
|
13
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/TypeFilter.php
vendored
Normal file
13
vendor/myclabs/deep-copy/src/DeepCopy/TypeFilter/TypeFilter.php
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\TypeFilter;
|
||||
|
||||
interface TypeFilter
|
||||
{
|
||||
/**
|
||||
* Applies the filter to the object.
|
||||
*
|
||||
* @param mixed $element
|
||||
*/
|
||||
public function apply($element);
|
||||
}
|
29
vendor/myclabs/deep-copy/src/DeepCopy/TypeMatcher/TypeMatcher.php
vendored
Normal file
29
vendor/myclabs/deep-copy/src/DeepCopy/TypeMatcher/TypeMatcher.php
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy\TypeMatcher;
|
||||
|
||||
class TypeMatcher
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
*/
|
||||
public function __construct($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $element
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function matches($element)
|
||||
{
|
||||
return is_object($element) ? is_a($element, $this->type) : gettype($element) === $this->type;
|
||||
}
|
||||
}
|
20
vendor/myclabs/deep-copy/src/DeepCopy/deep_copy.php
vendored
Normal file
20
vendor/myclabs/deep-copy/src/DeepCopy/deep_copy.php
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace DeepCopy;
|
||||
|
||||
use function function_exists;
|
||||
|
||||
if (false === function_exists('DeepCopy\deep_copy')) {
|
||||
/**
|
||||
* Deep copies the given value.
|
||||
*
|
||||
* @param mixed $value
|
||||
* @param bool $useCloneMethod
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
function deep_copy($value, $useCloneMethod = false)
|
||||
{
|
||||
return (new DeepCopy($useCloneMethod))->copy($value);
|
||||
}
|
||||
}
|
29
vendor/nikic/php-parser/LICENSE
vendored
Normal file
29
vendor/nikic/php-parser/LICENSE
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2011, Nikita Popov
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
225
vendor/nikic/php-parser/README.md
vendored
Normal file
225
vendor/nikic/php-parser/README.md
vendored
Normal file
|
@ -0,0 +1,225 @@
|
|||
PHP Parser
|
||||
==========
|
||||
|
||||
[![Coverage Status](https://coveralls.io/repos/github/nikic/PHP-Parser/badge.svg?branch=master)](https://coveralls.io/github/nikic/PHP-Parser?branch=master)
|
||||
|
||||
This is a PHP 5.2 to PHP 8.2 parser written in PHP. Its purpose is to simplify static code analysis and
|
||||
manipulation.
|
||||
|
||||
[**Documentation for version 4.x**][doc_4_x] (stable; for running on PHP >= 7.0; for parsing PHP 5.2 to PHP 8.2).
|
||||
|
||||
[Documentation for version 3.x][doc_3_x] (unsupported; for running on PHP >= 5.5; for parsing PHP 5.2 to PHP 7.2).
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
The main features provided by this library are:
|
||||
|
||||
* Parsing PHP 5, PHP 7, and PHP 8 code into an abstract syntax tree (AST).
|
||||
* Invalid code can be parsed into a partial AST.
|
||||
* The AST contains accurate location information.
|
||||
* Dumping the AST in human-readable form.
|
||||
* Converting an AST back to PHP code.
|
||||
* Experimental: Formatting can be preserved for partially changed ASTs.
|
||||
* Infrastructure to traverse and modify ASTs.
|
||||
* Resolution of namespaced names.
|
||||
* Evaluation of constant expressions.
|
||||
* Builders to simplify AST construction for code generation.
|
||||
* Converting an AST into JSON and back.
|
||||
|
||||
Quick Start
|
||||
-----------
|
||||
|
||||
Install the library using [composer](https://getcomposer.org):
|
||||
|
||||
php composer.phar require nikic/php-parser
|
||||
|
||||
Parse some PHP code into an AST and dump the result in human-readable form:
|
||||
|
||||
```php
|
||||
<?php
|
||||
use PhpParser\Error;
|
||||
use PhpParser\NodeDumper;
|
||||
use PhpParser\ParserFactory;
|
||||
|
||||
$code = <<<'CODE'
|
||||
<?php
|
||||
|
||||
function test($foo)
|
||||
{
|
||||
var_dump($foo);
|
||||
}
|
||||
CODE;
|
||||
|
||||
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
|
||||
try {
|
||||
$ast = $parser->parse($code);
|
||||
} catch (Error $error) {
|
||||
echo "Parse error: {$error->getMessage()}\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$dumper = new NodeDumper;
|
||||
echo $dumper->dump($ast) . "\n";
|
||||
```
|
||||
|
||||
This dumps an AST looking something like this:
|
||||
|
||||
```
|
||||
array(
|
||||
0: Stmt_Function(
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: test
|
||||
)
|
||||
params: array(
|
||||
0: Param(
|
||||
type: null
|
||||
byRef: false
|
||||
variadic: false
|
||||
var: Expr_Variable(
|
||||
name: foo
|
||||
)
|
||||
default: null
|
||||
)
|
||||
)
|
||||
returnType: null
|
||||
stmts: array(
|
||||
0: Stmt_Expression(
|
||||
expr: Expr_FuncCall(
|
||||
name: Name(
|
||||
parts: array(
|
||||
0: var_dump
|
||||
)
|
||||
)
|
||||
args: array(
|
||||
0: Arg(
|
||||
value: Expr_Variable(
|
||||
name: foo
|
||||
)
|
||||
byRef: false
|
||||
unpack: false
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
Let's traverse the AST and perform some kind of modification. For example, drop all function bodies:
|
||||
|
||||
```php
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\NodeTraverser;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
|
||||
$traverser = new NodeTraverser();
|
||||
$traverser->addVisitor(new class extends NodeVisitorAbstract {
|
||||
public function enterNode(Node $node) {
|
||||
if ($node instanceof Function_) {
|
||||
// Clean out the function body
|
||||
$node->stmts = [];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$ast = $traverser->traverse($ast);
|
||||
echo $dumper->dump($ast) . "\n";
|
||||
```
|
||||
|
||||
This gives us an AST where the `Function_::$stmts` are empty:
|
||||
|
||||
```
|
||||
array(
|
||||
0: Stmt_Function(
|
||||
byRef: false
|
||||
name: Identifier(
|
||||
name: test
|
||||
)
|
||||
params: array(
|
||||
0: Param(
|
||||
type: null
|
||||
byRef: false
|
||||
variadic: false
|
||||
var: Expr_Variable(
|
||||
name: foo
|
||||
)
|
||||
default: null
|
||||
)
|
||||
)
|
||||
returnType: null
|
||||
stmts: array(
|
||||
)
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
Finally, we can convert the new AST back to PHP code:
|
||||
|
||||
```php
|
||||
use PhpParser\PrettyPrinter;
|
||||
|
||||
$prettyPrinter = new PrettyPrinter\Standard;
|
||||
echo $prettyPrinter->prettyPrintFile($ast);
|
||||
```
|
||||
|
||||
This gives us our original code, minus the `var_dump()` call inside the function:
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
function test($foo)
|
||||
{
|
||||
}
|
||||
```
|
||||
|
||||
For a more comprehensive introduction, see the documentation.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
1. [Introduction](doc/0_Introduction.markdown)
|
||||
2. [Usage of basic components](doc/2_Usage_of_basic_components.markdown)
|
||||
|
||||
Component documentation:
|
||||
|
||||
* [Walking the AST](doc/component/Walking_the_AST.markdown)
|
||||
* Node visitors
|
||||
* Modifying the AST from a visitor
|
||||
* Short-circuiting traversals
|
||||
* Interleaved visitors
|
||||
* Simple node finding API
|
||||
* Parent and sibling references
|
||||
* [Name resolution](doc/component/Name_resolution.markdown)
|
||||
* Name resolver options
|
||||
* Name resolution context
|
||||
* [Pretty printing](doc/component/Pretty_printing.markdown)
|
||||
* Converting AST back to PHP code
|
||||
* Customizing formatting
|
||||
* Formatting-preserving code transformations
|
||||
* [AST builders](doc/component/AST_builders.markdown)
|
||||
* Fluent builders for AST nodes
|
||||
* [Lexer](doc/component/Lexer.markdown)
|
||||
* Lexer options
|
||||
* Token and file positions for nodes
|
||||
* Custom attributes
|
||||
* [Error handling](doc/component/Error_handling.markdown)
|
||||
* Column information for errors
|
||||
* Error recovery (parsing of syntactically incorrect code)
|
||||
* [Constant expression evaluation](doc/component/Constant_expression_evaluation.markdown)
|
||||
* Evaluating constant/property/etc initializers
|
||||
* Handling errors and unsupported expressions
|
||||
* [JSON representation](doc/component/JSON_representation.markdown)
|
||||
* JSON encoding and decoding of ASTs
|
||||
* [Performance](doc/component/Performance.markdown)
|
||||
* Disabling Xdebug
|
||||
* Reusing objects
|
||||
* Garbage collection impact
|
||||
* [Frequently asked questions](doc/component/FAQ.markdown)
|
||||
* Parent and sibling references
|
||||
|
||||
[doc_3_x]: https://github.com/nikic/PHP-Parser/tree/3.x/doc
|
||||
[doc_4_x]: https://github.com/nikic/PHP-Parser/tree/4.x/doc
|
205
vendor/nikic/php-parser/bin/php-parse
vendored
Normal file
205
vendor/nikic/php-parser/bin/php-parse
vendored
Normal file
|
@ -0,0 +1,205 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
foreach ([__DIR__ . '/../../../autoload.php', __DIR__ . '/../vendor/autoload.php'] as $file) {
|
||||
if (file_exists($file)) {
|
||||
require $file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ini_set('xdebug.max_nesting_level', 3000);
|
||||
|
||||
// Disable Xdebug var_dump() output truncation
|
||||
ini_set('xdebug.var_display_max_children', -1);
|
||||
ini_set('xdebug.var_display_max_data', -1);
|
||||
ini_set('xdebug.var_display_max_depth', -1);
|
||||
|
||||
list($operations, $files, $attributes) = parseArgs($argv);
|
||||
|
||||
/* Dump nodes by default */
|
||||
if (empty($operations)) {
|
||||
$operations[] = 'dump';
|
||||
}
|
||||
|
||||
if (empty($files)) {
|
||||
showHelp("Must specify at least one file.");
|
||||
}
|
||||
|
||||
$lexer = new PhpParser\Lexer\Emulative(['usedAttributes' => [
|
||||
'startLine', 'endLine', 'startFilePos', 'endFilePos', 'comments'
|
||||
]]);
|
||||
$parser = (new PhpParser\ParserFactory)->create(
|
||||
PhpParser\ParserFactory::PREFER_PHP7,
|
||||
$lexer
|
||||
);
|
||||
$dumper = new PhpParser\NodeDumper([
|
||||
'dumpComments' => true,
|
||||
'dumpPositions' => $attributes['with-positions'],
|
||||
]);
|
||||
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
|
||||
|
||||
$traverser = new PhpParser\NodeTraverser();
|
||||
$traverser->addVisitor(new PhpParser\NodeVisitor\NameResolver);
|
||||
|
||||
foreach ($files as $file) {
|
||||
if (strpos($file, '<?php') === 0) {
|
||||
$code = $file;
|
||||
fwrite(STDERR, "====> Code $code\n");
|
||||
} else {
|
||||
if (!file_exists($file)) {
|
||||
fwrite(STDERR, "File $file does not exist.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$code = file_get_contents($file);
|
||||
fwrite(STDERR, "====> File $file:\n");
|
||||
}
|
||||
|
||||
if ($attributes['with-recovery']) {
|
||||
$errorHandler = new PhpParser\ErrorHandler\Collecting;
|
||||
$stmts = $parser->parse($code, $errorHandler);
|
||||
foreach ($errorHandler->getErrors() as $error) {
|
||||
$message = formatErrorMessage($error, $code, $attributes['with-column-info']);
|
||||
fwrite(STDERR, $message . "\n");
|
||||
}
|
||||
if (null === $stmts) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$stmts = $parser->parse($code);
|
||||
} catch (PhpParser\Error $error) {
|
||||
$message = formatErrorMessage($error, $code, $attributes['with-column-info']);
|
||||
fwrite(STDERR, $message . "\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($operations as $operation) {
|
||||
if ('dump' === $operation) {
|
||||
fwrite(STDERR, "==> Node dump:\n");
|
||||
echo $dumper->dump($stmts, $code), "\n";
|
||||
} elseif ('pretty-print' === $operation) {
|
||||
fwrite(STDERR, "==> Pretty print:\n");
|
||||
echo $prettyPrinter->prettyPrintFile($stmts), "\n";
|
||||
} elseif ('json-dump' === $operation) {
|
||||
fwrite(STDERR, "==> JSON dump:\n");
|
||||
echo json_encode($stmts, JSON_PRETTY_PRINT), "\n";
|
||||
} elseif ('var-dump' === $operation) {
|
||||
fwrite(STDERR, "==> var_dump():\n");
|
||||
var_dump($stmts);
|
||||
} elseif ('resolve-names' === $operation) {
|
||||
fwrite(STDERR, "==> Resolved names.\n");
|
||||
$stmts = $traverser->traverse($stmts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function formatErrorMessage(PhpParser\Error $e, $code, $withColumnInfo) {
|
||||
if ($withColumnInfo && $e->hasColumnInfo()) {
|
||||
return $e->getMessageWithColumnInfo($code);
|
||||
} else {
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
function showHelp($error = '') {
|
||||
if ($error) {
|
||||
fwrite(STDERR, $error . "\n\n");
|
||||
}
|
||||
fwrite($error ? STDERR : STDOUT, <<<OUTPUT
|
||||
Usage: php-parse [operations] file1.php [file2.php ...]
|
||||
or: php-parse [operations] "<?php code"
|
||||
Turn PHP source code into an abstract syntax tree.
|
||||
|
||||
Operations is a list of the following options (--dump by default):
|
||||
|
||||
-d, --dump Dump nodes using NodeDumper
|
||||
-p, --pretty-print Pretty print file using PrettyPrinter\Standard
|
||||
-j, --json-dump Print json_encode() result
|
||||
--var-dump var_dump() nodes (for exact structure)
|
||||
-N, --resolve-names Resolve names using NodeVisitor\NameResolver
|
||||
-c, --with-column-info Show column-numbers for errors (if available)
|
||||
-P, --with-positions Show positions in node dumps
|
||||
-r, --with-recovery Use parsing with error recovery
|
||||
-h, --help Display this page
|
||||
|
||||
Example:
|
||||
php-parse -d -p -N -d file.php
|
||||
|
||||
Dumps nodes, pretty prints them, then resolves names and dumps them again.
|
||||
|
||||
|
||||
OUTPUT
|
||||
);
|
||||
exit($error ? 1 : 0);
|
||||
}
|
||||
|
||||
function parseArgs($args) {
|
||||
$operations = [];
|
||||
$files = [];
|
||||
$attributes = [
|
||||
'with-column-info' => false,
|
||||
'with-positions' => false,
|
||||
'with-recovery' => false,
|
||||
];
|
||||
|
||||
array_shift($args);
|
||||
$parseOptions = true;
|
||||
foreach ($args as $arg) {
|
||||
if (!$parseOptions) {
|
||||
$files[] = $arg;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch ($arg) {
|
||||
case '--dump':
|
||||
case '-d':
|
||||
$operations[] = 'dump';
|
||||
break;
|
||||
case '--pretty-print':
|
||||
case '-p':
|
||||
$operations[] = 'pretty-print';
|
||||
break;
|
||||
case '--json-dump':
|
||||
case '-j':
|
||||
$operations[] = 'json-dump';
|
||||
break;
|
||||
case '--var-dump':
|
||||
$operations[] = 'var-dump';
|
||||
break;
|
||||
case '--resolve-names':
|
||||
case '-N';
|
||||
$operations[] = 'resolve-names';
|
||||
break;
|
||||
case '--with-column-info':
|
||||
case '-c';
|
||||
$attributes['with-column-info'] = true;
|
||||
break;
|
||||
case '--with-positions':
|
||||
case '-P':
|
||||
$attributes['with-positions'] = true;
|
||||
break;
|
||||
case '--with-recovery':
|
||||
case '-r':
|
||||
$attributes['with-recovery'] = true;
|
||||
break;
|
||||
case '--help':
|
||||
case '-h';
|
||||
showHelp();
|
||||
break;
|
||||
case '--':
|
||||
$parseOptions = false;
|
||||
break;
|
||||
default:
|
||||
if ($arg[0] === '-') {
|
||||
showHelp("Invalid operation $arg.");
|
||||
} else {
|
||||
$files[] = $arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [$operations, $files, $attributes];
|
||||
}
|
41
vendor/nikic/php-parser/composer.json
vendored
Normal file
41
vendor/nikic/php-parser/composer.json
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"name": "nikic/php-parser",
|
||||
"type": "library",
|
||||
"description": "A PHP parser written in PHP",
|
||||
"keywords": [
|
||||
"php",
|
||||
"parser"
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nikita Popov"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.0",
|
||||
"ext-tokenizer": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0",
|
||||
"ircmaxell/php-yacc": "^0.0.7"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.9-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PhpParser\\": "lib/PhpParser"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"PhpParser\\": "test/PhpParser/"
|
||||
}
|
||||
},
|
||||
"bin": [
|
||||
"bin/php-parse"
|
||||
]
|
||||
}
|
30
vendor/nikic/php-parser/grammar/README.md
vendored
Normal file
30
vendor/nikic/php-parser/grammar/README.md
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
What do all those files mean?
|
||||
=============================
|
||||
|
||||
* `php5.y`: PHP 5 grammar written in a pseudo language
|
||||
* `php7.y`: PHP 7 grammar written in a pseudo language
|
||||
* `tokens.y`: Tokens definition shared between PHP 5 and PHP 7 grammars
|
||||
* `parser.template`: A `kmyacc` parser prototype file for PHP
|
||||
* `tokens.template`: A `kmyacc` prototype file for the `Tokens` class
|
||||
* `rebuildParsers.php`: Preprocesses the grammar and builds the parser using `kmyacc`
|
||||
|
||||
.phpy pseudo language
|
||||
=====================
|
||||
|
||||
The `.y` file is a normal grammar in `kmyacc` (`yacc`) style, with some transformations
|
||||
applied to it:
|
||||
|
||||
* Nodes are created using the syntax `Name[..., ...]`. This is transformed into
|
||||
`new Name(..., ..., attributes())`
|
||||
* Some function-like constructs are resolved (see `rebuildParsers.php` for a list)
|
||||
|
||||
Building the parser
|
||||
===================
|
||||
|
||||
Run `php grammar/rebuildParsers.php` to rebuild the parsers. Additional options:
|
||||
|
||||
* The `KMYACC` environment variable can be used to specify an alternative `kmyacc` binary.
|
||||
By default the `phpyacc` dev dependency will be used. To use the original `kmyacc`, you
|
||||
need to compile [moriyoshi's fork](https://github.com/moriyoshi/kmyacc-forked).
|
||||
* The `--debug` option enables emission of debug symbols and creates the `y.output` file.
|
||||
* The `--keep-tmp-grammar` option preserves the preprocessed grammar file.
|
106
vendor/nikic/php-parser/grammar/parser.template
vendored
Normal file
106
vendor/nikic/php-parser/grammar/parser.template
vendored
Normal file
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
$meta #
|
||||
#semval($) $this->semValue
|
||||
#semval($,%t) $this->semValue
|
||||
#semval(%n) $stackPos-(%l-%n)
|
||||
#semval(%n,%t) $stackPos-(%l-%n)
|
||||
|
||||
namespace PhpParser\Parser;
|
||||
|
||||
use PhpParser\Error;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Scalar;
|
||||
use PhpParser\Node\Stmt;
|
||||
#include;
|
||||
|
||||
/* This is an automatically GENERATED file, which should not be manually edited.
|
||||
* Instead edit one of the following:
|
||||
* * the grammar files grammar/php5.y or grammar/php7.y
|
||||
* * the skeleton file grammar/parser.template
|
||||
* * the preprocessing script grammar/rebuildParsers.php
|
||||
*/
|
||||
class #(-p) extends \PhpParser\ParserAbstract
|
||||
{
|
||||
protected $tokenToSymbolMapSize = #(YYMAXLEX);
|
||||
protected $actionTableSize = #(YYLAST);
|
||||
protected $gotoTableSize = #(YYGLAST);
|
||||
|
||||
protected $invalidSymbol = #(YYBADCH);
|
||||
protected $errorSymbol = #(YYINTERRTOK);
|
||||
protected $defaultAction = #(YYDEFAULT);
|
||||
protected $unexpectedTokenRule = #(YYUNEXPECTED);
|
||||
|
||||
protected $YY2TBLSTATE = #(YY2TBLSTATE);
|
||||
protected $numNonLeafStates = #(YYNLSTATES);
|
||||
|
||||
protected $symbolToName = array(
|
||||
#listvar terminals
|
||||
);
|
||||
|
||||
protected $tokenToSymbol = array(
|
||||
#listvar yytranslate
|
||||
);
|
||||
|
||||
protected $action = array(
|
||||
#listvar yyaction
|
||||
);
|
||||
|
||||
protected $actionCheck = array(
|
||||
#listvar yycheck
|
||||
);
|
||||
|
||||
protected $actionBase = array(
|
||||
#listvar yybase
|
||||
);
|
||||
|
||||
protected $actionDefault = array(
|
||||
#listvar yydefault
|
||||
);
|
||||
|
||||
protected $goto = array(
|
||||
#listvar yygoto
|
||||
);
|
||||
|
||||
protected $gotoCheck = array(
|
||||
#listvar yygcheck
|
||||
);
|
||||
|
||||
protected $gotoBase = array(
|
||||
#listvar yygbase
|
||||
);
|
||||
|
||||
protected $gotoDefault = array(
|
||||
#listvar yygdefault
|
||||
);
|
||||
|
||||
protected $ruleToNonTerminal = array(
|
||||
#listvar yylhs
|
||||
);
|
||||
|
||||
protected $ruleToLength = array(
|
||||
#listvar yylen
|
||||
);
|
||||
#if -t
|
||||
|
||||
protected $productions = array(
|
||||
#production-strings;
|
||||
);
|
||||
#endif
|
||||
|
||||
protected function initReduceCallbacks() {
|
||||
$this->reduceCallbacks = [
|
||||
#reduce
|
||||
%n => function ($stackPos) {
|
||||
%b
|
||||
},
|
||||
#noact
|
||||
%n => function ($stackPos) {
|
||||
$this->semValue = $this->semStack[$stackPos];
|
||||
},
|
||||
#endreduce
|
||||
];
|
||||
}
|
||||
}
|
||||
#tailcode;
|
1046
vendor/nikic/php-parser/grammar/php5.y
vendored
Normal file
1046
vendor/nikic/php-parser/grammar/php5.y
vendored
Normal file
File diff suppressed because it is too large
Load diff
1245
vendor/nikic/php-parser/grammar/php7.y
vendored
Normal file
1245
vendor/nikic/php-parser/grammar/php7.y
vendored
Normal file
File diff suppressed because it is too large
Load diff
184
vendor/nikic/php-parser/grammar/phpyLang.php
vendored
Normal file
184
vendor/nikic/php-parser/grammar/phpyLang.php
vendored
Normal file
|
@ -0,0 +1,184 @@
|
|||
<?php
|
||||
|
||||
///////////////////////////////
|
||||
/// Utility regex constants ///
|
||||
///////////////////////////////
|
||||
|
||||
const LIB = '(?(DEFINE)
|
||||
(?<singleQuotedString>\'[^\\\\\']*+(?:\\\\.[^\\\\\']*+)*+\')
|
||||
(?<doubleQuotedString>"[^\\\\"]*+(?:\\\\.[^\\\\"]*+)*+")
|
||||
(?<string>(?&singleQuotedString)|(?&doubleQuotedString))
|
||||
(?<comment>/\*[^*]*+(?:\*(?!/)[^*]*+)*+\*/)
|
||||
(?<code>\{[^\'"/{}]*+(?:(?:(?&string)|(?&comment)|(?&code)|/)[^\'"/{}]*+)*+})
|
||||
)';
|
||||
|
||||
const PARAMS = '\[(?<params>[^[\]]*+(?:\[(?¶ms)\][^[\]]*+)*+)\]';
|
||||
const ARGS = '\((?<args>[^()]*+(?:\((?&args)\)[^()]*+)*+)\)';
|
||||
|
||||
///////////////////////////////
|
||||
/// Preprocessing functions ///
|
||||
///////////////////////////////
|
||||
|
||||
function preprocessGrammar($code) {
|
||||
$code = resolveNodes($code);
|
||||
$code = resolveMacros($code);
|
||||
$code = resolveStackAccess($code);
|
||||
|
||||
return $code;
|
||||
}
|
||||
|
||||
function resolveNodes($code) {
|
||||
return preg_replace_callback(
|
||||
'~\b(?<name>[A-Z][a-zA-Z_\\\\]++)\s*' . PARAMS . '~',
|
||||
function($matches) {
|
||||
// recurse
|
||||
$matches['params'] = resolveNodes($matches['params']);
|
||||
|
||||
$params = magicSplit(
|
||||
'(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
|
||||
$matches['params']
|
||||
);
|
||||
|
||||
$paramCode = '';
|
||||
foreach ($params as $param) {
|
||||
$paramCode .= $param . ', ';
|
||||
}
|
||||
|
||||
return 'new ' . $matches['name'] . '(' . $paramCode . 'attributes())';
|
||||
},
|
||||
$code
|
||||
);
|
||||
}
|
||||
|
||||
function resolveMacros($code) {
|
||||
return preg_replace_callback(
|
||||
'~\b(?<!::|->)(?!array\()(?<name>[a-z][A-Za-z]++)' . ARGS . '~',
|
||||
function($matches) {
|
||||
// recurse
|
||||
$matches['args'] = resolveMacros($matches['args']);
|
||||
|
||||
$name = $matches['name'];
|
||||
$args = magicSplit(
|
||||
'(?:' . PARAMS . '|' . ARGS . ')(*SKIP)(*FAIL)|,',
|
||||
$matches['args']
|
||||
);
|
||||
|
||||
if ('attributes' === $name) {
|
||||
assertArgs(0, $args, $name);
|
||||
return '$this->startAttributeStack[#1] + $this->endAttributes';
|
||||
}
|
||||
|
||||
if ('stackAttributes' === $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
return '$this->startAttributeStack[' . $args[0] . ']'
|
||||
. ' + $this->endAttributeStack[' . $args[0] . ']';
|
||||
}
|
||||
|
||||
if ('init' === $name) {
|
||||
return '$$ = array(' . implode(', ', $args) . ')';
|
||||
}
|
||||
|
||||
if ('push' === $name) {
|
||||
assertArgs(2, $args, $name);
|
||||
|
||||
return $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0];
|
||||
}
|
||||
|
||||
if ('pushNormalizing' === $name) {
|
||||
assertArgs(2, $args, $name);
|
||||
|
||||
return 'if (is_array(' . $args[1] . ')) { $$ = array_merge(' . $args[0] . ', ' . $args[1] . '); }'
|
||||
. ' else { ' . $args[0] . '[] = ' . $args[1] . '; $$ = ' . $args[0] . '; }';
|
||||
}
|
||||
|
||||
if ('toArray' == $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
|
||||
return 'is_array(' . $args[0] . ') ? ' . $args[0] . ' : array(' . $args[0] . ')';
|
||||
}
|
||||
|
||||
if ('parseVar' === $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
|
||||
return 'substr(' . $args[0] . ', 1)';
|
||||
}
|
||||
|
||||
if ('parseEncapsed' === $name) {
|
||||
assertArgs(3, $args, $name);
|
||||
|
||||
return 'foreach (' . $args[0] . ' as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) {'
|
||||
. ' $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, ' . $args[1] . ', ' . $args[2] . '); } }';
|
||||
}
|
||||
|
||||
if ('makeNop' === $name) {
|
||||
assertArgs(3, $args, $name);
|
||||
|
||||
return '$startAttributes = ' . $args[1] . ';'
|
||||
. ' if (isset($startAttributes[\'comments\']))'
|
||||
. ' { ' . $args[0] . ' = new Stmt\Nop($startAttributes + ' . $args[2] . '); }'
|
||||
. ' else { ' . $args[0] . ' = null; }';
|
||||
}
|
||||
|
||||
if ('makeZeroLengthNop' == $name) {
|
||||
assertArgs(2, $args, $name);
|
||||
|
||||
return '$startAttributes = ' . $args[1] . ';'
|
||||
. ' if (isset($startAttributes[\'comments\']))'
|
||||
. ' { ' . $args[0] . ' = new Stmt\Nop($this->createCommentNopAttributes($startAttributes[\'comments\'])); }'
|
||||
. ' else { ' . $args[0] . ' = null; }';
|
||||
}
|
||||
|
||||
if ('prependLeadingComments' === $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
|
||||
return '$attrs = $this->startAttributeStack[#1]; $stmts = ' . $args[0] . '; '
|
||||
. 'if (!empty($attrs[\'comments\'])) {'
|
||||
. '$stmts[0]->setAttribute(\'comments\', '
|
||||
. 'array_merge($attrs[\'comments\'], $stmts[0]->getAttribute(\'comments\', []))); }';
|
||||
}
|
||||
|
||||
return $matches[0];
|
||||
},
|
||||
$code
|
||||
);
|
||||
}
|
||||
|
||||
function assertArgs($num, $args, $name) {
|
||||
if ($num != count($args)) {
|
||||
die('Wrong argument count for ' . $name . '().');
|
||||
}
|
||||
}
|
||||
|
||||
function resolveStackAccess($code) {
|
||||
$code = preg_replace('/\$\d+/', '$this->semStack[$0]', $code);
|
||||
$code = preg_replace('/#(\d+)/', '$$1', $code);
|
||||
return $code;
|
||||
}
|
||||
|
||||
function removeTrailingWhitespace($code) {
|
||||
$lines = explode("\n", $code);
|
||||
$lines = array_map('rtrim', $lines);
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
/// Regex helper functions ///
|
||||
//////////////////////////////
|
||||
|
||||
function regex($regex) {
|
||||
return '~' . LIB . '(?:' . str_replace('~', '\~', $regex) . ')~';
|
||||
}
|
||||
|
||||
function magicSplit($regex, $string) {
|
||||
$pieces = preg_split(regex('(?:(?&string)|(?&comment)|(?&code))(*SKIP)(*FAIL)|' . $regex), $string);
|
||||
|
||||
foreach ($pieces as &$piece) {
|
||||
$piece = trim($piece);
|
||||
}
|
||||
|
||||
if ($pieces === ['']) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return $pieces;
|
||||
}
|
81
vendor/nikic/php-parser/grammar/rebuildParsers.php
vendored
Normal file
81
vendor/nikic/php-parser/grammar/rebuildParsers.php
vendored
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
require __DIR__ . '/phpyLang.php';
|
||||
|
||||
$grammarFileToName = [
|
||||
__DIR__ . '/php5.y' => 'Php5',
|
||||
__DIR__ . '/php7.y' => 'Php7',
|
||||
];
|
||||
|
||||
$tokensFile = __DIR__ . '/tokens.y';
|
||||
$tokensTemplate = __DIR__ . '/tokens.template';
|
||||
$skeletonFile = __DIR__ . '/parser.template';
|
||||
$tmpGrammarFile = __DIR__ . '/tmp_parser.phpy';
|
||||
$tmpResultFile = __DIR__ . '/tmp_parser.php';
|
||||
$resultDir = __DIR__ . '/../lib/PhpParser/Parser';
|
||||
$tokensResultsFile = $resultDir . '/Tokens.php';
|
||||
|
||||
$kmyacc = getenv('KMYACC');
|
||||
if (!$kmyacc) {
|
||||
// Use phpyacc from dev dependencies by default.
|
||||
$kmyacc = __DIR__ . '/../vendor/bin/phpyacc';
|
||||
}
|
||||
|
||||
$options = array_flip($argv);
|
||||
$optionDebug = isset($options['--debug']);
|
||||
$optionKeepTmpGrammar = isset($options['--keep-tmp-grammar']);
|
||||
|
||||
///////////////////
|
||||
/// Main script ///
|
||||
///////////////////
|
||||
|
||||
$tokens = file_get_contents($tokensFile);
|
||||
|
||||
foreach ($grammarFileToName as $grammarFile => $name) {
|
||||
echo "Building temporary $name grammar file.\n";
|
||||
|
||||
$grammarCode = file_get_contents($grammarFile);
|
||||
$grammarCode = str_replace('%tokens', $tokens, $grammarCode);
|
||||
$grammarCode = preprocessGrammar($grammarCode);
|
||||
|
||||
file_put_contents($tmpGrammarFile, $grammarCode);
|
||||
|
||||
$additionalArgs = $optionDebug ? '-t -v' : '';
|
||||
|
||||
echo "Building $name parser.\n";
|
||||
$output = execCmd("$kmyacc $additionalArgs -m $skeletonFile -p $name $tmpGrammarFile");
|
||||
|
||||
$resultCode = file_get_contents($tmpResultFile);
|
||||
$resultCode = removeTrailingWhitespace($resultCode);
|
||||
|
||||
ensureDirExists($resultDir);
|
||||
file_put_contents("$resultDir/$name.php", $resultCode);
|
||||
unlink($tmpResultFile);
|
||||
|
||||
echo "Building token definition.\n";
|
||||
$output = execCmd("$kmyacc -m $tokensTemplate $tmpGrammarFile");
|
||||
rename($tmpResultFile, $tokensResultsFile);
|
||||
|
||||
if (!$optionKeepTmpGrammar) {
|
||||
unlink($tmpGrammarFile);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
/// Utility helper functions ///
|
||||
////////////////////////////////
|
||||
|
||||
function ensureDirExists($dir) {
|
||||
if (!is_dir($dir)) {
|
||||
mkdir($dir, 0777, true);
|
||||
}
|
||||
}
|
||||
|
||||
function execCmd($cmd) {
|
||||
$output = trim(shell_exec("$cmd 2>&1"));
|
||||
if ($output !== "") {
|
||||
echo "> " . $cmd . "\n";
|
||||
echo $output;
|
||||
}
|
||||
return $output;
|
||||
}
|
17
vendor/nikic/php-parser/grammar/tokens.template
vendored
Normal file
17
vendor/nikic/php-parser/grammar/tokens.template
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
$meta #
|
||||
#semval($) $this->semValue
|
||||
#semval($,%t) $this->semValue
|
||||
#semval(%n) $this->stackPos-(%l-%n)
|
||||
#semval(%n,%t) $this->stackPos-(%l-%n)
|
||||
|
||||
namespace PhpParser\Parser;
|
||||
#include;
|
||||
|
||||
/* GENERATED file based on grammar/tokens.y */
|
||||
final class Tokens
|
||||
{
|
||||
#tokenval
|
||||
const %s = %n;
|
||||
#endtokenval
|
||||
}
|
115
vendor/nikic/php-parser/grammar/tokens.y
vendored
Normal file
115
vendor/nikic/php-parser/grammar/tokens.y
vendored
Normal file
|
@ -0,0 +1,115 @@
|
|||
/* We currently rely on the token ID mapping to be the same between PHP 5 and PHP 7 - so the same lexer can be used for
|
||||
* both. This is enforced by sharing this token file. */
|
||||
|
||||
%right T_THROW
|
||||
%left T_INCLUDE T_INCLUDE_ONCE T_EVAL T_REQUIRE T_REQUIRE_ONCE
|
||||
%left ','
|
||||
%left T_LOGICAL_OR
|
||||
%left T_LOGICAL_XOR
|
||||
%left T_LOGICAL_AND
|
||||
%right T_PRINT
|
||||
%right T_YIELD
|
||||
%right T_DOUBLE_ARROW
|
||||
%right T_YIELD_FROM
|
||||
%left '=' T_PLUS_EQUAL T_MINUS_EQUAL T_MUL_EQUAL T_DIV_EQUAL T_CONCAT_EQUAL T_MOD_EQUAL T_AND_EQUAL T_OR_EQUAL T_XOR_EQUAL T_SL_EQUAL T_SR_EQUAL T_POW_EQUAL T_COALESCE_EQUAL
|
||||
%left '?' ':'
|
||||
%right T_COALESCE
|
||||
%left T_BOOLEAN_OR
|
||||
%left T_BOOLEAN_AND
|
||||
%left '|'
|
||||
%left '^'
|
||||
%left T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG
|
||||
%nonassoc T_IS_EQUAL T_IS_NOT_EQUAL T_IS_IDENTICAL T_IS_NOT_IDENTICAL T_SPACESHIP
|
||||
%nonassoc '<' T_IS_SMALLER_OR_EQUAL '>' T_IS_GREATER_OR_EQUAL
|
||||
%left T_SL T_SR
|
||||
%left '+' '-' '.'
|
||||
%left '*' '/' '%'
|
||||
%right '!'
|
||||
%nonassoc T_INSTANCEOF
|
||||
%right '~' T_INC T_DEC T_INT_CAST T_DOUBLE_CAST T_STRING_CAST T_ARRAY_CAST T_OBJECT_CAST T_BOOL_CAST T_UNSET_CAST '@'
|
||||
%right T_POW
|
||||
%right '['
|
||||
%nonassoc T_NEW T_CLONE
|
||||
%token T_EXIT
|
||||
%token T_IF
|
||||
%left T_ELSEIF
|
||||
%left T_ELSE
|
||||
%left T_ENDIF
|
||||
%token T_LNUMBER
|
||||
%token T_DNUMBER
|
||||
%token T_STRING
|
||||
%token T_STRING_VARNAME
|
||||
%token T_VARIABLE
|
||||
%token T_NUM_STRING
|
||||
%token T_INLINE_HTML
|
||||
%token T_ENCAPSED_AND_WHITESPACE
|
||||
%token T_CONSTANT_ENCAPSED_STRING
|
||||
%token T_ECHO
|
||||
%token T_DO
|
||||
%token T_WHILE
|
||||
%token T_ENDWHILE
|
||||
%token T_FOR
|
||||
%token T_ENDFOR
|
||||
%token T_FOREACH
|
||||
%token T_ENDFOREACH
|
||||
%token T_DECLARE
|
||||
%token T_ENDDECLARE
|
||||
%token T_AS
|
||||
%token T_SWITCH
|
||||
%token T_MATCH
|
||||
%token T_ENDSWITCH
|
||||
%token T_CASE
|
||||
%token T_DEFAULT
|
||||
%token T_BREAK
|
||||
%token T_CONTINUE
|
||||
%token T_GOTO
|
||||
%token T_FUNCTION
|
||||
%token T_FN
|
||||
%token T_CONST
|
||||
%token T_RETURN
|
||||
%token T_TRY
|
||||
%token T_CATCH
|
||||
%token T_FINALLY
|
||||
%token T_THROW
|
||||
%token T_USE
|
||||
%token T_INSTEADOF
|
||||
%token T_GLOBAL
|
||||
%right T_STATIC T_ABSTRACT T_FINAL T_PRIVATE T_PROTECTED T_PUBLIC T_READONLY
|
||||
%token T_VAR
|
||||
%token T_UNSET
|
||||
%token T_ISSET
|
||||
%token T_EMPTY
|
||||
%token T_HALT_COMPILER
|
||||
%token T_CLASS
|
||||
%token T_TRAIT
|
||||
%token T_INTERFACE
|
||||
%token T_ENUM
|
||||
%token T_EXTENDS
|
||||
%token T_IMPLEMENTS
|
||||
%token T_OBJECT_OPERATOR
|
||||
%token T_NULLSAFE_OBJECT_OPERATOR
|
||||
%token T_DOUBLE_ARROW
|
||||
%token T_LIST
|
||||
%token T_ARRAY
|
||||
%token T_CALLABLE
|
||||
%token T_CLASS_C
|
||||
%token T_TRAIT_C
|
||||
%token T_METHOD_C
|
||||
%token T_FUNC_C
|
||||
%token T_LINE
|
||||
%token T_FILE
|
||||
%token T_START_HEREDOC
|
||||
%token T_END_HEREDOC
|
||||
%token T_DOLLAR_OPEN_CURLY_BRACES
|
||||
%token T_CURLY_OPEN
|
||||
%token T_PAAMAYIM_NEKUDOTAYIM
|
||||
%token T_NAMESPACE
|
||||
%token T_NS_C
|
||||
%token T_DIR
|
||||
%token T_NS_SEPARATOR
|
||||
%token T_ELLIPSIS
|
||||
%token T_NAME_FULLY_QUALIFIED
|
||||
%token T_NAME_QUALIFIED
|
||||
%token T_NAME_RELATIVE
|
||||
%token T_ATTRIBUTE
|
||||
%token T_ENUM
|
13
vendor/nikic/php-parser/lib/PhpParser/Builder.php
vendored
Normal file
13
vendor/nikic/php-parser/lib/PhpParser/Builder.php
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser;
|
||||
|
||||
interface Builder
|
||||
{
|
||||
/**
|
||||
* Returns the built node.
|
||||
*
|
||||
* @return Node The built node
|
||||
*/
|
||||
public function getNode() : Node;
|
||||
}
|
148
vendor/nikic/php-parser/lib/PhpParser/Builder/ClassConst.php
vendored
Normal file
148
vendor/nikic/php-parser/lib/PhpParser/Builder/ClassConst.php
vendored
Normal file
|
@ -0,0 +1,148 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Const_;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class ClassConst implements PhpParser\Builder
|
||||
{
|
||||
protected $flags = 0;
|
||||
protected $attributes = [];
|
||||
protected $constants = [];
|
||||
|
||||
/** @var Node\AttributeGroup[] */
|
||||
protected $attributeGroups = [];
|
||||
/** @var Identifier|Node\Name|Node\ComplexType */
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* Creates a class constant builder
|
||||
*
|
||||
* @param string|Identifier $name Name
|
||||
* @param Node\Expr|bool|null|int|float|string|array $value Value
|
||||
*/
|
||||
public function __construct($name, $value) {
|
||||
$this->constants = [new Const_($name, BuilderHelpers::normalizeValue($value))];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add another constant to const group
|
||||
*
|
||||
* @param string|Identifier $name Name
|
||||
* @param Node\Expr|bool|null|int|float|string|array $value Value
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addConst($name, $value) {
|
||||
$this->constants[] = new Const_($name, BuilderHelpers::normalizeValue($value));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the constant public.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makePublic() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the constant protected.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeProtected() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the constant private.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makePrivate() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the constant final.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeFinal() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets doc comment for the constant.
|
||||
*
|
||||
* @param PhpParser\Comment\Doc|string $docComment Doc comment to set
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function setDocComment($docComment) {
|
||||
$this->attributes = [
|
||||
'comments' => [BuilderHelpers::normalizeDocComment($docComment)]
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute group.
|
||||
*
|
||||
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addAttribute($attribute) {
|
||||
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the constant type.
|
||||
*
|
||||
* @param string|Node\Name|Identifier|Node\ComplexType $type
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setType($type) {
|
||||
$this->type = BuilderHelpers::normalizeType($type);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built class node.
|
||||
*
|
||||
* @return Stmt\ClassConst The built constant node
|
||||
*/
|
||||
public function getNode(): PhpParser\Node {
|
||||
return new Stmt\ClassConst(
|
||||
$this->constants,
|
||||
$this->flags,
|
||||
$this->attributes,
|
||||
$this->attributeGroups,
|
||||
$this->type
|
||||
);
|
||||
}
|
||||
}
|
146
vendor/nikic/php-parser/lib/PhpParser/Builder/Class_.php
vendored
Normal file
146
vendor/nikic/php-parser/lib/PhpParser/Builder/Class_.php
vendored
Normal file
|
@ -0,0 +1,146 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class Class_ extends Declaration
|
||||
{
|
||||
protected $name;
|
||||
|
||||
protected $extends = null;
|
||||
protected $implements = [];
|
||||
protected $flags = 0;
|
||||
|
||||
protected $uses = [];
|
||||
protected $constants = [];
|
||||
protected $properties = [];
|
||||
protected $methods = [];
|
||||
|
||||
/** @var Node\AttributeGroup[] */
|
||||
protected $attributeGroups = [];
|
||||
|
||||
/**
|
||||
* Creates a class builder.
|
||||
*
|
||||
* @param string $name Name of the class
|
||||
*/
|
||||
public function __construct(string $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends a class.
|
||||
*
|
||||
* @param Name|string $class Name of class to extend
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function extend($class) {
|
||||
$this->extends = BuilderHelpers::normalizeName($class);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements one or more interfaces.
|
||||
*
|
||||
* @param Name|string ...$interfaces Names of interfaces to implement
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function implement(...$interfaces) {
|
||||
foreach ($interfaces as $interface) {
|
||||
$this->implements[] = BuilderHelpers::normalizeName($interface);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the class abstract.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeAbstract() {
|
||||
$this->flags = BuilderHelpers::addClassModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the class final.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeFinal() {
|
||||
$this->flags = BuilderHelpers::addClassModifier($this->flags, Stmt\Class_::MODIFIER_FINAL);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function makeReadonly() {
|
||||
$this->flags = BuilderHelpers::addClassModifier($this->flags, Stmt\Class_::MODIFIER_READONLY);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a statement.
|
||||
*
|
||||
* @param Stmt|PhpParser\Builder $stmt The statement to add
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addStmt($stmt) {
|
||||
$stmt = BuilderHelpers::normalizeNode($stmt);
|
||||
|
||||
$targets = [
|
||||
Stmt\TraitUse::class => &$this->uses,
|
||||
Stmt\ClassConst::class => &$this->constants,
|
||||
Stmt\Property::class => &$this->properties,
|
||||
Stmt\ClassMethod::class => &$this->methods,
|
||||
];
|
||||
|
||||
$class = \get_class($stmt);
|
||||
if (!isset($targets[$class])) {
|
||||
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
|
||||
}
|
||||
|
||||
$targets[$class][] = $stmt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute group.
|
||||
*
|
||||
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addAttribute($attribute) {
|
||||
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built class node.
|
||||
*
|
||||
* @return Stmt\Class_ The built class node
|
||||
*/
|
||||
public function getNode() : PhpParser\Node {
|
||||
return new Stmt\Class_($this->name, [
|
||||
'flags' => $this->flags,
|
||||
'extends' => $this->extends,
|
||||
'implements' => $this->implements,
|
||||
'stmts' => array_merge($this->uses, $this->constants, $this->properties, $this->methods),
|
||||
'attrGroups' => $this->attributeGroups,
|
||||
], $this->attributes);
|
||||
}
|
||||
}
|
43
vendor/nikic/php-parser/lib/PhpParser/Builder/Declaration.php
vendored
Normal file
43
vendor/nikic/php-parser/lib/PhpParser/Builder/Declaration.php
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
|
||||
abstract class Declaration implements PhpParser\Builder
|
||||
{
|
||||
protected $attributes = [];
|
||||
|
||||
abstract public function addStmt($stmt);
|
||||
|
||||
/**
|
||||
* Adds multiple statements.
|
||||
*
|
||||
* @param array $stmts The statements to add
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addStmts(array $stmts) {
|
||||
foreach ($stmts as $stmt) {
|
||||
$this->addStmt($stmt);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets doc comment for the declaration.
|
||||
*
|
||||
* @param PhpParser\Comment\Doc|string $docComment Doc comment to set
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function setDocComment($docComment) {
|
||||
$this->attributes['comments'] = [
|
||||
BuilderHelpers::normalizeDocComment($docComment)
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
85
vendor/nikic/php-parser/lib/PhpParser/Builder/EnumCase.php
vendored
Normal file
85
vendor/nikic/php-parser/lib/PhpParser/Builder/EnumCase.php
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class EnumCase implements PhpParser\Builder
|
||||
{
|
||||
protected $name;
|
||||
protected $value = null;
|
||||
protected $attributes = [];
|
||||
|
||||
/** @var Node\AttributeGroup[] */
|
||||
protected $attributeGroups = [];
|
||||
|
||||
/**
|
||||
* Creates an enum case builder.
|
||||
*
|
||||
* @param string|Identifier $name Name
|
||||
*/
|
||||
public function __construct($name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value.
|
||||
*
|
||||
* @param Node\Expr|string|int $value
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setValue($value) {
|
||||
$this->value = BuilderHelpers::normalizeValue($value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets doc comment for the constant.
|
||||
*
|
||||
* @param PhpParser\Comment\Doc|string $docComment Doc comment to set
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function setDocComment($docComment) {
|
||||
$this->attributes = [
|
||||
'comments' => [BuilderHelpers::normalizeDocComment($docComment)]
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute group.
|
||||
*
|
||||
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addAttribute($attribute) {
|
||||
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built enum case node.
|
||||
*
|
||||
* @return Stmt\EnumCase The built constant node
|
||||
*/
|
||||
public function getNode(): PhpParser\Node {
|
||||
return new Stmt\EnumCase(
|
||||
$this->name,
|
||||
$this->value,
|
||||
$this->attributeGroups,
|
||||
$this->attributes
|
||||
);
|
||||
}
|
||||
}
|
117
vendor/nikic/php-parser/lib/PhpParser/Builder/Enum_.php
vendored
Normal file
117
vendor/nikic/php-parser/lib/PhpParser/Builder/Enum_.php
vendored
Normal file
|
@ -0,0 +1,117 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class Enum_ extends Declaration
|
||||
{
|
||||
protected $name;
|
||||
protected $scalarType = null;
|
||||
|
||||
protected $implements = [];
|
||||
|
||||
protected $uses = [];
|
||||
protected $enumCases = [];
|
||||
protected $constants = [];
|
||||
protected $methods = [];
|
||||
|
||||
/** @var Node\AttributeGroup[] */
|
||||
protected $attributeGroups = [];
|
||||
|
||||
/**
|
||||
* Creates an enum builder.
|
||||
*
|
||||
* @param string $name Name of the enum
|
||||
*/
|
||||
public function __construct(string $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scalar type.
|
||||
*
|
||||
* @param string|Identifier $type
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setScalarType($scalarType) {
|
||||
$this->scalarType = BuilderHelpers::normalizeType($scalarType);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements one or more interfaces.
|
||||
*
|
||||
* @param Name|string ...$interfaces Names of interfaces to implement
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function implement(...$interfaces) {
|
||||
foreach ($interfaces as $interface) {
|
||||
$this->implements[] = BuilderHelpers::normalizeName($interface);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a statement.
|
||||
*
|
||||
* @param Stmt|PhpParser\Builder $stmt The statement to add
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addStmt($stmt) {
|
||||
$stmt = BuilderHelpers::normalizeNode($stmt);
|
||||
|
||||
$targets = [
|
||||
Stmt\TraitUse::class => &$this->uses,
|
||||
Stmt\EnumCase::class => &$this->enumCases,
|
||||
Stmt\ClassConst::class => &$this->constants,
|
||||
Stmt\ClassMethod::class => &$this->methods,
|
||||
];
|
||||
|
||||
$class = \get_class($stmt);
|
||||
if (!isset($targets[$class])) {
|
||||
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
|
||||
}
|
||||
|
||||
$targets[$class][] = $stmt;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute group.
|
||||
*
|
||||
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addAttribute($attribute) {
|
||||
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built class node.
|
||||
*
|
||||
* @return Stmt\Enum_ The built enum node
|
||||
*/
|
||||
public function getNode() : PhpParser\Node {
|
||||
return new Stmt\Enum_($this->name, [
|
||||
'scalarType' => $this->scalarType,
|
||||
'implements' => $this->implements,
|
||||
'stmts' => array_merge($this->uses, $this->enumCases, $this->constants, $this->methods),
|
||||
'attrGroups' => $this->attributeGroups,
|
||||
], $this->attributes);
|
||||
}
|
||||
}
|
73
vendor/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php
vendored
Normal file
73
vendor/nikic/php-parser/lib/PhpParser/Builder/FunctionLike.php
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
|
||||
abstract class FunctionLike extends Declaration
|
||||
{
|
||||
protected $returnByRef = false;
|
||||
protected $params = [];
|
||||
|
||||
/** @var string|Node\Name|Node\NullableType|null */
|
||||
protected $returnType = null;
|
||||
|
||||
/**
|
||||
* Make the function return by reference.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeReturnByRef() {
|
||||
$this->returnByRef = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a parameter.
|
||||
*
|
||||
* @param Node\Param|Param $param The parameter to add
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addParam($param) {
|
||||
$param = BuilderHelpers::normalizeNode($param);
|
||||
|
||||
if (!$param instanceof Node\Param) {
|
||||
throw new \LogicException(sprintf('Expected parameter node, got "%s"', $param->getType()));
|
||||
}
|
||||
|
||||
$this->params[] = $param;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds multiple parameters.
|
||||
*
|
||||
* @param array $params The parameters to add
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addParams(array $params) {
|
||||
foreach ($params as $param) {
|
||||
$this->addParam($param);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the return type for PHP 7.
|
||||
*
|
||||
* @param string|Node\Name|Node\Identifier|Node\ComplexType $type
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function setReturnType($type) {
|
||||
$this->returnType = BuilderHelpers::normalizeType($type);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
67
vendor/nikic/php-parser/lib/PhpParser/Builder/Function_.php
vendored
Normal file
67
vendor/nikic/php-parser/lib/PhpParser/Builder/Function_.php
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class Function_ extends FunctionLike
|
||||
{
|
||||
protected $name;
|
||||
protected $stmts = [];
|
||||
|
||||
/** @var Node\AttributeGroup[] */
|
||||
protected $attributeGroups = [];
|
||||
|
||||
/**
|
||||
* Creates a function builder.
|
||||
*
|
||||
* @param string $name Name of the function
|
||||
*/
|
||||
public function __construct(string $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a statement.
|
||||
*
|
||||
* @param Node|PhpParser\Builder $stmt The statement to add
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addStmt($stmt) {
|
||||
$this->stmts[] = BuilderHelpers::normalizeStmt($stmt);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute group.
|
||||
*
|
||||
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addAttribute($attribute) {
|
||||
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built function node.
|
||||
*
|
||||
* @return Stmt\Function_ The built function node
|
||||
*/
|
||||
public function getNode() : Node {
|
||||
return new Stmt\Function_($this->name, [
|
||||
'byRef' => $this->returnByRef,
|
||||
'params' => $this->params,
|
||||
'returnType' => $this->returnType,
|
||||
'stmts' => $this->stmts,
|
||||
'attrGroups' => $this->attributeGroups,
|
||||
], $this->attributes);
|
||||
}
|
||||
}
|
93
vendor/nikic/php-parser/lib/PhpParser/Builder/Interface_.php
vendored
Normal file
93
vendor/nikic/php-parser/lib/PhpParser/Builder/Interface_.php
vendored
Normal file
|
@ -0,0 +1,93 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class Interface_ extends Declaration
|
||||
{
|
||||
protected $name;
|
||||
protected $extends = [];
|
||||
protected $constants = [];
|
||||
protected $methods = [];
|
||||
|
||||
/** @var Node\AttributeGroup[] */
|
||||
protected $attributeGroups = [];
|
||||
|
||||
/**
|
||||
* Creates an interface builder.
|
||||
*
|
||||
* @param string $name Name of the interface
|
||||
*/
|
||||
public function __construct(string $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends one or more interfaces.
|
||||
*
|
||||
* @param Name|string ...$interfaces Names of interfaces to extend
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function extend(...$interfaces) {
|
||||
foreach ($interfaces as $interface) {
|
||||
$this->extends[] = BuilderHelpers::normalizeName($interface);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a statement.
|
||||
*
|
||||
* @param Stmt|PhpParser\Builder $stmt The statement to add
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addStmt($stmt) {
|
||||
$stmt = BuilderHelpers::normalizeNode($stmt);
|
||||
|
||||
if ($stmt instanceof Stmt\ClassConst) {
|
||||
$this->constants[] = $stmt;
|
||||
} elseif ($stmt instanceof Stmt\ClassMethod) {
|
||||
// we erase all statements in the body of an interface method
|
||||
$stmt->stmts = null;
|
||||
$this->methods[] = $stmt;
|
||||
} else {
|
||||
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute group.
|
||||
*
|
||||
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addAttribute($attribute) {
|
||||
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built interface node.
|
||||
*
|
||||
* @return Stmt\Interface_ The built interface node
|
||||
*/
|
||||
public function getNode() : PhpParser\Node {
|
||||
return new Stmt\Interface_($this->name, [
|
||||
'extends' => $this->extends,
|
||||
'stmts' => array_merge($this->constants, $this->methods),
|
||||
'attrGroups' => $this->attributeGroups,
|
||||
], $this->attributes);
|
||||
}
|
||||
}
|
146
vendor/nikic/php-parser/lib/PhpParser/Builder/Method.php
vendored
Normal file
146
vendor/nikic/php-parser/lib/PhpParser/Builder/Method.php
vendored
Normal file
|
@ -0,0 +1,146 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class Method extends FunctionLike
|
||||
{
|
||||
protected $name;
|
||||
protected $flags = 0;
|
||||
|
||||
/** @var array|null */
|
||||
protected $stmts = [];
|
||||
|
||||
/** @var Node\AttributeGroup[] */
|
||||
protected $attributeGroups = [];
|
||||
|
||||
/**
|
||||
* Creates a method builder.
|
||||
*
|
||||
* @param string $name Name of the method
|
||||
*/
|
||||
public function __construct(string $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the method public.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makePublic() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the method protected.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeProtected() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the method private.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makePrivate() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the method static.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeStatic() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_STATIC);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the method abstract.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeAbstract() {
|
||||
if (!empty($this->stmts)) {
|
||||
throw new \LogicException('Cannot make method with statements abstract');
|
||||
}
|
||||
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_ABSTRACT);
|
||||
$this->stmts = null; // abstract methods don't have statements
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the method final.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeFinal() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_FINAL);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a statement.
|
||||
*
|
||||
* @param Node|PhpParser\Builder $stmt The statement to add
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addStmt($stmt) {
|
||||
if (null === $this->stmts) {
|
||||
throw new \LogicException('Cannot add statements to an abstract method');
|
||||
}
|
||||
|
||||
$this->stmts[] = BuilderHelpers::normalizeStmt($stmt);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute group.
|
||||
*
|
||||
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addAttribute($attribute) {
|
||||
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built method node.
|
||||
*
|
||||
* @return Stmt\ClassMethod The built method node
|
||||
*/
|
||||
public function getNode() : Node {
|
||||
return new Stmt\ClassMethod($this->name, [
|
||||
'flags' => $this->flags,
|
||||
'byRef' => $this->returnByRef,
|
||||
'params' => $this->params,
|
||||
'returnType' => $this->returnType,
|
||||
'stmts' => $this->stmts,
|
||||
'attrGroups' => $this->attributeGroups,
|
||||
], $this->attributes);
|
||||
}
|
||||
}
|
45
vendor/nikic/php-parser/lib/PhpParser/Builder/Namespace_.php
vendored
Normal file
45
vendor/nikic/php-parser/lib/PhpParser/Builder/Namespace_.php
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class Namespace_ extends Declaration
|
||||
{
|
||||
private $name;
|
||||
private $stmts = [];
|
||||
|
||||
/**
|
||||
* Creates a namespace builder.
|
||||
*
|
||||
* @param Node\Name|string|null $name Name of the namespace
|
||||
*/
|
||||
public function __construct($name) {
|
||||
$this->name = null !== $name ? BuilderHelpers::normalizeName($name) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a statement.
|
||||
*
|
||||
* @param Node|PhpParser\Builder $stmt The statement to add
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addStmt($stmt) {
|
||||
$this->stmts[] = BuilderHelpers::normalizeStmt($stmt);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built node.
|
||||
*
|
||||
* @return Stmt\Namespace_ The built node
|
||||
*/
|
||||
public function getNode() : Node {
|
||||
return new Stmt\Namespace_($this->name, $this->stmts, $this->attributes);
|
||||
}
|
||||
}
|
168
vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php
vendored
Normal file
168
vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php
vendored
Normal file
|
@ -0,0 +1,168 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
|
||||
class Param implements PhpParser\Builder
|
||||
{
|
||||
protected $name;
|
||||
|
||||
protected $default = null;
|
||||
|
||||
/** @var Node\Identifier|Node\Name|Node\NullableType|null */
|
||||
protected $type = null;
|
||||
|
||||
protected $byRef = false;
|
||||
|
||||
protected $variadic = false;
|
||||
|
||||
protected $flags = 0;
|
||||
|
||||
/** @var Node\AttributeGroup[] */
|
||||
protected $attributeGroups = [];
|
||||
|
||||
/**
|
||||
* Creates a parameter builder.
|
||||
*
|
||||
* @param string $name Name of the parameter
|
||||
*/
|
||||
public function __construct(string $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default value for the parameter.
|
||||
*
|
||||
* @param mixed $value Default value to use
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function setDefault($value) {
|
||||
$this->default = BuilderHelpers::normalizeValue($value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets type for the parameter.
|
||||
*
|
||||
* @param string|Node\Name|Node\Identifier|Node\ComplexType $type Parameter type
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function setType($type) {
|
||||
$this->type = BuilderHelpers::normalizeType($type);
|
||||
if ($this->type == 'void') {
|
||||
throw new \LogicException('Parameter type cannot be void');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets type for the parameter.
|
||||
*
|
||||
* @param string|Node\Name|Node\Identifier|Node\ComplexType $type Parameter type
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*
|
||||
* @deprecated Use setType() instead
|
||||
*/
|
||||
public function setTypeHint($type) {
|
||||
return $this->setType($type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the parameter accept the value by reference.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeByRef() {
|
||||
$this->byRef = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make the parameter variadic
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeVariadic() {
|
||||
$this->variadic = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the (promoted) parameter public.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makePublic() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PUBLIC);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the (promoted) parameter protected.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeProtected() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PROTECTED);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the (promoted) parameter private.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makePrivate() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PRIVATE);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the (promoted) parameter readonly.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeReadonly() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_READONLY);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute group.
|
||||
*
|
||||
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addAttribute($attribute) {
|
||||
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built parameter node.
|
||||
*
|
||||
* @return Node\Param The built parameter node
|
||||
*/
|
||||
public function getNode() : Node {
|
||||
return new Node\Param(
|
||||
new Node\Expr\Variable($this->name),
|
||||
$this->default, $this->type, $this->byRef, $this->variadic, [], $this->flags, $this->attributeGroups
|
||||
);
|
||||
}
|
||||
}
|
161
vendor/nikic/php-parser/lib/PhpParser/Builder/Property.php
vendored
Normal file
161
vendor/nikic/php-parser/lib/PhpParser/Builder/Property.php
vendored
Normal file
|
@ -0,0 +1,161 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\Node\ComplexType;
|
||||
|
||||
class Property implements PhpParser\Builder
|
||||
{
|
||||
protected $name;
|
||||
|
||||
protected $flags = 0;
|
||||
protected $default = null;
|
||||
protected $attributes = [];
|
||||
|
||||
/** @var null|Identifier|Name|NullableType */
|
||||
protected $type;
|
||||
|
||||
/** @var Node\AttributeGroup[] */
|
||||
protected $attributeGroups = [];
|
||||
|
||||
/**
|
||||
* Creates a property builder.
|
||||
*
|
||||
* @param string $name Name of the property
|
||||
*/
|
||||
public function __construct(string $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the property public.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makePublic() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PUBLIC);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the property protected.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeProtected() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PROTECTED);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the property private.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makePrivate() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_PRIVATE);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the property static.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeStatic() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_STATIC);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the property readonly.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeReadonly() {
|
||||
$this->flags = BuilderHelpers::addModifier($this->flags, Stmt\Class_::MODIFIER_READONLY);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets default value for the property.
|
||||
*
|
||||
* @param mixed $value Default value to use
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function setDefault($value) {
|
||||
$this->default = BuilderHelpers::normalizeValue($value);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets doc comment for the property.
|
||||
*
|
||||
* @param PhpParser\Comment\Doc|string $docComment Doc comment to set
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function setDocComment($docComment) {
|
||||
$this->attributes = [
|
||||
'comments' => [BuilderHelpers::normalizeDocComment($docComment)]
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the property type for PHP 7.4+.
|
||||
*
|
||||
* @param string|Name|Identifier|ComplexType $type
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setType($type) {
|
||||
$this->type = BuilderHelpers::normalizeType($type);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute group.
|
||||
*
|
||||
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addAttribute($attribute) {
|
||||
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built class node.
|
||||
*
|
||||
* @return Stmt\Property The built property node
|
||||
*/
|
||||
public function getNode() : PhpParser\Node {
|
||||
return new Stmt\Property(
|
||||
$this->flags !== 0 ? $this->flags : Stmt\Class_::MODIFIER_PUBLIC,
|
||||
[
|
||||
new Stmt\PropertyProperty($this->name, $this->default)
|
||||
],
|
||||
$this->attributes,
|
||||
$this->type,
|
||||
$this->attributeGroups
|
||||
);
|
||||
}
|
||||
}
|
64
vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUse.php
vendored
Normal file
64
vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUse.php
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser\Builder;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class TraitUse implements Builder
|
||||
{
|
||||
protected $traits = [];
|
||||
protected $adaptations = [];
|
||||
|
||||
/**
|
||||
* Creates a trait use builder.
|
||||
*
|
||||
* @param Node\Name|string ...$traits Names of used traits
|
||||
*/
|
||||
public function __construct(...$traits) {
|
||||
foreach ($traits as $trait) {
|
||||
$this->and($trait);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds used trait.
|
||||
*
|
||||
* @param Node\Name|string $trait Trait name
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function and($trait) {
|
||||
$this->traits[] = BuilderHelpers::normalizeName($trait);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds trait adaptation.
|
||||
*
|
||||
* @param Stmt\TraitUseAdaptation|Builder\TraitUseAdaptation $adaptation Trait adaptation
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function with($adaptation) {
|
||||
$adaptation = BuilderHelpers::normalizeNode($adaptation);
|
||||
|
||||
if (!$adaptation instanceof Stmt\TraitUseAdaptation) {
|
||||
throw new \LogicException('Adaptation must have type TraitUseAdaptation');
|
||||
}
|
||||
|
||||
$this->adaptations[] = $adaptation;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built node.
|
||||
*
|
||||
* @return Node The built node
|
||||
*/
|
||||
public function getNode() : Node {
|
||||
return new Stmt\TraitUse($this->traits, $this->adaptations);
|
||||
}
|
||||
}
|
148
vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUseAdaptation.php
vendored
Normal file
148
vendor/nikic/php-parser/lib/PhpParser/Builder/TraitUseAdaptation.php
vendored
Normal file
|
@ -0,0 +1,148 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser\Builder;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class TraitUseAdaptation implements Builder
|
||||
{
|
||||
const TYPE_UNDEFINED = 0;
|
||||
const TYPE_ALIAS = 1;
|
||||
const TYPE_PRECEDENCE = 2;
|
||||
|
||||
/** @var int Type of building adaptation */
|
||||
protected $type;
|
||||
|
||||
protected $trait;
|
||||
protected $method;
|
||||
|
||||
protected $modifier = null;
|
||||
protected $alias = null;
|
||||
|
||||
protected $insteadof = [];
|
||||
|
||||
/**
|
||||
* Creates a trait use adaptation builder.
|
||||
*
|
||||
* @param Node\Name|string|null $trait Name of adaptated trait
|
||||
* @param Node\Identifier|string $method Name of adaptated method
|
||||
*/
|
||||
public function __construct($trait, $method) {
|
||||
$this->type = self::TYPE_UNDEFINED;
|
||||
|
||||
$this->trait = is_null($trait)? null: BuilderHelpers::normalizeName($trait);
|
||||
$this->method = BuilderHelpers::normalizeIdentifier($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets alias of method.
|
||||
*
|
||||
* @param Node\Identifier|string $alias Alias for adaptated method
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function as($alias) {
|
||||
if ($this->type === self::TYPE_UNDEFINED) {
|
||||
$this->type = self::TYPE_ALIAS;
|
||||
}
|
||||
|
||||
if ($this->type !== self::TYPE_ALIAS) {
|
||||
throw new \LogicException('Cannot set alias for not alias adaptation buider');
|
||||
}
|
||||
|
||||
$this->alias = $alias;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets adaptated method public.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makePublic() {
|
||||
$this->setModifier(Stmt\Class_::MODIFIER_PUBLIC);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets adaptated method protected.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makeProtected() {
|
||||
$this->setModifier(Stmt\Class_::MODIFIER_PROTECTED);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets adaptated method private.
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function makePrivate() {
|
||||
$this->setModifier(Stmt\Class_::MODIFIER_PRIVATE);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds overwritten traits.
|
||||
*
|
||||
* @param Node\Name|string ...$traits Traits for overwrite
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function insteadof(...$traits) {
|
||||
if ($this->type === self::TYPE_UNDEFINED) {
|
||||
if (is_null($this->trait)) {
|
||||
throw new \LogicException('Precedence adaptation must have trait');
|
||||
}
|
||||
|
||||
$this->type = self::TYPE_PRECEDENCE;
|
||||
}
|
||||
|
||||
if ($this->type !== self::TYPE_PRECEDENCE) {
|
||||
throw new \LogicException('Cannot add overwritten traits for not precedence adaptation buider');
|
||||
}
|
||||
|
||||
foreach ($traits as $trait) {
|
||||
$this->insteadof[] = BuilderHelpers::normalizeName($trait);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function setModifier(int $modifier) {
|
||||
if ($this->type === self::TYPE_UNDEFINED) {
|
||||
$this->type = self::TYPE_ALIAS;
|
||||
}
|
||||
|
||||
if ($this->type !== self::TYPE_ALIAS) {
|
||||
throw new \LogicException('Cannot set access modifier for not alias adaptation buider');
|
||||
}
|
||||
|
||||
if (is_null($this->modifier)) {
|
||||
$this->modifier = $modifier;
|
||||
} else {
|
||||
throw new \LogicException('Multiple access type modifiers are not allowed');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built node.
|
||||
*
|
||||
* @return Node The built node
|
||||
*/
|
||||
public function getNode() : Node {
|
||||
switch ($this->type) {
|
||||
case self::TYPE_ALIAS:
|
||||
return new Stmt\TraitUseAdaptation\Alias($this->trait, $this->method, $this->modifier, $this->alias);
|
||||
case self::TYPE_PRECEDENCE:
|
||||
return new Stmt\TraitUseAdaptation\Precedence($this->trait, $this->method, $this->insteadof);
|
||||
default:
|
||||
throw new \LogicException('Type of adaptation is not defined');
|
||||
}
|
||||
}
|
||||
}
|
78
vendor/nikic/php-parser/lib/PhpParser/Builder/Trait_.php
vendored
Normal file
78
vendor/nikic/php-parser/lib/PhpParser/Builder/Trait_.php
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class Trait_ extends Declaration
|
||||
{
|
||||
protected $name;
|
||||
protected $uses = [];
|
||||
protected $properties = [];
|
||||
protected $methods = [];
|
||||
|
||||
/** @var Node\AttributeGroup[] */
|
||||
protected $attributeGroups = [];
|
||||
|
||||
/**
|
||||
* Creates an interface builder.
|
||||
*
|
||||
* @param string $name Name of the interface
|
||||
*/
|
||||
public function __construct(string $name) {
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a statement.
|
||||
*
|
||||
* @param Stmt|PhpParser\Builder $stmt The statement to add
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addStmt($stmt) {
|
||||
$stmt = BuilderHelpers::normalizeNode($stmt);
|
||||
|
||||
if ($stmt instanceof Stmt\Property) {
|
||||
$this->properties[] = $stmt;
|
||||
} elseif ($stmt instanceof Stmt\ClassMethod) {
|
||||
$this->methods[] = $stmt;
|
||||
} elseif ($stmt instanceof Stmt\TraitUse) {
|
||||
$this->uses[] = $stmt;
|
||||
} else {
|
||||
throw new \LogicException(sprintf('Unexpected node of type "%s"', $stmt->getType()));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an attribute group.
|
||||
*
|
||||
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function addAttribute($attribute) {
|
||||
$this->attributeGroups[] = BuilderHelpers::normalizeAttribute($attribute);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built trait node.
|
||||
*
|
||||
* @return Stmt\Trait_ The built interface node
|
||||
*/
|
||||
public function getNode() : PhpParser\Node {
|
||||
return new Stmt\Trait_(
|
||||
$this->name, [
|
||||
'stmts' => array_merge($this->uses, $this->properties, $this->methods),
|
||||
'attrGroups' => $this->attributeGroups,
|
||||
], $this->attributes
|
||||
);
|
||||
}
|
||||
}
|
49
vendor/nikic/php-parser/lib/PhpParser/Builder/Use_.php
vendored
Normal file
49
vendor/nikic/php-parser/lib/PhpParser/Builder/Use_.php
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Builder;
|
||||
|
||||
use PhpParser\Builder;
|
||||
use PhpParser\BuilderHelpers;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class Use_ implements Builder
|
||||
{
|
||||
protected $name;
|
||||
protected $type;
|
||||
protected $alias = null;
|
||||
|
||||
/**
|
||||
* Creates a name use (alias) builder.
|
||||
*
|
||||
* @param Node\Name|string $name Name of the entity (namespace, class, function, constant) to alias
|
||||
* @param int $type One of the Stmt\Use_::TYPE_* constants
|
||||
*/
|
||||
public function __construct($name, int $type) {
|
||||
$this->name = BuilderHelpers::normalizeName($name);
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets alias for used name.
|
||||
*
|
||||
* @param string $alias Alias to use (last component of full name by default)
|
||||
*
|
||||
* @return $this The builder instance (for fluid interface)
|
||||
*/
|
||||
public function as(string $alias) {
|
||||
$this->alias = $alias;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the built node.
|
||||
*
|
||||
* @return Stmt\Use_ The built node
|
||||
*/
|
||||
public function getNode() : Node {
|
||||
return new Stmt\Use_([
|
||||
new Stmt\UseUse($this->name, $this->alias)
|
||||
], $this->type);
|
||||
}
|
||||
}
|
399
vendor/nikic/php-parser/lib/PhpParser/BuilderFactory.php
vendored
Normal file
399
vendor/nikic/php-parser/lib/PhpParser/BuilderFactory.php
vendored
Normal file
|
@ -0,0 +1,399 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser;
|
||||
|
||||
use PhpParser\Node\Arg;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Expr\BinaryOp\Concat;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\Node\Stmt\Use_;
|
||||
|
||||
class BuilderFactory
|
||||
{
|
||||
/**
|
||||
* Creates an attribute node.
|
||||
*
|
||||
* @param string|Name $name Name of the attribute
|
||||
* @param array $args Attribute named arguments
|
||||
*
|
||||
* @return Node\Attribute
|
||||
*/
|
||||
public function attribute($name, array $args = []) : Node\Attribute {
|
||||
return new Node\Attribute(
|
||||
BuilderHelpers::normalizeName($name),
|
||||
$this->args($args)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a namespace builder.
|
||||
*
|
||||
* @param null|string|Node\Name $name Name of the namespace
|
||||
*
|
||||
* @return Builder\Namespace_ The created namespace builder
|
||||
*/
|
||||
public function namespace($name) : Builder\Namespace_ {
|
||||
return new Builder\Namespace_($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a class builder.
|
||||
*
|
||||
* @param string $name Name of the class
|
||||
*
|
||||
* @return Builder\Class_ The created class builder
|
||||
*/
|
||||
public function class(string $name) : Builder\Class_ {
|
||||
return new Builder\Class_($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an interface builder.
|
||||
*
|
||||
* @param string $name Name of the interface
|
||||
*
|
||||
* @return Builder\Interface_ The created interface builder
|
||||
*/
|
||||
public function interface(string $name) : Builder\Interface_ {
|
||||
return new Builder\Interface_($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a trait builder.
|
||||
*
|
||||
* @param string $name Name of the trait
|
||||
*
|
||||
* @return Builder\Trait_ The created trait builder
|
||||
*/
|
||||
public function trait(string $name) : Builder\Trait_ {
|
||||
return new Builder\Trait_($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an enum builder.
|
||||
*
|
||||
* @param string $name Name of the enum
|
||||
*
|
||||
* @return Builder\Enum_ The created enum builder
|
||||
*/
|
||||
public function enum(string $name) : Builder\Enum_ {
|
||||
return new Builder\Enum_($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a trait use builder.
|
||||
*
|
||||
* @param Node\Name|string ...$traits Trait names
|
||||
*
|
||||
* @return Builder\TraitUse The create trait use builder
|
||||
*/
|
||||
public function useTrait(...$traits) : Builder\TraitUse {
|
||||
return new Builder\TraitUse(...$traits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a trait use adaptation builder.
|
||||
*
|
||||
* @param Node\Name|string|null $trait Trait name
|
||||
* @param Node\Identifier|string $method Method name
|
||||
*
|
||||
* @return Builder\TraitUseAdaptation The create trait use adaptation builder
|
||||
*/
|
||||
public function traitUseAdaptation($trait, $method = null) : Builder\TraitUseAdaptation {
|
||||
if ($method === null) {
|
||||
$method = $trait;
|
||||
$trait = null;
|
||||
}
|
||||
|
||||
return new Builder\TraitUseAdaptation($trait, $method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a method builder.
|
||||
*
|
||||
* @param string $name Name of the method
|
||||
*
|
||||
* @return Builder\Method The created method builder
|
||||
*/
|
||||
public function method(string $name) : Builder\Method {
|
||||
return new Builder\Method($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a parameter builder.
|
||||
*
|
||||
* @param string $name Name of the parameter
|
||||
*
|
||||
* @return Builder\Param The created parameter builder
|
||||
*/
|
||||
public function param(string $name) : Builder\Param {
|
||||
return new Builder\Param($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a property builder.
|
||||
*
|
||||
* @param string $name Name of the property
|
||||
*
|
||||
* @return Builder\Property The created property builder
|
||||
*/
|
||||
public function property(string $name) : Builder\Property {
|
||||
return new Builder\Property($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function builder.
|
||||
*
|
||||
* @param string $name Name of the function
|
||||
*
|
||||
* @return Builder\Function_ The created function builder
|
||||
*/
|
||||
public function function(string $name) : Builder\Function_ {
|
||||
return new Builder\Function_($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a namespace/class use builder.
|
||||
*
|
||||
* @param Node\Name|string $name Name of the entity (namespace or class) to alias
|
||||
*
|
||||
* @return Builder\Use_ The created use builder
|
||||
*/
|
||||
public function use($name) : Builder\Use_ {
|
||||
return new Builder\Use_($name, Use_::TYPE_NORMAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function use builder.
|
||||
*
|
||||
* @param Node\Name|string $name Name of the function to alias
|
||||
*
|
||||
* @return Builder\Use_ The created use function builder
|
||||
*/
|
||||
public function useFunction($name) : Builder\Use_ {
|
||||
return new Builder\Use_($name, Use_::TYPE_FUNCTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a constant use builder.
|
||||
*
|
||||
* @param Node\Name|string $name Name of the const to alias
|
||||
*
|
||||
* @return Builder\Use_ The created use const builder
|
||||
*/
|
||||
public function useConst($name) : Builder\Use_ {
|
||||
return new Builder\Use_($name, Use_::TYPE_CONSTANT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a class constant builder.
|
||||
*
|
||||
* @param string|Identifier $name Name
|
||||
* @param Node\Expr|bool|null|int|float|string|array $value Value
|
||||
*
|
||||
* @return Builder\ClassConst The created use const builder
|
||||
*/
|
||||
public function classConst($name, $value) : Builder\ClassConst {
|
||||
return new Builder\ClassConst($name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an enum case builder.
|
||||
*
|
||||
* @param string|Identifier $name Name
|
||||
*
|
||||
* @return Builder\EnumCase The created use const builder
|
||||
*/
|
||||
public function enumCase($name) : Builder\EnumCase {
|
||||
return new Builder\EnumCase($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates node a for a literal value.
|
||||
*
|
||||
* @param Expr|bool|null|int|float|string|array $value $value
|
||||
*
|
||||
* @return Expr
|
||||
*/
|
||||
public function val($value) : Expr {
|
||||
return BuilderHelpers::normalizeValue($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates variable node.
|
||||
*
|
||||
* @param string|Expr $name Name
|
||||
*
|
||||
* @return Expr\Variable
|
||||
*/
|
||||
public function var($name) : Expr\Variable {
|
||||
if (!\is_string($name) && !$name instanceof Expr) {
|
||||
throw new \LogicException('Variable name must be string or Expr');
|
||||
}
|
||||
|
||||
return new Expr\Variable($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes an argument list.
|
||||
*
|
||||
* Creates Arg nodes for all arguments and converts literal values to expressions.
|
||||
*
|
||||
* @param array $args List of arguments to normalize
|
||||
*
|
||||
* @return Arg[]
|
||||
*/
|
||||
public function args(array $args) : array {
|
||||
$normalizedArgs = [];
|
||||
foreach ($args as $key => $arg) {
|
||||
if (!($arg instanceof Arg)) {
|
||||
$arg = new Arg(BuilderHelpers::normalizeValue($arg));
|
||||
}
|
||||
if (\is_string($key)) {
|
||||
$arg->name = BuilderHelpers::normalizeIdentifier($key);
|
||||
}
|
||||
$normalizedArgs[] = $arg;
|
||||
}
|
||||
return $normalizedArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function call node.
|
||||
*
|
||||
* @param string|Name|Expr $name Function name
|
||||
* @param array $args Function arguments
|
||||
*
|
||||
* @return Expr\FuncCall
|
||||
*/
|
||||
public function funcCall($name, array $args = []) : Expr\FuncCall {
|
||||
return new Expr\FuncCall(
|
||||
BuilderHelpers::normalizeNameOrExpr($name),
|
||||
$this->args($args)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a method call node.
|
||||
*
|
||||
* @param Expr $var Variable the method is called on
|
||||
* @param string|Identifier|Expr $name Method name
|
||||
* @param array $args Method arguments
|
||||
*
|
||||
* @return Expr\MethodCall
|
||||
*/
|
||||
public function methodCall(Expr $var, $name, array $args = []) : Expr\MethodCall {
|
||||
return new Expr\MethodCall(
|
||||
$var,
|
||||
BuilderHelpers::normalizeIdentifierOrExpr($name),
|
||||
$this->args($args)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a static method call node.
|
||||
*
|
||||
* @param string|Name|Expr $class Class name
|
||||
* @param string|Identifier|Expr $name Method name
|
||||
* @param array $args Method arguments
|
||||
*
|
||||
* @return Expr\StaticCall
|
||||
*/
|
||||
public function staticCall($class, $name, array $args = []) : Expr\StaticCall {
|
||||
return new Expr\StaticCall(
|
||||
BuilderHelpers::normalizeNameOrExpr($class),
|
||||
BuilderHelpers::normalizeIdentifierOrExpr($name),
|
||||
$this->args($args)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an object creation node.
|
||||
*
|
||||
* @param string|Name|Expr $class Class name
|
||||
* @param array $args Constructor arguments
|
||||
*
|
||||
* @return Expr\New_
|
||||
*/
|
||||
public function new($class, array $args = []) : Expr\New_ {
|
||||
return new Expr\New_(
|
||||
BuilderHelpers::normalizeNameOrExpr($class),
|
||||
$this->args($args)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a constant fetch node.
|
||||
*
|
||||
* @param string|Name $name Constant name
|
||||
*
|
||||
* @return Expr\ConstFetch
|
||||
*/
|
||||
public function constFetch($name) : Expr\ConstFetch {
|
||||
return new Expr\ConstFetch(BuilderHelpers::normalizeName($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a property fetch node.
|
||||
*
|
||||
* @param Expr $var Variable holding object
|
||||
* @param string|Identifier|Expr $name Property name
|
||||
*
|
||||
* @return Expr\PropertyFetch
|
||||
*/
|
||||
public function propertyFetch(Expr $var, $name) : Expr\PropertyFetch {
|
||||
return new Expr\PropertyFetch($var, BuilderHelpers::normalizeIdentifierOrExpr($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a class constant fetch node.
|
||||
*
|
||||
* @param string|Name|Expr $class Class name
|
||||
* @param string|Identifier|Expr $name Constant name
|
||||
*
|
||||
* @return Expr\ClassConstFetch
|
||||
*/
|
||||
public function classConstFetch($class, $name): Expr\ClassConstFetch {
|
||||
return new Expr\ClassConstFetch(
|
||||
BuilderHelpers::normalizeNameOrExpr($class),
|
||||
BuilderHelpers::normalizeIdentifierOrExpr($name)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates nested Concat nodes from a list of expressions.
|
||||
*
|
||||
* @param Expr|string ...$exprs Expressions or literal strings
|
||||
*
|
||||
* @return Concat
|
||||
*/
|
||||
public function concat(...$exprs) : Concat {
|
||||
$numExprs = count($exprs);
|
||||
if ($numExprs < 2) {
|
||||
throw new \LogicException('Expected at least two expressions');
|
||||
}
|
||||
|
||||
$lastConcat = $this->normalizeStringExpr($exprs[0]);
|
||||
for ($i = 1; $i < $numExprs; $i++) {
|
||||
$lastConcat = new Concat($lastConcat, $this->normalizeStringExpr($exprs[$i]));
|
||||
}
|
||||
return $lastConcat;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|Expr $expr
|
||||
* @return Expr
|
||||
*/
|
||||
private function normalizeStringExpr($expr) : Expr {
|
||||
if ($expr instanceof Expr) {
|
||||
return $expr;
|
||||
}
|
||||
|
||||
if (\is_string($expr)) {
|
||||
return new String_($expr);
|
||||
}
|
||||
|
||||
throw new \LogicException('Expected string or Expr');
|
||||
}
|
||||
}
|
335
vendor/nikic/php-parser/lib/PhpParser/BuilderHelpers.php
vendored
Normal file
335
vendor/nikic/php-parser/lib/PhpParser/BuilderHelpers.php
vendored
Normal file
|
@ -0,0 +1,335 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser;
|
||||
|
||||
use PhpParser\Node\ComplexType;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\NullableType;
|
||||
use PhpParser\Node\Scalar;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
/**
|
||||
* This class defines helpers used in the implementation of builders. Don't use it directly.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class BuilderHelpers
|
||||
{
|
||||
/**
|
||||
* Normalizes a node: Converts builder objects to nodes.
|
||||
*
|
||||
* @param Node|Builder $node The node to normalize
|
||||
*
|
||||
* @return Node The normalized node
|
||||
*/
|
||||
public static function normalizeNode($node) : Node {
|
||||
if ($node instanceof Builder) {
|
||||
return $node->getNode();
|
||||
}
|
||||
|
||||
if ($node instanceof Node) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
throw new \LogicException('Expected node or builder object');
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a node to a statement.
|
||||
*
|
||||
* Expressions are wrapped in a Stmt\Expression node.
|
||||
*
|
||||
* @param Node|Builder $node The node to normalize
|
||||
*
|
||||
* @return Stmt The normalized statement node
|
||||
*/
|
||||
public static function normalizeStmt($node) : Stmt {
|
||||
$node = self::normalizeNode($node);
|
||||
if ($node instanceof Stmt) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
if ($node instanceof Expr) {
|
||||
return new Stmt\Expression($node);
|
||||
}
|
||||
|
||||
throw new \LogicException('Expected statement or expression node');
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes strings to Identifier.
|
||||
*
|
||||
* @param string|Identifier $name The identifier to normalize
|
||||
*
|
||||
* @return Identifier The normalized identifier
|
||||
*/
|
||||
public static function normalizeIdentifier($name) : Identifier {
|
||||
if ($name instanceof Identifier) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
if (\is_string($name)) {
|
||||
return new Identifier($name);
|
||||
}
|
||||
|
||||
throw new \LogicException('Expected string or instance of Node\Identifier');
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes strings to Identifier, also allowing expressions.
|
||||
*
|
||||
* @param string|Identifier|Expr $name The identifier to normalize
|
||||
*
|
||||
* @return Identifier|Expr The normalized identifier or expression
|
||||
*/
|
||||
public static function normalizeIdentifierOrExpr($name) {
|
||||
if ($name instanceof Identifier || $name instanceof Expr) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
if (\is_string($name)) {
|
||||
return new Identifier($name);
|
||||
}
|
||||
|
||||
throw new \LogicException('Expected string or instance of Node\Identifier or Node\Expr');
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a name: Converts string names to Name nodes.
|
||||
*
|
||||
* @param Name|string $name The name to normalize
|
||||
*
|
||||
* @return Name The normalized name
|
||||
*/
|
||||
public static function normalizeName($name) : Name {
|
||||
if ($name instanceof Name) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
if (is_string($name)) {
|
||||
if (!$name) {
|
||||
throw new \LogicException('Name cannot be empty');
|
||||
}
|
||||
|
||||
if ($name[0] === '\\') {
|
||||
return new Name\FullyQualified(substr($name, 1));
|
||||
}
|
||||
|
||||
if (0 === strpos($name, 'namespace\\')) {
|
||||
return new Name\Relative(substr($name, strlen('namespace\\')));
|
||||
}
|
||||
|
||||
return new Name($name);
|
||||
}
|
||||
|
||||
throw new \LogicException('Name must be a string or an instance of Node\Name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a name: Converts string names to Name nodes, while also allowing expressions.
|
||||
*
|
||||
* @param Expr|Name|string $name The name to normalize
|
||||
*
|
||||
* @return Name|Expr The normalized name or expression
|
||||
*/
|
||||
public static function normalizeNameOrExpr($name) {
|
||||
if ($name instanceof Expr) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
if (!is_string($name) && !($name instanceof Name)) {
|
||||
throw new \LogicException(
|
||||
'Name must be a string or an instance of Node\Name or Node\Expr'
|
||||
);
|
||||
}
|
||||
|
||||
return self::normalizeName($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a type: Converts plain-text type names into proper AST representation.
|
||||
*
|
||||
* In particular, builtin types become Identifiers, custom types become Names and nullables
|
||||
* are wrapped in NullableType nodes.
|
||||
*
|
||||
* @param string|Name|Identifier|ComplexType $type The type to normalize
|
||||
*
|
||||
* @return Name|Identifier|ComplexType The normalized type
|
||||
*/
|
||||
public static function normalizeType($type) {
|
||||
if (!is_string($type)) {
|
||||
if (
|
||||
!$type instanceof Name && !$type instanceof Identifier &&
|
||||
!$type instanceof ComplexType
|
||||
) {
|
||||
throw new \LogicException(
|
||||
'Type must be a string, or an instance of Name, Identifier or ComplexType'
|
||||
);
|
||||
}
|
||||
return $type;
|
||||
}
|
||||
|
||||
$nullable = false;
|
||||
if (strlen($type) > 0 && $type[0] === '?') {
|
||||
$nullable = true;
|
||||
$type = substr($type, 1);
|
||||
}
|
||||
|
||||
$builtinTypes = [
|
||||
'array',
|
||||
'callable',
|
||||
'bool',
|
||||
'int',
|
||||
'float',
|
||||
'string',
|
||||
'iterable',
|
||||
'void',
|
||||
'object',
|
||||
'null',
|
||||
'false',
|
||||
'mixed',
|
||||
'never',
|
||||
'true',
|
||||
];
|
||||
|
||||
$lowerType = strtolower($type);
|
||||
if (in_array($lowerType, $builtinTypes)) {
|
||||
$type = new Identifier($lowerType);
|
||||
} else {
|
||||
$type = self::normalizeName($type);
|
||||
}
|
||||
|
||||
$notNullableTypes = [
|
||||
'void', 'mixed', 'never',
|
||||
];
|
||||
if ($nullable && in_array((string) $type, $notNullableTypes)) {
|
||||
throw new \LogicException(sprintf('%s type cannot be nullable', $type));
|
||||
}
|
||||
|
||||
return $nullable ? new NullableType($type) : $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a value: Converts nulls, booleans, integers,
|
||||
* floats, strings and arrays into their respective nodes
|
||||
*
|
||||
* @param Node\Expr|bool|null|int|float|string|array $value The value to normalize
|
||||
*
|
||||
* @return Expr The normalized value
|
||||
*/
|
||||
public static function normalizeValue($value) : Expr {
|
||||
if ($value instanceof Node\Expr) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (is_null($value)) {
|
||||
return new Expr\ConstFetch(
|
||||
new Name('null')
|
||||
);
|
||||
}
|
||||
|
||||
if (is_bool($value)) {
|
||||
return new Expr\ConstFetch(
|
||||
new Name($value ? 'true' : 'false')
|
||||
);
|
||||
}
|
||||
|
||||
if (is_int($value)) {
|
||||
return new Scalar\LNumber($value);
|
||||
}
|
||||
|
||||
if (is_float($value)) {
|
||||
return new Scalar\DNumber($value);
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
return new Scalar\String_($value);
|
||||
}
|
||||
|
||||
if (is_array($value)) {
|
||||
$items = [];
|
||||
$lastKey = -1;
|
||||
foreach ($value as $itemKey => $itemValue) {
|
||||
// for consecutive, numeric keys don't generate keys
|
||||
if (null !== $lastKey && ++$lastKey === $itemKey) {
|
||||
$items[] = new Expr\ArrayItem(
|
||||
self::normalizeValue($itemValue)
|
||||
);
|
||||
} else {
|
||||
$lastKey = null;
|
||||
$items[] = new Expr\ArrayItem(
|
||||
self::normalizeValue($itemValue),
|
||||
self::normalizeValue($itemKey)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new Expr\Array_($items);
|
||||
}
|
||||
|
||||
throw new \LogicException('Invalid value');
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a doc comment: Converts plain strings to PhpParser\Comment\Doc.
|
||||
*
|
||||
* @param Comment\Doc|string $docComment The doc comment to normalize
|
||||
*
|
||||
* @return Comment\Doc The normalized doc comment
|
||||
*/
|
||||
public static function normalizeDocComment($docComment) : Comment\Doc {
|
||||
if ($docComment instanceof Comment\Doc) {
|
||||
return $docComment;
|
||||
}
|
||||
|
||||
if (is_string($docComment)) {
|
||||
return new Comment\Doc($docComment);
|
||||
}
|
||||
|
||||
throw new \LogicException('Doc comment must be a string or an instance of PhpParser\Comment\Doc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a attribute: Converts attribute to the Attribute Group if needed.
|
||||
*
|
||||
* @param Node\Attribute|Node\AttributeGroup $attribute
|
||||
*
|
||||
* @return Node\AttributeGroup The Attribute Group
|
||||
*/
|
||||
public static function normalizeAttribute($attribute) : Node\AttributeGroup
|
||||
{
|
||||
if ($attribute instanceof Node\AttributeGroup) {
|
||||
return $attribute;
|
||||
}
|
||||
|
||||
if (!($attribute instanceof Node\Attribute)) {
|
||||
throw new \LogicException('Attribute must be an instance of PhpParser\Node\Attribute or PhpParser\Node\AttributeGroup');
|
||||
}
|
||||
|
||||
return new Node\AttributeGroup([$attribute]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a modifier and returns new modifier bitmask.
|
||||
*
|
||||
* @param int $modifiers Existing modifiers
|
||||
* @param int $modifier Modifier to set
|
||||
*
|
||||
* @return int New modifiers
|
||||
*/
|
||||
public static function addModifier(int $modifiers, int $modifier) : int {
|
||||
Stmt\Class_::verifyModifier($modifiers, $modifier);
|
||||
return $modifiers | $modifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a modifier and returns new modifier bitmask.
|
||||
* @return int New modifiers
|
||||
*/
|
||||
public static function addClassModifier(int $existingModifiers, int $modifierToSet) : int {
|
||||
Stmt\Class_::verifyClassModifier($existingModifiers, $modifierToSet);
|
||||
return $existingModifiers | $modifierToSet;
|
||||
}
|
||||
}
|
239
vendor/nikic/php-parser/lib/PhpParser/Comment.php
vendored
Normal file
239
vendor/nikic/php-parser/lib/PhpParser/Comment.php
vendored
Normal file
|
@ -0,0 +1,239 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser;
|
||||
|
||||
class Comment implements \JsonSerializable
|
||||
{
|
||||
protected $text;
|
||||
protected $startLine;
|
||||
protected $startFilePos;
|
||||
protected $startTokenPos;
|
||||
protected $endLine;
|
||||
protected $endFilePos;
|
||||
protected $endTokenPos;
|
||||
|
||||
/**
|
||||
* Constructs a comment node.
|
||||
*
|
||||
* @param string $text Comment text (including comment delimiters like /*)
|
||||
* @param int $startLine Line number the comment started on
|
||||
* @param int $startFilePos File offset the comment started on
|
||||
* @param int $startTokenPos Token offset the comment started on
|
||||
*/
|
||||
public function __construct(
|
||||
string $text,
|
||||
int $startLine = -1, int $startFilePos = -1, int $startTokenPos = -1,
|
||||
int $endLine = -1, int $endFilePos = -1, int $endTokenPos = -1
|
||||
) {
|
||||
$this->text = $text;
|
||||
$this->startLine = $startLine;
|
||||
$this->startFilePos = $startFilePos;
|
||||
$this->startTokenPos = $startTokenPos;
|
||||
$this->endLine = $endLine;
|
||||
$this->endFilePos = $endFilePos;
|
||||
$this->endTokenPos = $endTokenPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the comment text.
|
||||
*
|
||||
* @return string The comment text (including comment delimiters like /*)
|
||||
*/
|
||||
public function getText() : string {
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line number the comment started on.
|
||||
*
|
||||
* @return int Line number (or -1 if not available)
|
||||
*/
|
||||
public function getStartLine() : int {
|
||||
return $this->startLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file offset the comment started on.
|
||||
*
|
||||
* @return int File offset (or -1 if not available)
|
||||
*/
|
||||
public function getStartFilePos() : int {
|
||||
return $this->startFilePos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token offset the comment started on.
|
||||
*
|
||||
* @return int Token offset (or -1 if not available)
|
||||
*/
|
||||
public function getStartTokenPos() : int {
|
||||
return $this->startTokenPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line number the comment ends on.
|
||||
*
|
||||
* @return int Line number (or -1 if not available)
|
||||
*/
|
||||
public function getEndLine() : int {
|
||||
return $this->endLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file offset the comment ends on.
|
||||
*
|
||||
* @return int File offset (or -1 if not available)
|
||||
*/
|
||||
public function getEndFilePos() : int {
|
||||
return $this->endFilePos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token offset the comment ends on.
|
||||
*
|
||||
* @return int Token offset (or -1 if not available)
|
||||
*/
|
||||
public function getEndTokenPos() : int {
|
||||
return $this->endTokenPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line number the comment started on.
|
||||
*
|
||||
* @deprecated Use getStartLine() instead
|
||||
*
|
||||
* @return int Line number
|
||||
*/
|
||||
public function getLine() : int {
|
||||
return $this->startLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the file offset the comment started on.
|
||||
*
|
||||
* @deprecated Use getStartFilePos() instead
|
||||
*
|
||||
* @return int File offset
|
||||
*/
|
||||
public function getFilePos() : int {
|
||||
return $this->startFilePos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the token offset the comment started on.
|
||||
*
|
||||
* @deprecated Use getStartTokenPos() instead
|
||||
*
|
||||
* @return int Token offset
|
||||
*/
|
||||
public function getTokenPos() : int {
|
||||
return $this->startTokenPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the comment text.
|
||||
*
|
||||
* @return string The comment text (including comment delimiters like /*)
|
||||
*/
|
||||
public function __toString() : string {
|
||||
return $this->text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the reformatted comment text.
|
||||
*
|
||||
* "Reformatted" here means that we try to clean up the whitespace at the
|
||||
* starts of the lines. This is necessary because we receive the comments
|
||||
* without trailing whitespace on the first line, but with trailing whitespace
|
||||
* on all subsequent lines.
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function getReformattedText() {
|
||||
$text = trim($this->text);
|
||||
$newlinePos = strpos($text, "\n");
|
||||
if (false === $newlinePos) {
|
||||
// Single line comments don't need further processing
|
||||
return $text;
|
||||
} elseif (preg_match('((*BSR_ANYCRLF)(*ANYCRLF)^.*(?:\R\s+\*.*)+$)', $text)) {
|
||||
// Multi line comment of the type
|
||||
//
|
||||
// /*
|
||||
// * Some text.
|
||||
// * Some more text.
|
||||
// */
|
||||
//
|
||||
// is handled by replacing the whitespace sequences before the * by a single space
|
||||
return preg_replace('(^\s+\*)m', ' *', $this->text);
|
||||
} elseif (preg_match('(^/\*\*?\s*[\r\n])', $text) && preg_match('(\n(\s*)\*/$)', $text, $matches)) {
|
||||
// Multi line comment of the type
|
||||
//
|
||||
// /*
|
||||
// Some text.
|
||||
// Some more text.
|
||||
// */
|
||||
//
|
||||
// is handled by removing the whitespace sequence on the line before the closing
|
||||
// */ on all lines. So if the last line is " */", then " " is removed at the
|
||||
// start of all lines.
|
||||
return preg_replace('(^' . preg_quote($matches[1]) . ')m', '', $text);
|
||||
} elseif (preg_match('(^/\*\*?\s*(?!\s))', $text, $matches)) {
|
||||
// Multi line comment of the type
|
||||
//
|
||||
// /* Some text.
|
||||
// Some more text.
|
||||
// Indented text.
|
||||
// Even more text. */
|
||||
//
|
||||
// is handled by removing the difference between the shortest whitespace prefix on all
|
||||
// lines and the length of the "/* " opening sequence.
|
||||
$prefixLen = $this->getShortestWhitespacePrefixLen(substr($text, $newlinePos + 1));
|
||||
$removeLen = $prefixLen - strlen($matches[0]);
|
||||
return preg_replace('(^\s{' . $removeLen . '})m', '', $text);
|
||||
}
|
||||
|
||||
// No idea how to format this comment, so simply return as is
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get length of shortest whitespace prefix (at the start of a line).
|
||||
*
|
||||
* If there is a line with no prefix whitespace, 0 is a valid return value.
|
||||
*
|
||||
* @param string $str String to check
|
||||
* @return int Length in characters. Tabs count as single characters.
|
||||
*/
|
||||
private function getShortestWhitespacePrefixLen(string $str) : int {
|
||||
$lines = explode("\n", $str);
|
||||
$shortestPrefixLen = \INF;
|
||||
foreach ($lines as $line) {
|
||||
preg_match('(^\s*)', $line, $matches);
|
||||
$prefixLen = strlen($matches[0]);
|
||||
if ($prefixLen < $shortestPrefixLen) {
|
||||
$shortestPrefixLen = $prefixLen;
|
||||
}
|
||||
}
|
||||
return $shortestPrefixLen;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @psalm-return array{nodeType:string, text:mixed, line:mixed, filePos:mixed}
|
||||
*/
|
||||
public function jsonSerialize() : array {
|
||||
// Technically not a node, but we make it look like one anyway
|
||||
$type = $this instanceof Comment\Doc ? 'Comment_Doc' : 'Comment';
|
||||
return [
|
||||
'nodeType' => $type,
|
||||
'text' => $this->text,
|
||||
// TODO: Rename these to include "start".
|
||||
'line' => $this->startLine,
|
||||
'filePos' => $this->startFilePos,
|
||||
'tokenPos' => $this->startTokenPos,
|
||||
'endLine' => $this->endLine,
|
||||
'endFilePos' => $this->endFilePos,
|
||||
'endTokenPos' => $this->endTokenPos,
|
||||
];
|
||||
}
|
||||
}
|
7
vendor/nikic/php-parser/lib/PhpParser/Comment/Doc.php
vendored
Normal file
7
vendor/nikic/php-parser/lib/PhpParser/Comment/Doc.php
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Comment;
|
||||
|
||||
class Doc extends \PhpParser\Comment
|
||||
{
|
||||
}
|
6
vendor/nikic/php-parser/lib/PhpParser/ConstExprEvaluationException.php
vendored
Normal file
6
vendor/nikic/php-parser/lib/PhpParser/ConstExprEvaluationException.php
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace PhpParser;
|
||||
|
||||
class ConstExprEvaluationException extends \Exception
|
||||
{}
|
229
vendor/nikic/php-parser/lib/PhpParser/ConstExprEvaluator.php
vendored
Normal file
229
vendor/nikic/php-parser/lib/PhpParser/ConstExprEvaluator.php
vendored
Normal file
|
@ -0,0 +1,229 @@
|
|||
<?php
|
||||
|
||||
namespace PhpParser;
|
||||
|
||||
use function array_merge;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Scalar;
|
||||
|
||||
/**
|
||||
* Evaluates constant expressions.
|
||||
*
|
||||
* This evaluator is able to evaluate all constant expressions (as defined by PHP), which can be
|
||||
* evaluated without further context. If a subexpression is not of this type, a user-provided
|
||||
* fallback evaluator is invoked. To support all constant expressions that are also supported by
|
||||
* PHP (and not already handled by this class), the fallback evaluator must be able to handle the
|
||||
* following node types:
|
||||
*
|
||||
* * All Scalar\MagicConst\* nodes.
|
||||
* * Expr\ConstFetch nodes. Only null/false/true are already handled by this class.
|
||||
* * Expr\ClassConstFetch nodes.
|
||||
*
|
||||
* The fallback evaluator should throw ConstExprEvaluationException for nodes it cannot evaluate.
|
||||
*
|
||||
* The evaluation is dependent on runtime configuration in two respects: Firstly, floating
|
||||
* point to string conversions are affected by the precision ini setting. Secondly, they are also
|
||||
* affected by the LC_NUMERIC locale.
|
||||
*/
|
||||
class ConstExprEvaluator
|
||||
{
|
||||
private $fallbackEvaluator;
|
||||
|
||||
/**
|
||||
* Create a constant expression evaluator.
|
||||
*
|
||||
* The provided fallback evaluator is invoked whenever a subexpression cannot be evaluated. See
|
||||
* class doc comment for more information.
|
||||
*
|
||||
* @param callable|null $fallbackEvaluator To call if subexpression cannot be evaluated
|
||||
*/
|
||||
public function __construct(callable $fallbackEvaluator = null) {
|
||||
$this->fallbackEvaluator = $fallbackEvaluator ?? function(Expr $expr) {
|
||||
throw new ConstExprEvaluationException(
|
||||
"Expression of type {$expr->getType()} cannot be evaluated"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Silently evaluates a constant expression into a PHP value.
|
||||
*
|
||||
* Thrown Errors, warnings or notices will be converted into a ConstExprEvaluationException.
|
||||
* The original source of the exception is available through getPrevious().
|
||||
*
|
||||
* If some part of the expression cannot be evaluated, the fallback evaluator passed to the
|
||||
* constructor will be invoked. By default, if no fallback is provided, an exception of type
|
||||
* ConstExprEvaluationException is thrown.
|
||||
*
|
||||
* See class doc comment for caveats and limitations.
|
||||
*
|
||||
* @param Expr $expr Constant expression to evaluate
|
||||
* @return mixed Result of evaluation
|
||||
*
|
||||
* @throws ConstExprEvaluationException if the expression cannot be evaluated or an error occurred
|
||||
*/
|
||||
public function evaluateSilently(Expr $expr) {
|
||||
set_error_handler(function($num, $str, $file, $line) {
|
||||
throw new \ErrorException($str, 0, $num, $file, $line);
|
||||
});
|
||||
|
||||
try {
|
||||
return $this->evaluate($expr);
|
||||
} catch (\Throwable $e) {
|
||||
if (!$e instanceof ConstExprEvaluationException) {
|
||||
$e = new ConstExprEvaluationException(
|
||||
"An error occurred during constant expression evaluation", 0, $e);
|
||||
}
|
||||
throw $e;
|
||||
} finally {
|
||||
restore_error_handler();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly evaluates a constant expression into a PHP value.
|
||||
*
|
||||
* May generate Error exceptions, warnings or notices. Use evaluateSilently() to convert these
|
||||
* into a ConstExprEvaluationException.
|
||||
*
|
||||
* If some part of the expression cannot be evaluated, the fallback evaluator passed to the
|
||||
* constructor will be invoked. By default, if no fallback is provided, an exception of type
|
||||
* ConstExprEvaluationException is thrown.
|
||||
*
|
||||
* See class doc comment for caveats and limitations.
|
||||
*
|
||||
* @param Expr $expr Constant expression to evaluate
|
||||
* @return mixed Result of evaluation
|
||||
*
|
||||
* @throws ConstExprEvaluationException if the expression cannot be evaluated
|
||||
*/
|
||||
public function evaluateDirectly(Expr $expr) {
|
||||
return $this->evaluate($expr);
|
||||
}
|
||||
|
||||
private function evaluate(Expr $expr) {
|
||||
if ($expr instanceof Scalar\LNumber
|
||||
|| $expr instanceof Scalar\DNumber
|
||||
|| $expr instanceof Scalar\String_
|
||||
) {
|
||||
return $expr->value;
|
||||
}
|
||||
|
||||
if ($expr instanceof Expr\Array_) {
|
||||
return $this->evaluateArray($expr);
|
||||
}
|
||||
|
||||
// Unary operators
|
||||
if ($expr instanceof Expr\UnaryPlus) {
|
||||
return +$this->evaluate($expr->expr);
|
||||
}
|
||||
if ($expr instanceof Expr\UnaryMinus) {
|
||||
return -$this->evaluate($expr->expr);
|
||||
}
|
||||
if ($expr instanceof Expr\BooleanNot) {
|
||||
return !$this->evaluate($expr->expr);
|
||||
}
|
||||
if ($expr instanceof Expr\BitwiseNot) {
|
||||
return ~$this->evaluate($expr->expr);
|
||||
}
|
||||
|
||||
if ($expr instanceof Expr\BinaryOp) {
|
||||
return $this->evaluateBinaryOp($expr);
|
||||
}
|
||||
|
||||
if ($expr instanceof Expr\Ternary) {
|
||||
return $this->evaluateTernary($expr);
|
||||
}
|
||||
|
||||
if ($expr instanceof Expr\ArrayDimFetch && null !== $expr->dim) {
|
||||
return $this->evaluate($expr->var)[$this->evaluate($expr->dim)];
|
||||
}
|
||||
|
||||
if ($expr instanceof Expr\ConstFetch) {
|
||||
return $this->evaluateConstFetch($expr);
|
||||
}
|
||||
|
||||
return ($this->fallbackEvaluator)($expr);
|
||||
}
|
||||
|
||||
private function evaluateArray(Expr\Array_ $expr) {
|
||||
$array = [];
|
||||
foreach ($expr->items as $item) {
|
||||
if (null !== $item->key) {
|
||||
$array[$this->evaluate($item->key)] = $this->evaluate($item->value);
|
||||
} elseif ($item->unpack) {
|
||||
$array = array_merge($array, $this->evaluate($item->value));
|
||||
} else {
|
||||
$array[] = $this->evaluate($item->value);
|
||||
}
|
||||
}
|
||||
return $array;
|
||||
}
|
||||
|
||||
private function evaluateTernary(Expr\Ternary $expr) {
|
||||
if (null === $expr->if) {
|
||||
return $this->evaluate($expr->cond) ?: $this->evaluate($expr->else);
|
||||
}
|
||||
|
||||
return $this->evaluate($expr->cond)
|
||||
? $this->evaluate($expr->if)
|
||||
: $this->evaluate($expr->else);
|
||||
}
|
||||
|
||||
private function evaluateBinaryOp(Expr\BinaryOp $expr) {
|
||||
if ($expr instanceof Expr\BinaryOp\Coalesce
|
||||
&& $expr->left instanceof Expr\ArrayDimFetch
|
||||
) {
|
||||
// This needs to be special cased to respect BP_VAR_IS fetch semantics
|
||||
return $this->evaluate($expr->left->var)[$this->evaluate($expr->left->dim)]
|
||||
?? $this->evaluate($expr->right);
|
||||
}
|
||||
|
||||
// The evaluate() calls are repeated in each branch, because some of the operators are
|
||||
// short-circuiting and evaluating the RHS in advance may be illegal in that case
|
||||
$l = $expr->left;
|
||||
$r = $expr->right;
|
||||
switch ($expr->getOperatorSigil()) {
|
||||
case '&': return $this->evaluate($l) & $this->evaluate($r);
|
||||
case '|': return $this->evaluate($l) | $this->evaluate($r);
|
||||
case '^': return $this->evaluate($l) ^ $this->evaluate($r);
|
||||
case '&&': return $this->evaluate($l) && $this->evaluate($r);
|
||||
case '||': return $this->evaluate($l) || $this->evaluate($r);
|
||||
case '??': return $this->evaluate($l) ?? $this->evaluate($r);
|
||||
case '.': return $this->evaluate($l) . $this->evaluate($r);
|
||||
case '/': return $this->evaluate($l) / $this->evaluate($r);
|
||||
case '==': return $this->evaluate($l) == $this->evaluate($r);
|
||||
case '>': return $this->evaluate($l) > $this->evaluate($r);
|
||||
case '>=': return $this->evaluate($l) >= $this->evaluate($r);
|
||||
case '===': return $this->evaluate($l) === $this->evaluate($r);
|
||||
case 'and': return $this->evaluate($l) and $this->evaluate($r);
|
||||
case 'or': return $this->evaluate($l) or $this->evaluate($r);
|
||||
case 'xor': return $this->evaluate($l) xor $this->evaluate($r);
|
||||
case '-': return $this->evaluate($l) - $this->evaluate($r);
|
||||
case '%': return $this->evaluate($l) % $this->evaluate($r);
|
||||
case '*': return $this->evaluate($l) * $this->evaluate($r);
|
||||
case '!=': return $this->evaluate($l) != $this->evaluate($r);
|
||||
case '!==': return $this->evaluate($l) !== $this->evaluate($r);
|
||||
case '+': return $this->evaluate($l) + $this->evaluate($r);
|
||||
case '**': return $this->evaluate($l) ** $this->evaluate($r);
|
||||
case '<<': return $this->evaluate($l) << $this->evaluate($r);
|
||||
case '>>': return $this->evaluate($l) >> $this->evaluate($r);
|
||||
case '<': return $this->evaluate($l) < $this->evaluate($r);
|
||||
case '<=': return $this->evaluate($l) <= $this->evaluate($r);
|
||||
case '<=>': return $this->evaluate($l) <=> $this->evaluate($r);
|
||||
}
|
||||
|
||||
throw new \Exception('Should not happen');
|
||||
}
|
||||
|
||||
private function evaluateConstFetch(Expr\ConstFetch $expr) {
|
||||
$name = $expr->name->toLowerString();
|
||||
switch ($name) {
|
||||
case 'null': return null;
|
||||
case 'false': return false;
|
||||
case 'true': return true;
|
||||
}
|
||||
|
||||
return ($this->fallbackEvaluator)($expr);
|
||||
}
|
||||
}
|
180
vendor/nikic/php-parser/lib/PhpParser/Error.php
vendored
Normal file
180
vendor/nikic/php-parser/lib/PhpParser/Error.php
vendored
Normal file
|
@ -0,0 +1,180 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser;
|
||||
|
||||
class Error extends \RuntimeException
|
||||
{
|
||||
protected $rawMessage;
|
||||
protected $attributes;
|
||||
|
||||
/**
|
||||
* Creates an Exception signifying a parse error.
|
||||
*
|
||||
* @param string $message Error message
|
||||
* @param array|int $attributes Attributes of node/token where error occurred
|
||||
* (or start line of error -- deprecated)
|
||||
*/
|
||||
public function __construct(string $message, $attributes = []) {
|
||||
$this->rawMessage = $message;
|
||||
if (is_array($attributes)) {
|
||||
$this->attributes = $attributes;
|
||||
} else {
|
||||
$this->attributes = ['startLine' => $attributes];
|
||||
}
|
||||
$this->updateMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the error message
|
||||
*
|
||||
* @return string Error message
|
||||
*/
|
||||
public function getRawMessage() : string {
|
||||
return $this->rawMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line the error starts in.
|
||||
*
|
||||
* @return int Error start line
|
||||
*/
|
||||
public function getStartLine() : int {
|
||||
return $this->attributes['startLine'] ?? -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line the error ends in.
|
||||
*
|
||||
* @return int Error end line
|
||||
*/
|
||||
public function getEndLine() : int {
|
||||
return $this->attributes['endLine'] ?? -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the attributes of the node/token the error occurred at.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAttributes() : array {
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the attributes of the node/token the error occurred at.
|
||||
*
|
||||
* @param array $attributes
|
||||
*/
|
||||
public function setAttributes(array $attributes) {
|
||||
$this->attributes = $attributes;
|
||||
$this->updateMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the line of the PHP file the error occurred in.
|
||||
*
|
||||
* @param string $message Error message
|
||||
*/
|
||||
public function setRawMessage(string $message) {
|
||||
$this->rawMessage = $message;
|
||||
$this->updateMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the line the error starts in.
|
||||
*
|
||||
* @param int $line Error start line
|
||||
*/
|
||||
public function setStartLine(int $line) {
|
||||
$this->attributes['startLine'] = $line;
|
||||
$this->updateMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the error has start and end column information.
|
||||
*
|
||||
* For column information enable the startFilePos and endFilePos in the lexer options.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasColumnInfo() : bool {
|
||||
return isset($this->attributes['startFilePos'], $this->attributes['endFilePos']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the start column (1-based) into the line where the error started.
|
||||
*
|
||||
* @param string $code Source code of the file
|
||||
* @return int
|
||||
*/
|
||||
public function getStartColumn(string $code) : int {
|
||||
if (!$this->hasColumnInfo()) {
|
||||
throw new \RuntimeException('Error does not have column information');
|
||||
}
|
||||
|
||||
return $this->toColumn($code, $this->attributes['startFilePos']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the end column (1-based) into the line where the error ended.
|
||||
*
|
||||
* @param string $code Source code of the file
|
||||
* @return int
|
||||
*/
|
||||
public function getEndColumn(string $code) : int {
|
||||
if (!$this->hasColumnInfo()) {
|
||||
throw new \RuntimeException('Error does not have column information');
|
||||
}
|
||||
|
||||
return $this->toColumn($code, $this->attributes['endFilePos']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats message including line and column information.
|
||||
*
|
||||
* @param string $code Source code associated with the error, for calculation of the columns
|
||||
*
|
||||
* @return string Formatted message
|
||||
*/
|
||||
public function getMessageWithColumnInfo(string $code) : string {
|
||||
return sprintf(
|
||||
'%s from %d:%d to %d:%d', $this->getRawMessage(),
|
||||
$this->getStartLine(), $this->getStartColumn($code),
|
||||
$this->getEndLine(), $this->getEndColumn($code)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a file offset into a column.
|
||||
*
|
||||
* @param string $code Source code that $pos indexes into
|
||||
* @param int $pos 0-based position in $code
|
||||
*
|
||||
* @return int 1-based column (relative to start of line)
|
||||
*/
|
||||
private function toColumn(string $code, int $pos) : int {
|
||||
if ($pos > strlen($code)) {
|
||||
throw new \RuntimeException('Invalid position information');
|
||||
}
|
||||
|
||||
$lineStartPos = strrpos($code, "\n", $pos - strlen($code));
|
||||
if (false === $lineStartPos) {
|
||||
$lineStartPos = -1;
|
||||
}
|
||||
|
||||
return $pos - $lineStartPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the exception message after a change to rawMessage or rawLine.
|
||||
*/
|
||||
protected function updateMessage() {
|
||||
$this->message = $this->rawMessage;
|
||||
|
||||
if (-1 === $this->getStartLine()) {
|
||||
$this->message .= ' on unknown line';
|
||||
} else {
|
||||
$this->message .= ' on line ' . $this->getStartLine();
|
||||
}
|
||||
}
|
||||
}
|
13
vendor/nikic/php-parser/lib/PhpParser/ErrorHandler.php
vendored
Normal file
13
vendor/nikic/php-parser/lib/PhpParser/ErrorHandler.php
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser;
|
||||
|
||||
interface ErrorHandler
|
||||
{
|
||||
/**
|
||||
* Handle an error generated during lexing, parsing or some other operation.
|
||||
*
|
||||
* @param Error $error The error that needs to be handled
|
||||
*/
|
||||
public function handleError(Error $error);
|
||||
}
|
46
vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Collecting.php
vendored
Normal file
46
vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Collecting.php
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\ErrorHandler;
|
||||
|
||||
use PhpParser\Error;
|
||||
use PhpParser\ErrorHandler;
|
||||
|
||||
/**
|
||||
* Error handler that collects all errors into an array.
|
||||
*
|
||||
* This allows graceful handling of errors.
|
||||
*/
|
||||
class Collecting implements ErrorHandler
|
||||
{
|
||||
/** @var Error[] Collected errors */
|
||||
private $errors = [];
|
||||
|
||||
public function handleError(Error $error) {
|
||||
$this->errors[] = $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get collected errors.
|
||||
*
|
||||
* @return Error[]
|
||||
*/
|
||||
public function getErrors() : array {
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether there are any errors.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasErrors() : bool {
|
||||
return !empty($this->errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset/clear collected errors.
|
||||
*/
|
||||
public function clearErrors() {
|
||||
$this->errors = [];
|
||||
}
|
||||
}
|
18
vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Throwing.php
vendored
Normal file
18
vendor/nikic/php-parser/lib/PhpParser/ErrorHandler/Throwing.php
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\ErrorHandler;
|
||||
|
||||
use PhpParser\Error;
|
||||
use PhpParser\ErrorHandler;
|
||||
|
||||
/**
|
||||
* Error handler that handles all errors by throwing them.
|
||||
*
|
||||
* This is the default strategy used by all components.
|
||||
*/
|
||||
class Throwing implements ErrorHandler
|
||||
{
|
||||
public function handleError(Error $error) {
|
||||
throw $error;
|
||||
}
|
||||
}
|
27
vendor/nikic/php-parser/lib/PhpParser/Internal/DiffElem.php
vendored
Normal file
27
vendor/nikic/php-parser/lib/PhpParser/Internal/DiffElem.php
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Internal;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class DiffElem
|
||||
{
|
||||
const TYPE_KEEP = 0;
|
||||
const TYPE_REMOVE = 1;
|
||||
const TYPE_ADD = 2;
|
||||
const TYPE_REPLACE = 3;
|
||||
|
||||
/** @var int One of the TYPE_* constants */
|
||||
public $type;
|
||||
/** @var mixed Is null for add operations */
|
||||
public $old;
|
||||
/** @var mixed Is null for remove operations */
|
||||
public $new;
|
||||
|
||||
public function __construct(int $type, $old, $new) {
|
||||
$this->type = $type;
|
||||
$this->old = $old;
|
||||
$this->new = $new;
|
||||
}
|
||||
}
|
164
vendor/nikic/php-parser/lib/PhpParser/Internal/Differ.php
vendored
Normal file
164
vendor/nikic/php-parser/lib/PhpParser/Internal/Differ.php
vendored
Normal file
|
@ -0,0 +1,164 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Internal;
|
||||
|
||||
/**
|
||||
* Implements the Myers diff algorithm.
|
||||
*
|
||||
* Myers, Eugene W. "An O (ND) difference algorithm and its variations."
|
||||
* Algorithmica 1.1 (1986): 251-266.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class Differ
|
||||
{
|
||||
private $isEqual;
|
||||
|
||||
/**
|
||||
* Create differ over the given equality relation.
|
||||
*
|
||||
* @param callable $isEqual Equality relation with signature function($a, $b) : bool
|
||||
*/
|
||||
public function __construct(callable $isEqual) {
|
||||
$this->isEqual = $isEqual;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate diff (edit script) from $old to $new.
|
||||
*
|
||||
* @param array $old Original array
|
||||
* @param array $new New array
|
||||
*
|
||||
* @return DiffElem[] Diff (edit script)
|
||||
*/
|
||||
public function diff(array $old, array $new) {
|
||||
list($trace, $x, $y) = $this->calculateTrace($old, $new);
|
||||
return $this->extractDiff($trace, $x, $y, $old, $new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate diff, including "replace" operations.
|
||||
*
|
||||
* If a sequence of remove operations is followed by the same number of add operations, these
|
||||
* will be coalesced into replace operations.
|
||||
*
|
||||
* @param array $old Original array
|
||||
* @param array $new New array
|
||||
*
|
||||
* @return DiffElem[] Diff (edit script), including replace operations
|
||||
*/
|
||||
public function diffWithReplacements(array $old, array $new) {
|
||||
return $this->coalesceReplacements($this->diff($old, $new));
|
||||
}
|
||||
|
||||
private function calculateTrace(array $a, array $b) {
|
||||
$n = \count($a);
|
||||
$m = \count($b);
|
||||
$max = $n + $m;
|
||||
$v = [1 => 0];
|
||||
$trace = [];
|
||||
for ($d = 0; $d <= $max; $d++) {
|
||||
$trace[] = $v;
|
||||
for ($k = -$d; $k <= $d; $k += 2) {
|
||||
if ($k === -$d || ($k !== $d && $v[$k-1] < $v[$k+1])) {
|
||||
$x = $v[$k+1];
|
||||
} else {
|
||||
$x = $v[$k-1] + 1;
|
||||
}
|
||||
|
||||
$y = $x - $k;
|
||||
while ($x < $n && $y < $m && ($this->isEqual)($a[$x], $b[$y])) {
|
||||
$x++;
|
||||
$y++;
|
||||
}
|
||||
|
||||
$v[$k] = $x;
|
||||
if ($x >= $n && $y >= $m) {
|
||||
return [$trace, $x, $y];
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new \Exception('Should not happen');
|
||||
}
|
||||
|
||||
private function extractDiff(array $trace, int $x, int $y, array $a, array $b) {
|
||||
$result = [];
|
||||
for ($d = \count($trace) - 1; $d >= 0; $d--) {
|
||||
$v = $trace[$d];
|
||||
$k = $x - $y;
|
||||
|
||||
if ($k === -$d || ($k !== $d && $v[$k-1] < $v[$k+1])) {
|
||||
$prevK = $k + 1;
|
||||
} else {
|
||||
$prevK = $k - 1;
|
||||
}
|
||||
|
||||
$prevX = $v[$prevK];
|
||||
$prevY = $prevX - $prevK;
|
||||
|
||||
while ($x > $prevX && $y > $prevY) {
|
||||
$result[] = new DiffElem(DiffElem::TYPE_KEEP, $a[$x-1], $b[$y-1]);
|
||||
$x--;
|
||||
$y--;
|
||||
}
|
||||
|
||||
if ($d === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
while ($x > $prevX) {
|
||||
$result[] = new DiffElem(DiffElem::TYPE_REMOVE, $a[$x-1], null);
|
||||
$x--;
|
||||
}
|
||||
|
||||
while ($y > $prevY) {
|
||||
$result[] = new DiffElem(DiffElem::TYPE_ADD, null, $b[$y-1]);
|
||||
$y--;
|
||||
}
|
||||
}
|
||||
return array_reverse($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Coalesce equal-length sequences of remove+add into a replace operation.
|
||||
*
|
||||
* @param DiffElem[] $diff
|
||||
* @return DiffElem[]
|
||||
*/
|
||||
private function coalesceReplacements(array $diff) {
|
||||
$newDiff = [];
|
||||
$c = \count($diff);
|
||||
for ($i = 0; $i < $c; $i++) {
|
||||
$diffType = $diff[$i]->type;
|
||||
if ($diffType !== DiffElem::TYPE_REMOVE) {
|
||||
$newDiff[] = $diff[$i];
|
||||
continue;
|
||||
}
|
||||
|
||||
$j = $i;
|
||||
while ($j < $c && $diff[$j]->type === DiffElem::TYPE_REMOVE) {
|
||||
$j++;
|
||||
}
|
||||
|
||||
$k = $j;
|
||||
while ($k < $c && $diff[$k]->type === DiffElem::TYPE_ADD) {
|
||||
$k++;
|
||||
}
|
||||
|
||||
if ($j - $i === $k - $j) {
|
||||
$len = $j - $i;
|
||||
for ($n = 0; $n < $len; $n++) {
|
||||
$newDiff[] = new DiffElem(
|
||||
DiffElem::TYPE_REPLACE, $diff[$i + $n]->old, $diff[$j + $n]->new
|
||||
);
|
||||
}
|
||||
} else {
|
||||
for (; $i < $k; $i++) {
|
||||
$newDiff[] = $diff[$i];
|
||||
}
|
||||
}
|
||||
$i = $k - 1;
|
||||
}
|
||||
return $newDiff;
|
||||
}
|
||||
}
|
64
vendor/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php
vendored
Normal file
64
vendor/nikic/php-parser/lib/PhpParser/Internal/PrintableNewAnonClassNode.php
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Internal;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr;
|
||||
|
||||
/**
|
||||
* This node is used internally by the format-preserving pretty printer to print anonymous classes.
|
||||
*
|
||||
* The normal anonymous class structure violates assumptions about the order of token offsets.
|
||||
* Namely, the constructor arguments are part of the Expr\New_ node and follow the class node, even
|
||||
* though they are actually interleaved with them. This special node type is used temporarily to
|
||||
* restore a sane token offset order.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class PrintableNewAnonClassNode extends Expr
|
||||
{
|
||||
/** @var Node\AttributeGroup[] PHP attribute groups */
|
||||
public $attrGroups;
|
||||
/** @var int Modifiers */
|
||||
public $flags;
|
||||
/** @var Node\Arg[] Arguments */
|
||||
public $args;
|
||||
/** @var null|Node\Name Name of extended class */
|
||||
public $extends;
|
||||
/** @var Node\Name[] Names of implemented interfaces */
|
||||
public $implements;
|
||||
/** @var Node\Stmt[] Statements */
|
||||
public $stmts;
|
||||
|
||||
public function __construct(
|
||||
array $attrGroups, int $flags, array $args, Node\Name $extends = null, array $implements,
|
||||
array $stmts, array $attributes
|
||||
) {
|
||||
parent::__construct($attributes);
|
||||
$this->attrGroups = $attrGroups;
|
||||
$this->flags = $flags;
|
||||
$this->args = $args;
|
||||
$this->extends = $extends;
|
||||
$this->implements = $implements;
|
||||
$this->stmts = $stmts;
|
||||
}
|
||||
|
||||
public static function fromNewNode(Expr\New_ $newNode) {
|
||||
$class = $newNode->class;
|
||||
assert($class instanceof Node\Stmt\Class_);
|
||||
// We don't assert that $class->name is null here, to allow consumers to assign unique names
|
||||
// to anonymous classes for their own purposes. We simplify ignore the name here.
|
||||
return new self(
|
||||
$class->attrGroups, $class->flags, $newNode->args, $class->extends, $class->implements,
|
||||
$class->stmts, $newNode->getAttributes()
|
||||
);
|
||||
}
|
||||
|
||||
public function getType() : string {
|
||||
return 'Expr_PrintableNewAnonClass';
|
||||
}
|
||||
|
||||
public function getSubNodeNames() : array {
|
||||
return ['attrGroups', 'flags', 'args', 'extends', 'implements', 'stmts'];
|
||||
}
|
||||
}
|
286
vendor/nikic/php-parser/lib/PhpParser/Internal/TokenStream.php
vendored
Normal file
286
vendor/nikic/php-parser/lib/PhpParser/Internal/TokenStream.php
vendored
Normal file
|
@ -0,0 +1,286 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Internal;
|
||||
|
||||
/**
|
||||
* Provides operations on token streams, for use by pretty printer.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class TokenStream
|
||||
{
|
||||
/** @var array Tokens (in token_get_all format) */
|
||||
private $tokens;
|
||||
/** @var int[] Map from position to indentation */
|
||||
private $indentMap;
|
||||
|
||||
/**
|
||||
* Create token stream instance.
|
||||
*
|
||||
* @param array $tokens Tokens in token_get_all() format
|
||||
*/
|
||||
public function __construct(array $tokens) {
|
||||
$this->tokens = $tokens;
|
||||
$this->indentMap = $this->calcIndentMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given position is immediately surrounded by parenthesis.
|
||||
*
|
||||
* @param int $startPos Start position
|
||||
* @param int $endPos End position
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function haveParens(int $startPos, int $endPos) : bool {
|
||||
return $this->haveTokenImmediatelyBefore($startPos, '(')
|
||||
&& $this->haveTokenImmediatelyAfter($endPos, ')');
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given position is immediately surrounded by braces.
|
||||
*
|
||||
* @param int $startPos Start position
|
||||
* @param int $endPos End position
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function haveBraces(int $startPos, int $endPos) : bool {
|
||||
return ($this->haveTokenImmediatelyBefore($startPos, '{')
|
||||
|| $this->haveTokenImmediatelyBefore($startPos, T_CURLY_OPEN))
|
||||
&& $this->haveTokenImmediatelyAfter($endPos, '}');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the position is directly preceded by a certain token type.
|
||||
*
|
||||
* During this check whitespace and comments are skipped.
|
||||
*
|
||||
* @param int $pos Position before which the token should occur
|
||||
* @param int|string $expectedTokenType Token to check for
|
||||
*
|
||||
* @return bool Whether the expected token was found
|
||||
*/
|
||||
public function haveTokenImmediatelyBefore(int $pos, $expectedTokenType) : bool {
|
||||
$tokens = $this->tokens;
|
||||
$pos--;
|
||||
for (; $pos >= 0; $pos--) {
|
||||
$tokenType = $tokens[$pos][0];
|
||||
if ($tokenType === $expectedTokenType) {
|
||||
return true;
|
||||
}
|
||||
if ($tokenType !== \T_WHITESPACE
|
||||
&& $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the position is directly followed by a certain token type.
|
||||
*
|
||||
* During this check whitespace and comments are skipped.
|
||||
*
|
||||
* @param int $pos Position after which the token should occur
|
||||
* @param int|string $expectedTokenType Token to check for
|
||||
*
|
||||
* @return bool Whether the expected token was found
|
||||
*/
|
||||
public function haveTokenImmediatelyAfter(int $pos, $expectedTokenType) : bool {
|
||||
$tokens = $this->tokens;
|
||||
$pos++;
|
||||
for (; $pos < \count($tokens); $pos++) {
|
||||
$tokenType = $tokens[$pos][0];
|
||||
if ($tokenType === $expectedTokenType) {
|
||||
return true;
|
||||
}
|
||||
if ($tokenType !== \T_WHITESPACE
|
||||
&& $tokenType !== \T_COMMENT && $tokenType !== \T_DOC_COMMENT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function skipLeft(int $pos, $skipTokenType) {
|
||||
$tokens = $this->tokens;
|
||||
|
||||
$pos = $this->skipLeftWhitespace($pos);
|
||||
if ($skipTokenType === \T_WHITESPACE) {
|
||||
return $pos;
|
||||
}
|
||||
|
||||
if ($tokens[$pos][0] !== $skipTokenType) {
|
||||
// Shouldn't happen. The skip token MUST be there
|
||||
throw new \Exception('Encountered unexpected token');
|
||||
}
|
||||
$pos--;
|
||||
|
||||
return $this->skipLeftWhitespace($pos);
|
||||
}
|
||||
|
||||
public function skipRight(int $pos, $skipTokenType) {
|
||||
$tokens = $this->tokens;
|
||||
|
||||
$pos = $this->skipRightWhitespace($pos);
|
||||
if ($skipTokenType === \T_WHITESPACE) {
|
||||
return $pos;
|
||||
}
|
||||
|
||||
if ($tokens[$pos][0] !== $skipTokenType) {
|
||||
// Shouldn't happen. The skip token MUST be there
|
||||
throw new \Exception('Encountered unexpected token');
|
||||
}
|
||||
$pos++;
|
||||
|
||||
return $this->skipRightWhitespace($pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return first non-whitespace token position smaller or equal to passed position.
|
||||
*
|
||||
* @param int $pos Token position
|
||||
* @return int Non-whitespace token position
|
||||
*/
|
||||
public function skipLeftWhitespace(int $pos) {
|
||||
$tokens = $this->tokens;
|
||||
for (; $pos >= 0; $pos--) {
|
||||
$type = $tokens[$pos][0];
|
||||
if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return first non-whitespace position greater or equal to passed position.
|
||||
*
|
||||
* @param int $pos Token position
|
||||
* @return int Non-whitespace token position
|
||||
*/
|
||||
public function skipRightWhitespace(int $pos) {
|
||||
$tokens = $this->tokens;
|
||||
for ($count = \count($tokens); $pos < $count; $pos++) {
|
||||
$type = $tokens[$pos][0];
|
||||
if ($type !== \T_WHITESPACE && $type !== \T_COMMENT && $type !== \T_DOC_COMMENT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $pos;
|
||||
}
|
||||
|
||||
public function findRight(int $pos, $findTokenType) {
|
||||
$tokens = $this->tokens;
|
||||
for ($count = \count($tokens); $pos < $count; $pos++) {
|
||||
$type = $tokens[$pos][0];
|
||||
if ($type === $findTokenType) {
|
||||
return $pos;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the given position range contains a certain token type.
|
||||
*
|
||||
* @param int $startPos Starting position (inclusive)
|
||||
* @param int $endPos Ending position (exclusive)
|
||||
* @param int|string $tokenType Token type to look for
|
||||
* @return bool Whether the token occurs in the given range
|
||||
*/
|
||||
public function haveTokenInRange(int $startPos, int $endPos, $tokenType) {
|
||||
$tokens = $this->tokens;
|
||||
for ($pos = $startPos; $pos < $endPos; $pos++) {
|
||||
if ($tokens[$pos][0] === $tokenType) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function haveBracesInRange(int $startPos, int $endPos) {
|
||||
return $this->haveTokenInRange($startPos, $endPos, '{')
|
||||
|| $this->haveTokenInRange($startPos, $endPos, T_CURLY_OPEN)
|
||||
|| $this->haveTokenInRange($startPos, $endPos, '}');
|
||||
}
|
||||
|
||||
public function haveTagInRange(int $startPos, int $endPos): bool {
|
||||
return $this->haveTokenInRange($startPos, $endPos, \T_OPEN_TAG)
|
||||
|| $this->haveTokenInRange($startPos, $endPos, \T_CLOSE_TAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get indentation before token position.
|
||||
*
|
||||
* @param int $pos Token position
|
||||
*
|
||||
* @return int Indentation depth (in spaces)
|
||||
*/
|
||||
public function getIndentationBefore(int $pos) : int {
|
||||
return $this->indentMap[$pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the code corresponding to a token offset range, optionally adjusted for indentation.
|
||||
*
|
||||
* @param int $from Token start position (inclusive)
|
||||
* @param int $to Token end position (exclusive)
|
||||
* @param int $indent By how much the code should be indented (can be negative as well)
|
||||
*
|
||||
* @return string Code corresponding to token range, adjusted for indentation
|
||||
*/
|
||||
public function getTokenCode(int $from, int $to, int $indent) : string {
|
||||
$tokens = $this->tokens;
|
||||
$result = '';
|
||||
for ($pos = $from; $pos < $to; $pos++) {
|
||||
$token = $tokens[$pos];
|
||||
if (\is_array($token)) {
|
||||
$type = $token[0];
|
||||
$content = $token[1];
|
||||
if ($type === \T_CONSTANT_ENCAPSED_STRING || $type === \T_ENCAPSED_AND_WHITESPACE) {
|
||||
$result .= $content;
|
||||
} else {
|
||||
// TODO Handle non-space indentation
|
||||
if ($indent < 0) {
|
||||
$result .= str_replace("\n" . str_repeat(" ", -$indent), "\n", $content);
|
||||
} elseif ($indent > 0) {
|
||||
$result .= str_replace("\n", "\n" . str_repeat(" ", $indent), $content);
|
||||
} else {
|
||||
$result .= $content;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$result .= $token;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Precalculate the indentation at every token position.
|
||||
*
|
||||
* @return int[] Token position to indentation map
|
||||
*/
|
||||
private function calcIndentMap() {
|
||||
$indentMap = [];
|
||||
$indent = 0;
|
||||
foreach ($this->tokens as $token) {
|
||||
$indentMap[] = $indent;
|
||||
|
||||
if ($token[0] === \T_WHITESPACE) {
|
||||
$content = $token[1];
|
||||
$newlinePos = \strrpos($content, "\n");
|
||||
if (false !== $newlinePos) {
|
||||
$indent = \strlen($content) - $newlinePos - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a sentinel for one past end of the file
|
||||
$indentMap[] = $indent;
|
||||
|
||||
return $indentMap;
|
||||
}
|
||||
}
|
103
vendor/nikic/php-parser/lib/PhpParser/JsonDecoder.php
vendored
Normal file
103
vendor/nikic/php-parser/lib/PhpParser/JsonDecoder.php
vendored
Normal file
|
@ -0,0 +1,103 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser;
|
||||
|
||||
class JsonDecoder
|
||||
{
|
||||
/** @var \ReflectionClass[] Node type to reflection class map */
|
||||
private $reflectionClassCache;
|
||||
|
||||
public function decode(string $json) {
|
||||
$value = json_decode($json, true);
|
||||
if (json_last_error()) {
|
||||
throw new \RuntimeException('JSON decoding error: ' . json_last_error_msg());
|
||||
}
|
||||
|
||||
return $this->decodeRecursive($value);
|
||||
}
|
||||
|
||||
private function decodeRecursive($value) {
|
||||
if (\is_array($value)) {
|
||||
if (isset($value['nodeType'])) {
|
||||
if ($value['nodeType'] === 'Comment' || $value['nodeType'] === 'Comment_Doc') {
|
||||
return $this->decodeComment($value);
|
||||
}
|
||||
return $this->decodeNode($value);
|
||||
}
|
||||
return $this->decodeArray($value);
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function decodeArray(array $array) : array {
|
||||
$decodedArray = [];
|
||||
foreach ($array as $key => $value) {
|
||||
$decodedArray[$key] = $this->decodeRecursive($value);
|
||||
}
|
||||
return $decodedArray;
|
||||
}
|
||||
|
||||
private function decodeNode(array $value) : Node {
|
||||
$nodeType = $value['nodeType'];
|
||||
if (!\is_string($nodeType)) {
|
||||
throw new \RuntimeException('Node type must be a string');
|
||||
}
|
||||
|
||||
$reflectionClass = $this->reflectionClassFromNodeType($nodeType);
|
||||
/** @var Node $node */
|
||||
$node = $reflectionClass->newInstanceWithoutConstructor();
|
||||
|
||||
if (isset($value['attributes'])) {
|
||||
if (!\is_array($value['attributes'])) {
|
||||
throw new \RuntimeException('Attributes must be an array');
|
||||
}
|
||||
|
||||
$node->setAttributes($this->decodeArray($value['attributes']));
|
||||
}
|
||||
|
||||
foreach ($value as $name => $subNode) {
|
||||
if ($name === 'nodeType' || $name === 'attributes') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$node->$name = $this->decodeRecursive($subNode);
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function decodeComment(array $value) : Comment {
|
||||
$className = $value['nodeType'] === 'Comment' ? Comment::class : Comment\Doc::class;
|
||||
if (!isset($value['text'])) {
|
||||
throw new \RuntimeException('Comment must have text');
|
||||
}
|
||||
|
||||
return new $className(
|
||||
$value['text'],
|
||||
$value['line'] ?? -1, $value['filePos'] ?? -1, $value['tokenPos'] ?? -1,
|
||||
$value['endLine'] ?? -1, $value['endFilePos'] ?? -1, $value['endTokenPos'] ?? -1
|
||||
);
|
||||
}
|
||||
|
||||
private function reflectionClassFromNodeType(string $nodeType) : \ReflectionClass {
|
||||
if (!isset($this->reflectionClassCache[$nodeType])) {
|
||||
$className = $this->classNameFromNodeType($nodeType);
|
||||
$this->reflectionClassCache[$nodeType] = new \ReflectionClass($className);
|
||||
}
|
||||
return $this->reflectionClassCache[$nodeType];
|
||||
}
|
||||
|
||||
private function classNameFromNodeType(string $nodeType) : string {
|
||||
$className = 'PhpParser\\Node\\' . strtr($nodeType, '_', '\\');
|
||||
if (class_exists($className)) {
|
||||
return $className;
|
||||
}
|
||||
|
||||
$className .= '_';
|
||||
if (class_exists($className)) {
|
||||
return $className;
|
||||
}
|
||||
|
||||
throw new \RuntimeException("Unknown node type \"$nodeType\"");
|
||||
}
|
||||
}
|
560
vendor/nikic/php-parser/lib/PhpParser/Lexer.php
vendored
Normal file
560
vendor/nikic/php-parser/lib/PhpParser/Lexer.php
vendored
Normal file
|
@ -0,0 +1,560 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser;
|
||||
|
||||
use PhpParser\Parser\Tokens;
|
||||
|
||||
class Lexer
|
||||
{
|
||||
protected $code;
|
||||
protected $tokens;
|
||||
protected $pos;
|
||||
protected $line;
|
||||
protected $filePos;
|
||||
protected $prevCloseTagHasNewline;
|
||||
|
||||
protected $tokenMap;
|
||||
protected $dropTokens;
|
||||
protected $identifierTokens;
|
||||
|
||||
private $attributeStartLineUsed;
|
||||
private $attributeEndLineUsed;
|
||||
private $attributeStartTokenPosUsed;
|
||||
private $attributeEndTokenPosUsed;
|
||||
private $attributeStartFilePosUsed;
|
||||
private $attributeEndFilePosUsed;
|
||||
private $attributeCommentsUsed;
|
||||
|
||||
/**
|
||||
* Creates a Lexer.
|
||||
*
|
||||
* @param array $options Options array. Currently only the 'usedAttributes' option is supported,
|
||||
* which is an array of attributes to add to the AST nodes. Possible
|
||||
* attributes are: 'comments', 'startLine', 'endLine', 'startTokenPos',
|
||||
* 'endTokenPos', 'startFilePos', 'endFilePos'. The option defaults to the
|
||||
* first three. For more info see getNextToken() docs.
|
||||
*/
|
||||
public function __construct(array $options = []) {
|
||||
// Create Map from internal tokens to PhpParser tokens.
|
||||
$this->defineCompatibilityTokens();
|
||||
$this->tokenMap = $this->createTokenMap();
|
||||
$this->identifierTokens = $this->createIdentifierTokenMap();
|
||||
|
||||
// map of tokens to drop while lexing (the map is only used for isset lookup,
|
||||
// that's why the value is simply set to 1; the value is never actually used.)
|
||||
$this->dropTokens = array_fill_keys(
|
||||
[\T_WHITESPACE, \T_OPEN_TAG, \T_COMMENT, \T_DOC_COMMENT, \T_BAD_CHARACTER], 1
|
||||
);
|
||||
|
||||
$defaultAttributes = ['comments', 'startLine', 'endLine'];
|
||||
$usedAttributes = array_fill_keys($options['usedAttributes'] ?? $defaultAttributes, true);
|
||||
|
||||
// Create individual boolean properties to make these checks faster.
|
||||
$this->attributeStartLineUsed = isset($usedAttributes['startLine']);
|
||||
$this->attributeEndLineUsed = isset($usedAttributes['endLine']);
|
||||
$this->attributeStartTokenPosUsed = isset($usedAttributes['startTokenPos']);
|
||||
$this->attributeEndTokenPosUsed = isset($usedAttributes['endTokenPos']);
|
||||
$this->attributeStartFilePosUsed = isset($usedAttributes['startFilePos']);
|
||||
$this->attributeEndFilePosUsed = isset($usedAttributes['endFilePos']);
|
||||
$this->attributeCommentsUsed = isset($usedAttributes['comments']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the lexer for lexing the provided source code.
|
||||
*
|
||||
* This function does not throw if lexing errors occur. Instead, errors may be retrieved using
|
||||
* the getErrors() method.
|
||||
*
|
||||
* @param string $code The source code to lex
|
||||
* @param ErrorHandler|null $errorHandler Error handler to use for lexing errors. Defaults to
|
||||
* ErrorHandler\Throwing
|
||||
*/
|
||||
public function startLexing(string $code, ErrorHandler $errorHandler = null) {
|
||||
if (null === $errorHandler) {
|
||||
$errorHandler = new ErrorHandler\Throwing();
|
||||
}
|
||||
|
||||
$this->code = $code; // keep the code around for __halt_compiler() handling
|
||||
$this->pos = -1;
|
||||
$this->line = 1;
|
||||
$this->filePos = 0;
|
||||
|
||||
// If inline HTML occurs without preceding code, treat it as if it had a leading newline.
|
||||
// This ensures proper composability, because having a newline is the "safe" assumption.
|
||||
$this->prevCloseTagHasNewline = true;
|
||||
|
||||
$scream = ini_set('xdebug.scream', '0');
|
||||
|
||||
$this->tokens = @token_get_all($code);
|
||||
$this->postprocessTokens($errorHandler);
|
||||
|
||||
if (false !== $scream) {
|
||||
ini_set('xdebug.scream', $scream);
|
||||
}
|
||||
}
|
||||
|
||||
private function handleInvalidCharacterRange($start, $end, $line, ErrorHandler $errorHandler) {
|
||||
$tokens = [];
|
||||
for ($i = $start; $i < $end; $i++) {
|
||||
$chr = $this->code[$i];
|
||||
if ($chr === "\0") {
|
||||
// PHP cuts error message after null byte, so need special case
|
||||
$errorMsg = 'Unexpected null byte';
|
||||
} else {
|
||||
$errorMsg = sprintf(
|
||||
'Unexpected character "%s" (ASCII %d)', $chr, ord($chr)
|
||||
);
|
||||
}
|
||||
|
||||
$tokens[] = [\T_BAD_CHARACTER, $chr, $line];
|
||||
$errorHandler->handleError(new Error($errorMsg, [
|
||||
'startLine' => $line,
|
||||
'endLine' => $line,
|
||||
'startFilePos' => $i,
|
||||
'endFilePos' => $i,
|
||||
]));
|
||||
}
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether comment token is unterminated.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isUnterminatedComment($token) : bool {
|
||||
return ($token[0] === \T_COMMENT || $token[0] === \T_DOC_COMMENT)
|
||||
&& substr($token[1], 0, 2) === '/*'
|
||||
&& substr($token[1], -2) !== '*/';
|
||||
}
|
||||
|
||||
protected function postprocessTokens(ErrorHandler $errorHandler) {
|
||||
// PHP's error handling for token_get_all() is rather bad, so if we want detailed
|
||||
// error information we need to compute it ourselves. Invalid character errors are
|
||||
// detected by finding "gaps" in the token array. Unterminated comments are detected
|
||||
// by checking if a trailing comment has a "*/" at the end.
|
||||
//
|
||||
// Additionally, we perform a number of canonicalizations here:
|
||||
// * Use the PHP 8.0 comment format, which does not include trailing whitespace anymore.
|
||||
// * Use PHP 8.0 T_NAME_* tokens.
|
||||
// * Use PHP 8.1 T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG and
|
||||
// T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG tokens used to disambiguate intersection types.
|
||||
|
||||
$filePos = 0;
|
||||
$line = 1;
|
||||
$numTokens = \count($this->tokens);
|
||||
for ($i = 0; $i < $numTokens; $i++) {
|
||||
$token = $this->tokens[$i];
|
||||
|
||||
// Since PHP 7.4 invalid characters are represented by a T_BAD_CHARACTER token.
|
||||
// In this case we only need to emit an error.
|
||||
if ($token[0] === \T_BAD_CHARACTER) {
|
||||
$this->handleInvalidCharacterRange($filePos, $filePos + 1, $line, $errorHandler);
|
||||
}
|
||||
|
||||
if ($token[0] === \T_COMMENT && substr($token[1], 0, 2) !== '/*'
|
||||
&& preg_match('/(\r\n|\n|\r)$/D', $token[1], $matches)) {
|
||||
$trailingNewline = $matches[0];
|
||||
$token[1] = substr($token[1], 0, -strlen($trailingNewline));
|
||||
$this->tokens[$i] = $token;
|
||||
if (isset($this->tokens[$i + 1]) && $this->tokens[$i + 1][0] === \T_WHITESPACE) {
|
||||
// Move trailing newline into following T_WHITESPACE token, if it already exists.
|
||||
$this->tokens[$i + 1][1] = $trailingNewline . $this->tokens[$i + 1][1];
|
||||
$this->tokens[$i + 1][2]--;
|
||||
} else {
|
||||
// Otherwise, we need to create a new T_WHITESPACE token.
|
||||
array_splice($this->tokens, $i + 1, 0, [
|
||||
[\T_WHITESPACE, $trailingNewline, $line],
|
||||
]);
|
||||
$numTokens++;
|
||||
}
|
||||
}
|
||||
|
||||
// Emulate PHP 8 T_NAME_* tokens, by combining sequences of T_NS_SEPARATOR and T_STRING
|
||||
// into a single token.
|
||||
if (\is_array($token)
|
||||
&& ($token[0] === \T_NS_SEPARATOR || isset($this->identifierTokens[$token[0]]))) {
|
||||
$lastWasSeparator = $token[0] === \T_NS_SEPARATOR;
|
||||
$text = $token[1];
|
||||
for ($j = $i + 1; isset($this->tokens[$j]); $j++) {
|
||||
if ($lastWasSeparator) {
|
||||
if (!isset($this->identifierTokens[$this->tokens[$j][0]])) {
|
||||
break;
|
||||
}
|
||||
$lastWasSeparator = false;
|
||||
} else {
|
||||
if ($this->tokens[$j][0] !== \T_NS_SEPARATOR) {
|
||||
break;
|
||||
}
|
||||
$lastWasSeparator = true;
|
||||
}
|
||||
$text .= $this->tokens[$j][1];
|
||||
}
|
||||
if ($lastWasSeparator) {
|
||||
// Trailing separator is not part of the name.
|
||||
$j--;
|
||||
$text = substr($text, 0, -1);
|
||||
}
|
||||
if ($j > $i + 1) {
|
||||
if ($token[0] === \T_NS_SEPARATOR) {
|
||||
$type = \T_NAME_FULLY_QUALIFIED;
|
||||
} else if ($token[0] === \T_NAMESPACE) {
|
||||
$type = \T_NAME_RELATIVE;
|
||||
} else {
|
||||
$type = \T_NAME_QUALIFIED;
|
||||
}
|
||||
$token = [$type, $text, $line];
|
||||
array_splice($this->tokens, $i, $j - $i, [$token]);
|
||||
$numTokens -= $j - $i - 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($token === '&') {
|
||||
$next = $i + 1;
|
||||
while (isset($this->tokens[$next]) && $this->tokens[$next][0] === \T_WHITESPACE) {
|
||||
$next++;
|
||||
}
|
||||
$followedByVarOrVarArg = isset($this->tokens[$next]) &&
|
||||
($this->tokens[$next][0] === \T_VARIABLE || $this->tokens[$next][0] === \T_ELLIPSIS);
|
||||
$this->tokens[$i] = $token = [
|
||||
$followedByVarOrVarArg
|
||||
? \T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG
|
||||
: \T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG,
|
||||
'&',
|
||||
$line,
|
||||
];
|
||||
}
|
||||
|
||||
$tokenValue = \is_string($token) ? $token : $token[1];
|
||||
$tokenLen = \strlen($tokenValue);
|
||||
|
||||
if (substr($this->code, $filePos, $tokenLen) !== $tokenValue) {
|
||||
// Something is missing, must be an invalid character
|
||||
$nextFilePos = strpos($this->code, $tokenValue, $filePos);
|
||||
$badCharTokens = $this->handleInvalidCharacterRange(
|
||||
$filePos, $nextFilePos, $line, $errorHandler);
|
||||
$filePos = (int) $nextFilePos;
|
||||
|
||||
array_splice($this->tokens, $i, 0, $badCharTokens);
|
||||
$numTokens += \count($badCharTokens);
|
||||
$i += \count($badCharTokens);
|
||||
}
|
||||
|
||||
$filePos += $tokenLen;
|
||||
$line += substr_count($tokenValue, "\n");
|
||||
}
|
||||
|
||||
if ($filePos !== \strlen($this->code)) {
|
||||
if (substr($this->code, $filePos, 2) === '/*') {
|
||||
// Unlike PHP, HHVM will drop unterminated comments entirely
|
||||
$comment = substr($this->code, $filePos);
|
||||
$errorHandler->handleError(new Error('Unterminated comment', [
|
||||
'startLine' => $line,
|
||||
'endLine' => $line + substr_count($comment, "\n"),
|
||||
'startFilePos' => $filePos,
|
||||
'endFilePos' => $filePos + \strlen($comment),
|
||||
]));
|
||||
|
||||
// Emulate the PHP behavior
|
||||
$isDocComment = isset($comment[3]) && $comment[3] === '*';
|
||||
$this->tokens[] = [$isDocComment ? \T_DOC_COMMENT : \T_COMMENT, $comment, $line];
|
||||
} else {
|
||||
// Invalid characters at the end of the input
|
||||
$badCharTokens = $this->handleInvalidCharacterRange(
|
||||
$filePos, \strlen($this->code), $line, $errorHandler);
|
||||
$this->tokens = array_merge($this->tokens, $badCharTokens);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (count($this->tokens) > 0) {
|
||||
// Check for unterminated comment
|
||||
$lastToken = $this->tokens[count($this->tokens) - 1];
|
||||
if ($this->isUnterminatedComment($lastToken)) {
|
||||
$errorHandler->handleError(new Error('Unterminated comment', [
|
||||
'startLine' => $line - substr_count($lastToken[1], "\n"),
|
||||
'endLine' => $line,
|
||||
'startFilePos' => $filePos - \strlen($lastToken[1]),
|
||||
'endFilePos' => $filePos,
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the next token.
|
||||
*
|
||||
* The available attributes are determined by the 'usedAttributes' option, which can
|
||||
* be specified in the constructor. The following attributes are supported:
|
||||
*
|
||||
* * 'comments' => Array of PhpParser\Comment or PhpParser\Comment\Doc instances,
|
||||
* representing all comments that occurred between the previous
|
||||
* non-discarded token and the current one.
|
||||
* * 'startLine' => Line in which the node starts.
|
||||
* * 'endLine' => Line in which the node ends.
|
||||
* * 'startTokenPos' => Offset into the token array of the first token in the node.
|
||||
* * 'endTokenPos' => Offset into the token array of the last token in the node.
|
||||
* * 'startFilePos' => Offset into the code string of the first character that is part of the node.
|
||||
* * 'endFilePos' => Offset into the code string of the last character that is part of the node.
|
||||
*
|
||||
* @param mixed $value Variable to store token content in
|
||||
* @param mixed $startAttributes Variable to store start attributes in
|
||||
* @param mixed $endAttributes Variable to store end attributes in
|
||||
*
|
||||
* @return int Token id
|
||||
*/
|
||||
public function getNextToken(&$value = null, &$startAttributes = null, &$endAttributes = null) : int {
|
||||
$startAttributes = [];
|
||||
$endAttributes = [];
|
||||
|
||||
while (1) {
|
||||
if (isset($this->tokens[++$this->pos])) {
|
||||
$token = $this->tokens[$this->pos];
|
||||
} else {
|
||||
// EOF token with ID 0
|
||||
$token = "\0";
|
||||
}
|
||||
|
||||
if ($this->attributeStartLineUsed) {
|
||||
$startAttributes['startLine'] = $this->line;
|
||||
}
|
||||
if ($this->attributeStartTokenPosUsed) {
|
||||
$startAttributes['startTokenPos'] = $this->pos;
|
||||
}
|
||||
if ($this->attributeStartFilePosUsed) {
|
||||
$startAttributes['startFilePos'] = $this->filePos;
|
||||
}
|
||||
|
||||
if (\is_string($token)) {
|
||||
$value = $token;
|
||||
if (isset($token[1])) {
|
||||
// bug in token_get_all
|
||||
$this->filePos += 2;
|
||||
$id = ord('"');
|
||||
} else {
|
||||
$this->filePos += 1;
|
||||
$id = ord($token);
|
||||
}
|
||||
} elseif (!isset($this->dropTokens[$token[0]])) {
|
||||
$value = $token[1];
|
||||
$id = $this->tokenMap[$token[0]];
|
||||
if (\T_CLOSE_TAG === $token[0]) {
|
||||
$this->prevCloseTagHasNewline = false !== strpos($token[1], "\n")
|
||||
|| false !== strpos($token[1], "\r");
|
||||
} elseif (\T_INLINE_HTML === $token[0]) {
|
||||
$startAttributes['hasLeadingNewline'] = $this->prevCloseTagHasNewline;
|
||||
}
|
||||
|
||||
$this->line += substr_count($value, "\n");
|
||||
$this->filePos += \strlen($value);
|
||||
} else {
|
||||
$origLine = $this->line;
|
||||
$origFilePos = $this->filePos;
|
||||
$this->line += substr_count($token[1], "\n");
|
||||
$this->filePos += \strlen($token[1]);
|
||||
|
||||
if (\T_COMMENT === $token[0] || \T_DOC_COMMENT === $token[0]) {
|
||||
if ($this->attributeCommentsUsed) {
|
||||
$comment = \T_DOC_COMMENT === $token[0]
|
||||
? new Comment\Doc($token[1],
|
||||
$origLine, $origFilePos, $this->pos,
|
||||
$this->line, $this->filePos - 1, $this->pos)
|
||||
: new Comment($token[1],
|
||||
$origLine, $origFilePos, $this->pos,
|
||||
$this->line, $this->filePos - 1, $this->pos);
|
||||
$startAttributes['comments'][] = $comment;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->attributeEndLineUsed) {
|
||||
$endAttributes['endLine'] = $this->line;
|
||||
}
|
||||
if ($this->attributeEndTokenPosUsed) {
|
||||
$endAttributes['endTokenPos'] = $this->pos;
|
||||
}
|
||||
if ($this->attributeEndFilePosUsed) {
|
||||
$endAttributes['endFilePos'] = $this->filePos - 1;
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
throw new \RuntimeException('Reached end of lexer loop');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the token array for current code.
|
||||
*
|
||||
* The token array is in the same format as provided by the
|
||||
* token_get_all() function and does not discard tokens (i.e.
|
||||
* whitespace and comments are included). The token position
|
||||
* attributes are against this token array.
|
||||
*
|
||||
* @return array Array of tokens in token_get_all() format
|
||||
*/
|
||||
public function getTokens() : array {
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles __halt_compiler() by returning the text after it.
|
||||
*
|
||||
* @return string Remaining text
|
||||
*/
|
||||
public function handleHaltCompiler() : string {
|
||||
// text after T_HALT_COMPILER, still including ();
|
||||
$textAfter = substr($this->code, $this->filePos);
|
||||
|
||||
// ensure that it is followed by ();
|
||||
// this simplifies the situation, by not allowing any comments
|
||||
// in between of the tokens.
|
||||
if (!preg_match('~^\s*\(\s*\)\s*(?:;|\?>\r?\n?)~', $textAfter, $matches)) {
|
||||
throw new Error('__HALT_COMPILER must be followed by "();"');
|
||||
}
|
||||
|
||||
// prevent the lexer from returning any further tokens
|
||||
$this->pos = count($this->tokens);
|
||||
|
||||
// return with (); removed
|
||||
return substr($textAfter, strlen($matches[0]));
|
||||
}
|
||||
|
||||
private function defineCompatibilityTokens() {
|
||||
static $compatTokensDefined = false;
|
||||
if ($compatTokensDefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
$compatTokens = [
|
||||
// PHP 7.4
|
||||
'T_BAD_CHARACTER',
|
||||
'T_FN',
|
||||
'T_COALESCE_EQUAL',
|
||||
// PHP 8.0
|
||||
'T_NAME_QUALIFIED',
|
||||
'T_NAME_FULLY_QUALIFIED',
|
||||
'T_NAME_RELATIVE',
|
||||
'T_MATCH',
|
||||
'T_NULLSAFE_OBJECT_OPERATOR',
|
||||
'T_ATTRIBUTE',
|
||||
// PHP 8.1
|
||||
'T_ENUM',
|
||||
'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG',
|
||||
'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG',
|
||||
'T_READONLY',
|
||||
];
|
||||
|
||||
// PHP-Parser might be used together with another library that also emulates some or all
|
||||
// of these tokens. Perform a sanity-check that all already defined tokens have been
|
||||
// assigned a unique ID.
|
||||
$usedTokenIds = [];
|
||||
foreach ($compatTokens as $token) {
|
||||
if (\defined($token)) {
|
||||
$tokenId = \constant($token);
|
||||
$clashingToken = $usedTokenIds[$tokenId] ?? null;
|
||||
if ($clashingToken !== null) {
|
||||
throw new \Error(sprintf(
|
||||
'Token %s has same ID as token %s, ' .
|
||||
'you may be using a library with broken token emulation',
|
||||
$token, $clashingToken
|
||||
));
|
||||
}
|
||||
$usedTokenIds[$tokenId] = $token;
|
||||
}
|
||||
}
|
||||
|
||||
// Now define any tokens that have not yet been emulated. Try to assign IDs from -1
|
||||
// downwards, but skip any IDs that may already be in use.
|
||||
$newTokenId = -1;
|
||||
foreach ($compatTokens as $token) {
|
||||
if (!\defined($token)) {
|
||||
while (isset($usedTokenIds[$newTokenId])) {
|
||||
$newTokenId--;
|
||||
}
|
||||
\define($token, $newTokenId);
|
||||
$newTokenId--;
|
||||
}
|
||||
}
|
||||
|
||||
$compatTokensDefined = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the token map.
|
||||
*
|
||||
* The token map maps the PHP internal token identifiers
|
||||
* to the identifiers used by the Parser. Additionally it
|
||||
* maps T_OPEN_TAG_WITH_ECHO to T_ECHO and T_CLOSE_TAG to ';'.
|
||||
*
|
||||
* @return array The token map
|
||||
*/
|
||||
protected function createTokenMap() : array {
|
||||
$tokenMap = [];
|
||||
|
||||
// 256 is the minimum possible token number, as everything below
|
||||
// it is an ASCII value
|
||||
for ($i = 256; $i < 1000; ++$i) {
|
||||
if (\T_DOUBLE_COLON === $i) {
|
||||
// T_DOUBLE_COLON is equivalent to T_PAAMAYIM_NEKUDOTAYIM
|
||||
$tokenMap[$i] = Tokens::T_PAAMAYIM_NEKUDOTAYIM;
|
||||
} elseif(\T_OPEN_TAG_WITH_ECHO === $i) {
|
||||
// T_OPEN_TAG_WITH_ECHO with dropped T_OPEN_TAG results in T_ECHO
|
||||
$tokenMap[$i] = Tokens::T_ECHO;
|
||||
} elseif(\T_CLOSE_TAG === $i) {
|
||||
// T_CLOSE_TAG is equivalent to ';'
|
||||
$tokenMap[$i] = ord(';');
|
||||
} elseif ('UNKNOWN' !== $name = token_name($i)) {
|
||||
if ('T_HASHBANG' === $name) {
|
||||
// HHVM uses a special token for #! hashbang lines
|
||||
$tokenMap[$i] = Tokens::T_INLINE_HTML;
|
||||
} elseif (defined($name = Tokens::class . '::' . $name)) {
|
||||
// Other tokens can be mapped directly
|
||||
$tokenMap[$i] = constant($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HHVM uses a special token for numbers that overflow to double
|
||||
if (defined('T_ONUMBER')) {
|
||||
$tokenMap[\T_ONUMBER] = Tokens::T_DNUMBER;
|
||||
}
|
||||
// HHVM also has a separate token for the __COMPILER_HALT_OFFSET__ constant
|
||||
if (defined('T_COMPILER_HALT_OFFSET')) {
|
||||
$tokenMap[\T_COMPILER_HALT_OFFSET] = Tokens::T_STRING;
|
||||
}
|
||||
|
||||
// Assign tokens for which we define compatibility constants, as token_name() does not know them.
|
||||
$tokenMap[\T_FN] = Tokens::T_FN;
|
||||
$tokenMap[\T_COALESCE_EQUAL] = Tokens::T_COALESCE_EQUAL;
|
||||
$tokenMap[\T_NAME_QUALIFIED] = Tokens::T_NAME_QUALIFIED;
|
||||
$tokenMap[\T_NAME_FULLY_QUALIFIED] = Tokens::T_NAME_FULLY_QUALIFIED;
|
||||
$tokenMap[\T_NAME_RELATIVE] = Tokens::T_NAME_RELATIVE;
|
||||
$tokenMap[\T_MATCH] = Tokens::T_MATCH;
|
||||
$tokenMap[\T_NULLSAFE_OBJECT_OPERATOR] = Tokens::T_NULLSAFE_OBJECT_OPERATOR;
|
||||
$tokenMap[\T_ATTRIBUTE] = Tokens::T_ATTRIBUTE;
|
||||
$tokenMap[\T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG] = Tokens::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG;
|
||||
$tokenMap[\T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG] = Tokens::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG;
|
||||
$tokenMap[\T_ENUM] = Tokens::T_ENUM;
|
||||
$tokenMap[\T_READONLY] = Tokens::T_READONLY;
|
||||
|
||||
return $tokenMap;
|
||||
}
|
||||
|
||||
private function createIdentifierTokenMap(): array {
|
||||
// Based on semi_reserved production.
|
||||
return array_fill_keys([
|
||||
\T_STRING,
|
||||
\T_STATIC, \T_ABSTRACT, \T_FINAL, \T_PRIVATE, \T_PROTECTED, \T_PUBLIC, \T_READONLY,
|
||||
\T_INCLUDE, \T_INCLUDE_ONCE, \T_EVAL, \T_REQUIRE, \T_REQUIRE_ONCE, \T_LOGICAL_OR, \T_LOGICAL_XOR, \T_LOGICAL_AND,
|
||||
\T_INSTANCEOF, \T_NEW, \T_CLONE, \T_EXIT, \T_IF, \T_ELSEIF, \T_ELSE, \T_ENDIF, \T_ECHO, \T_DO, \T_WHILE,
|
||||
\T_ENDWHILE, \T_FOR, \T_ENDFOR, \T_FOREACH, \T_ENDFOREACH, \T_DECLARE, \T_ENDDECLARE, \T_AS, \T_TRY, \T_CATCH,
|
||||
\T_FINALLY, \T_THROW, \T_USE, \T_INSTEADOF, \T_GLOBAL, \T_VAR, \T_UNSET, \T_ISSET, \T_EMPTY, \T_CONTINUE, \T_GOTO,
|
||||
\T_FUNCTION, \T_CONST, \T_RETURN, \T_PRINT, \T_YIELD, \T_LIST, \T_SWITCH, \T_ENDSWITCH, \T_CASE, \T_DEFAULT,
|
||||
\T_BREAK, \T_ARRAY, \T_CALLABLE, \T_EXTENDS, \T_IMPLEMENTS, \T_NAMESPACE, \T_TRAIT, \T_INTERFACE, \T_CLASS,
|
||||
\T_CLASS_C, \T_TRAIT_C, \T_FUNC_C, \T_METHOD_C, \T_LINE, \T_FILE, \T_DIR, \T_NS_C, \T_HALT_COMPILER, \T_FN,
|
||||
\T_MATCH,
|
||||
], true);
|
||||
}
|
||||
}
|
251
vendor/nikic/php-parser/lib/PhpParser/Lexer/Emulative.php
vendored
Normal file
251
vendor/nikic/php-parser/lib/PhpParser/Lexer/Emulative.php
vendored
Normal file
|
@ -0,0 +1,251 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer;
|
||||
|
||||
use PhpParser\Error;
|
||||
use PhpParser\ErrorHandler;
|
||||
use PhpParser\Lexer;
|
||||
use PhpParser\Lexer\TokenEmulator\AttributeEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\EnumTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\CoaleseEqualTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\ExplicitOctalEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\FlexibleDocStringEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\FnTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\MatchTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\NullsafeTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\NumericLiteralSeparatorEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\ReadonlyFunctionTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\ReadonlyTokenEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\ReverseEmulator;
|
||||
use PhpParser\Lexer\TokenEmulator\TokenEmulator;
|
||||
|
||||
class Emulative extends Lexer
|
||||
{
|
||||
const PHP_7_3 = '7.3dev';
|
||||
const PHP_7_4 = '7.4dev';
|
||||
const PHP_8_0 = '8.0dev';
|
||||
const PHP_8_1 = '8.1dev';
|
||||
const PHP_8_2 = '8.2dev';
|
||||
|
||||
/** @var mixed[] Patches used to reverse changes introduced in the code */
|
||||
private $patches = [];
|
||||
|
||||
/** @var TokenEmulator[] */
|
||||
private $emulators = [];
|
||||
|
||||
/** @var string */
|
||||
private $targetPhpVersion;
|
||||
|
||||
/**
|
||||
* @param mixed[] $options Lexer options. In addition to the usual options,
|
||||
* accepts a 'phpVersion' string that specifies the
|
||||
* version to emulate. Defaults to newest supported.
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->targetPhpVersion = $options['phpVersion'] ?? Emulative::PHP_8_2;
|
||||
unset($options['phpVersion']);
|
||||
|
||||
parent::__construct($options);
|
||||
|
||||
$emulators = [
|
||||
new FlexibleDocStringEmulator(),
|
||||
new FnTokenEmulator(),
|
||||
new MatchTokenEmulator(),
|
||||
new CoaleseEqualTokenEmulator(),
|
||||
new NumericLiteralSeparatorEmulator(),
|
||||
new NullsafeTokenEmulator(),
|
||||
new AttributeEmulator(),
|
||||
new EnumTokenEmulator(),
|
||||
new ReadonlyTokenEmulator(),
|
||||
new ExplicitOctalEmulator(),
|
||||
new ReadonlyFunctionTokenEmulator(),
|
||||
];
|
||||
|
||||
// Collect emulators that are relevant for the PHP version we're running
|
||||
// and the PHP version we're targeting for emulation.
|
||||
foreach ($emulators as $emulator) {
|
||||
$emulatorPhpVersion = $emulator->getPhpVersion();
|
||||
if ($this->isForwardEmulationNeeded($emulatorPhpVersion)) {
|
||||
$this->emulators[] = $emulator;
|
||||
} else if ($this->isReverseEmulationNeeded($emulatorPhpVersion)) {
|
||||
$this->emulators[] = new ReverseEmulator($emulator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function startLexing(string $code, ErrorHandler $errorHandler = null) {
|
||||
$emulators = array_filter($this->emulators, function($emulator) use($code) {
|
||||
return $emulator->isEmulationNeeded($code);
|
||||
});
|
||||
|
||||
if (empty($emulators)) {
|
||||
// Nothing to emulate, yay
|
||||
parent::startLexing($code, $errorHandler);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->patches = [];
|
||||
foreach ($emulators as $emulator) {
|
||||
$code = $emulator->preprocessCode($code, $this->patches);
|
||||
}
|
||||
|
||||
$collector = new ErrorHandler\Collecting();
|
||||
parent::startLexing($code, $collector);
|
||||
$this->sortPatches();
|
||||
$this->fixupTokens();
|
||||
|
||||
$errors = $collector->getErrors();
|
||||
if (!empty($errors)) {
|
||||
$this->fixupErrors($errors);
|
||||
foreach ($errors as $error) {
|
||||
$errorHandler->handleError($error);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($emulators as $emulator) {
|
||||
$this->tokens = $emulator->emulate($code, $this->tokens);
|
||||
}
|
||||
}
|
||||
|
||||
private function isForwardEmulationNeeded(string $emulatorPhpVersion): bool {
|
||||
return version_compare(\PHP_VERSION, $emulatorPhpVersion, '<')
|
||||
&& version_compare($this->targetPhpVersion, $emulatorPhpVersion, '>=');
|
||||
}
|
||||
|
||||
private function isReverseEmulationNeeded(string $emulatorPhpVersion): bool {
|
||||
return version_compare(\PHP_VERSION, $emulatorPhpVersion, '>=')
|
||||
&& version_compare($this->targetPhpVersion, $emulatorPhpVersion, '<');
|
||||
}
|
||||
|
||||
private function sortPatches()
|
||||
{
|
||||
// Patches may be contributed by different emulators.
|
||||
// Make sure they are sorted by increasing patch position.
|
||||
usort($this->patches, function($p1, $p2) {
|
||||
return $p1[0] <=> $p2[0];
|
||||
});
|
||||
}
|
||||
|
||||
private function fixupTokens()
|
||||
{
|
||||
if (\count($this->patches) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load first patch
|
||||
$patchIdx = 0;
|
||||
|
||||
list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx];
|
||||
|
||||
// We use a manual loop over the tokens, because we modify the array on the fly
|
||||
$pos = 0;
|
||||
for ($i = 0, $c = \count($this->tokens); $i < $c; $i++) {
|
||||
$token = $this->tokens[$i];
|
||||
if (\is_string($token)) {
|
||||
if ($patchPos === $pos) {
|
||||
// Only support replacement for string tokens.
|
||||
assert($patchType === 'replace');
|
||||
$this->tokens[$i] = $patchText;
|
||||
|
||||
// Fetch the next patch
|
||||
$patchIdx++;
|
||||
if ($patchIdx >= \count($this->patches)) {
|
||||
// No more patches, we're done
|
||||
return;
|
||||
}
|
||||
list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx];
|
||||
}
|
||||
|
||||
$pos += \strlen($token);
|
||||
continue;
|
||||
}
|
||||
|
||||
$len = \strlen($token[1]);
|
||||
$posDelta = 0;
|
||||
while ($patchPos >= $pos && $patchPos < $pos + $len) {
|
||||
$patchTextLen = \strlen($patchText);
|
||||
if ($patchType === 'remove') {
|
||||
if ($patchPos === $pos && $patchTextLen === $len) {
|
||||
// Remove token entirely
|
||||
array_splice($this->tokens, $i, 1, []);
|
||||
$i--;
|
||||
$c--;
|
||||
} else {
|
||||
// Remove from token string
|
||||
$this->tokens[$i][1] = substr_replace(
|
||||
$token[1], '', $patchPos - $pos + $posDelta, $patchTextLen
|
||||
);
|
||||
$posDelta -= $patchTextLen;
|
||||
}
|
||||
} elseif ($patchType === 'add') {
|
||||
// Insert into the token string
|
||||
$this->tokens[$i][1] = substr_replace(
|
||||
$token[1], $patchText, $patchPos - $pos + $posDelta, 0
|
||||
);
|
||||
$posDelta += $patchTextLen;
|
||||
} else if ($patchType === 'replace') {
|
||||
// Replace inside the token string
|
||||
$this->tokens[$i][1] = substr_replace(
|
||||
$token[1], $patchText, $patchPos - $pos + $posDelta, $patchTextLen
|
||||
);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// Fetch the next patch
|
||||
$patchIdx++;
|
||||
if ($patchIdx >= \count($this->patches)) {
|
||||
// No more patches, we're done
|
||||
return;
|
||||
}
|
||||
|
||||
list($patchPos, $patchType, $patchText) = $this->patches[$patchIdx];
|
||||
|
||||
// Multiple patches may apply to the same token. Reload the current one to check
|
||||
// If the new patch applies
|
||||
$token = $this->tokens[$i];
|
||||
}
|
||||
|
||||
$pos += $len;
|
||||
}
|
||||
|
||||
// A patch did not apply
|
||||
assert(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fixup line and position information in errors.
|
||||
*
|
||||
* @param Error[] $errors
|
||||
*/
|
||||
private function fixupErrors(array $errors) {
|
||||
foreach ($errors as $error) {
|
||||
$attrs = $error->getAttributes();
|
||||
|
||||
$posDelta = 0;
|
||||
$lineDelta = 0;
|
||||
foreach ($this->patches as $patch) {
|
||||
list($patchPos, $patchType, $patchText) = $patch;
|
||||
if ($patchPos >= $attrs['startFilePos']) {
|
||||
// No longer relevant
|
||||
break;
|
||||
}
|
||||
|
||||
if ($patchType === 'add') {
|
||||
$posDelta += strlen($patchText);
|
||||
$lineDelta += substr_count($patchText, "\n");
|
||||
} else if ($patchType === 'remove') {
|
||||
$posDelta -= strlen($patchText);
|
||||
$lineDelta -= substr_count($patchText, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
$attrs['startFilePos'] += $posDelta;
|
||||
$attrs['endFilePos'] += $posDelta;
|
||||
$attrs['startLine'] += $lineDelta;
|
||||
$attrs['endLine'] += $lineDelta;
|
||||
$error->setAttributes($attrs);
|
||||
}
|
||||
}
|
||||
}
|
56
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php
vendored
Normal file
56
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/AttributeEmulator.php
vendored
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\Lexer\Emulative;
|
||||
|
||||
final class AttributeEmulator extends TokenEmulator
|
||||
{
|
||||
public function getPhpVersion(): string
|
||||
{
|
||||
return Emulative::PHP_8_0;
|
||||
}
|
||||
|
||||
public function isEmulationNeeded(string $code) : bool
|
||||
{
|
||||
return strpos($code, '#[') !== false;
|
||||
}
|
||||
|
||||
public function emulate(string $code, array $tokens): array
|
||||
{
|
||||
// We need to manually iterate and manage a count because we'll change
|
||||
// the tokens array on the way.
|
||||
$line = 1;
|
||||
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
|
||||
if ($tokens[$i] === '#' && isset($tokens[$i + 1]) && $tokens[$i + 1] === '[') {
|
||||
array_splice($tokens, $i, 2, [
|
||||
[\T_ATTRIBUTE, '#[', $line]
|
||||
]);
|
||||
$c--;
|
||||
continue;
|
||||
}
|
||||
if (\is_array($tokens[$i])) {
|
||||
$line += substr_count($tokens[$i][1], "\n");
|
||||
}
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
public function reverseEmulate(string $code, array $tokens): array
|
||||
{
|
||||
// TODO
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
public function preprocessCode(string $code, array &$patches): string {
|
||||
$pos = 0;
|
||||
while (false !== $pos = strpos($code, '#[', $pos)) {
|
||||
// Replace #[ with %[
|
||||
$code[$pos] = '%';
|
||||
$patches[] = [$pos, 'replace', '#'];
|
||||
$pos += 2;
|
||||
}
|
||||
return $code;
|
||||
}
|
||||
}
|
47
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.php
vendored
Normal file
47
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/CoaleseEqualTokenEmulator.php
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\Lexer\Emulative;
|
||||
|
||||
final class CoaleseEqualTokenEmulator extends TokenEmulator
|
||||
{
|
||||
public function getPhpVersion(): string
|
||||
{
|
||||
return Emulative::PHP_7_4;
|
||||
}
|
||||
|
||||
public function isEmulationNeeded(string $code): bool
|
||||
{
|
||||
return strpos($code, '??=') !== false;
|
||||
}
|
||||
|
||||
public function emulate(string $code, array $tokens): array
|
||||
{
|
||||
// We need to manually iterate and manage a count because we'll change
|
||||
// the tokens array on the way
|
||||
$line = 1;
|
||||
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
|
||||
if (isset($tokens[$i + 1])) {
|
||||
if ($tokens[$i][0] === T_COALESCE && $tokens[$i + 1] === '=') {
|
||||
array_splice($tokens, $i, 2, [
|
||||
[\T_COALESCE_EQUAL, '??=', $line]
|
||||
]);
|
||||
$c--;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (\is_array($tokens[$i])) {
|
||||
$line += substr_count($tokens[$i][1], "\n");
|
||||
}
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
public function reverseEmulate(string $code, array $tokens): array
|
||||
{
|
||||
// ??= was not valid code previously, don't bother.
|
||||
return $tokens;
|
||||
}
|
||||
}
|
31
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php
vendored
Normal file
31
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/EnumTokenEmulator.php
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\Lexer\Emulative;
|
||||
|
||||
final class EnumTokenEmulator extends KeywordEmulator
|
||||
{
|
||||
public function getPhpVersion(): string
|
||||
{
|
||||
return Emulative::PHP_8_1;
|
||||
}
|
||||
|
||||
public function getKeywordString(): string
|
||||
{
|
||||
return 'enum';
|
||||
}
|
||||
|
||||
public function getKeywordToken(): int
|
||||
{
|
||||
return \T_ENUM;
|
||||
}
|
||||
|
||||
protected function isKeywordContext(array $tokens, int $pos): bool
|
||||
{
|
||||
return parent::isKeywordContext($tokens, $pos)
|
||||
&& isset($tokens[$pos + 2])
|
||||
&& $tokens[$pos + 1][0] === \T_WHITESPACE
|
||||
&& $tokens[$pos + 2][0] === \T_STRING;
|
||||
}
|
||||
}
|
44
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php
vendored
Normal file
44
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ExplicitOctalEmulator.php
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\Lexer\Emulative;
|
||||
|
||||
class ExplicitOctalEmulator extends TokenEmulator {
|
||||
public function getPhpVersion(): string {
|
||||
return Emulative::PHP_8_1;
|
||||
}
|
||||
|
||||
public function isEmulationNeeded(string $code): bool {
|
||||
return strpos($code, '0o') !== false || strpos($code, '0O') !== false;
|
||||
}
|
||||
|
||||
public function emulate(string $code, array $tokens): array {
|
||||
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
|
||||
if ($tokens[$i][0] == \T_LNUMBER && $tokens[$i][1] === '0' &&
|
||||
isset($tokens[$i + 1]) && $tokens[$i + 1][0] == \T_STRING &&
|
||||
preg_match('/[oO][0-7]+(?:_[0-7]+)*/', $tokens[$i + 1][1])
|
||||
) {
|
||||
$tokenKind = $this->resolveIntegerOrFloatToken($tokens[$i + 1][1]);
|
||||
array_splice($tokens, $i, 2, [
|
||||
[$tokenKind, '0' . $tokens[$i + 1][1], $tokens[$i][2]],
|
||||
]);
|
||||
$c--;
|
||||
}
|
||||
}
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
private function resolveIntegerOrFloatToken(string $str): int
|
||||
{
|
||||
$str = substr($str, 1);
|
||||
$str = str_replace('_', '', $str);
|
||||
$num = octdec($str);
|
||||
return is_float($num) ? \T_DNUMBER : \T_LNUMBER;
|
||||
}
|
||||
|
||||
public function reverseEmulate(string $code, array $tokens): array {
|
||||
// Explicit octals were not legal code previously, don't bother.
|
||||
return $tokens;
|
||||
}
|
||||
}
|
76
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/FlexibleDocStringEmulator.php
vendored
Normal file
76
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/FlexibleDocStringEmulator.php
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\Lexer\Emulative;
|
||||
|
||||
final class FlexibleDocStringEmulator extends TokenEmulator
|
||||
{
|
||||
const FLEXIBLE_DOC_STRING_REGEX = <<<'REGEX'
|
||||
/<<<[ \t]*(['"]?)([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)\1\r?\n
|
||||
(?:.*\r?\n)*?
|
||||
(?<indentation>\h*)\2(?![a-zA-Z0-9_\x80-\xff])(?<separator>(?:;?[\r\n])?)/x
|
||||
REGEX;
|
||||
|
||||
public function getPhpVersion(): string
|
||||
{
|
||||
return Emulative::PHP_7_3;
|
||||
}
|
||||
|
||||
public function isEmulationNeeded(string $code) : bool
|
||||
{
|
||||
return strpos($code, '<<<') !== false;
|
||||
}
|
||||
|
||||
public function emulate(string $code, array $tokens): array
|
||||
{
|
||||
// Handled by preprocessing + fixup.
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
public function reverseEmulate(string $code, array $tokens): array
|
||||
{
|
||||
// Not supported.
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
public function preprocessCode(string $code, array &$patches): string {
|
||||
if (!preg_match_all(self::FLEXIBLE_DOC_STRING_REGEX, $code, $matches, PREG_SET_ORDER|PREG_OFFSET_CAPTURE)) {
|
||||
// No heredoc/nowdoc found
|
||||
return $code;
|
||||
}
|
||||
|
||||
// Keep track of how much we need to adjust string offsets due to the modifications we
|
||||
// already made
|
||||
$posDelta = 0;
|
||||
foreach ($matches as $match) {
|
||||
$indentation = $match['indentation'][0];
|
||||
$indentationStart = $match['indentation'][1];
|
||||
|
||||
$separator = $match['separator'][0];
|
||||
$separatorStart = $match['separator'][1];
|
||||
|
||||
if ($indentation === '' && $separator !== '') {
|
||||
// Ordinary heredoc/nowdoc
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($indentation !== '') {
|
||||
// Remove indentation
|
||||
$indentationLen = strlen($indentation);
|
||||
$code = substr_replace($code, '', $indentationStart + $posDelta, $indentationLen);
|
||||
$patches[] = [$indentationStart + $posDelta, 'add', $indentation];
|
||||
$posDelta -= $indentationLen;
|
||||
}
|
||||
|
||||
if ($separator === '') {
|
||||
// Insert newline as separator
|
||||
$code = substr_replace($code, "\n", $separatorStart + $posDelta, 0);
|
||||
$patches[] = [$separatorStart + $posDelta, 'remove', "\n"];
|
||||
$posDelta += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return $code;
|
||||
}
|
||||
}
|
23
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php
vendored
Normal file
23
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/FnTokenEmulator.php
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\Lexer\Emulative;
|
||||
|
||||
final class FnTokenEmulator extends KeywordEmulator
|
||||
{
|
||||
public function getPhpVersion(): string
|
||||
{
|
||||
return Emulative::PHP_7_4;
|
||||
}
|
||||
|
||||
public function getKeywordString(): string
|
||||
{
|
||||
return 'fn';
|
||||
}
|
||||
|
||||
public function getKeywordToken(): int
|
||||
{
|
||||
return \T_FN;
|
||||
}
|
||||
}
|
62
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php
vendored
Normal file
62
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/KeywordEmulator.php
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
abstract class KeywordEmulator extends TokenEmulator
|
||||
{
|
||||
abstract function getKeywordString(): string;
|
||||
abstract function getKeywordToken(): int;
|
||||
|
||||
public function isEmulationNeeded(string $code): bool
|
||||
{
|
||||
return strpos(strtolower($code), $this->getKeywordString()) !== false;
|
||||
}
|
||||
|
||||
protected function isKeywordContext(array $tokens, int $pos): bool
|
||||
{
|
||||
$previousNonSpaceToken = $this->getPreviousNonSpaceToken($tokens, $pos);
|
||||
return $previousNonSpaceToken === null || $previousNonSpaceToken[0] !== \T_OBJECT_OPERATOR;
|
||||
}
|
||||
|
||||
public function emulate(string $code, array $tokens): array
|
||||
{
|
||||
$keywordString = $this->getKeywordString();
|
||||
foreach ($tokens as $i => $token) {
|
||||
if ($token[0] === T_STRING && strtolower($token[1]) === $keywordString
|
||||
&& $this->isKeywordContext($tokens, $i)) {
|
||||
$tokens[$i][0] = $this->getKeywordToken();
|
||||
}
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $tokens
|
||||
* @return array|string|null
|
||||
*/
|
||||
private function getPreviousNonSpaceToken(array $tokens, int $start)
|
||||
{
|
||||
for ($i = $start - 1; $i >= 0; --$i) {
|
||||
if ($tokens[$i][0] === T_WHITESPACE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $tokens[$i];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function reverseEmulate(string $code, array $tokens): array
|
||||
{
|
||||
$keywordToken = $this->getKeywordToken();
|
||||
foreach ($tokens as $i => $token) {
|
||||
if ($token[0] === $keywordToken) {
|
||||
$tokens[$i][0] = \T_STRING;
|
||||
}
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
}
|
23
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php
vendored
Normal file
23
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/MatchTokenEmulator.php
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\Lexer\Emulative;
|
||||
|
||||
final class MatchTokenEmulator extends KeywordEmulator
|
||||
{
|
||||
public function getPhpVersion(): string
|
||||
{
|
||||
return Emulative::PHP_8_0;
|
||||
}
|
||||
|
||||
public function getKeywordString(): string
|
||||
{
|
||||
return 'match';
|
||||
}
|
||||
|
||||
public function getKeywordToken(): int
|
||||
{
|
||||
return \T_MATCH;
|
||||
}
|
||||
}
|
67
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php
vendored
Normal file
67
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\Lexer\Emulative;
|
||||
|
||||
final class NullsafeTokenEmulator extends TokenEmulator
|
||||
{
|
||||
public function getPhpVersion(): string
|
||||
{
|
||||
return Emulative::PHP_8_0;
|
||||
}
|
||||
|
||||
public function isEmulationNeeded(string $code): bool
|
||||
{
|
||||
return strpos($code, '?->') !== false;
|
||||
}
|
||||
|
||||
public function emulate(string $code, array $tokens): array
|
||||
{
|
||||
// We need to manually iterate and manage a count because we'll change
|
||||
// the tokens array on the way
|
||||
$line = 1;
|
||||
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
|
||||
if ($tokens[$i] === '?' && isset($tokens[$i + 1]) && $tokens[$i + 1][0] === \T_OBJECT_OPERATOR) {
|
||||
array_splice($tokens, $i, 2, [
|
||||
[\T_NULLSAFE_OBJECT_OPERATOR, '?->', $line]
|
||||
]);
|
||||
$c--;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle ?-> inside encapsed string.
|
||||
if ($tokens[$i][0] === \T_ENCAPSED_AND_WHITESPACE && isset($tokens[$i - 1])
|
||||
&& $tokens[$i - 1][0] === \T_VARIABLE
|
||||
&& preg_match('/^\?->([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)/', $tokens[$i][1], $matches)
|
||||
) {
|
||||
$replacement = [
|
||||
[\T_NULLSAFE_OBJECT_OPERATOR, '?->', $line],
|
||||
[\T_STRING, $matches[1], $line],
|
||||
];
|
||||
if (\strlen($matches[0]) !== \strlen($tokens[$i][1])) {
|
||||
$replacement[] = [
|
||||
\T_ENCAPSED_AND_WHITESPACE,
|
||||
\substr($tokens[$i][1], \strlen($matches[0])),
|
||||
$line
|
||||
];
|
||||
}
|
||||
array_splice($tokens, $i, 1, $replacement);
|
||||
$c += \count($replacement) - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (\is_array($tokens[$i])) {
|
||||
$line += substr_count($tokens[$i][1], "\n");
|
||||
}
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
public function reverseEmulate(string $code, array $tokens): array
|
||||
{
|
||||
// ?-> was not valid code previously, don't bother.
|
||||
return $tokens;
|
||||
}
|
||||
}
|
105
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.php
vendored
Normal file
105
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/NumericLiteralSeparatorEmulator.php
vendored
Normal file
|
@ -0,0 +1,105 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\Lexer\Emulative;
|
||||
|
||||
final class NumericLiteralSeparatorEmulator extends TokenEmulator
|
||||
{
|
||||
const BIN = '(?:0b[01]+(?:_[01]+)*)';
|
||||
const HEX = '(?:0x[0-9a-f]+(?:_[0-9a-f]+)*)';
|
||||
const DEC = '(?:[0-9]+(?:_[0-9]+)*)';
|
||||
const SIMPLE_FLOAT = '(?:' . self::DEC . '\.' . self::DEC . '?|\.' . self::DEC . ')';
|
||||
const EXP = '(?:e[+-]?' . self::DEC . ')';
|
||||
const FLOAT = '(?:' . self::SIMPLE_FLOAT . self::EXP . '?|' . self::DEC . self::EXP . ')';
|
||||
const NUMBER = '~' . self::FLOAT . '|' . self::BIN . '|' . self::HEX . '|' . self::DEC . '~iA';
|
||||
|
||||
public function getPhpVersion(): string
|
||||
{
|
||||
return Emulative::PHP_7_4;
|
||||
}
|
||||
|
||||
public function isEmulationNeeded(string $code) : bool
|
||||
{
|
||||
return preg_match('~[0-9]_[0-9]~', $code)
|
||||
|| preg_match('~0x[0-9a-f]+_[0-9a-f]~i', $code);
|
||||
}
|
||||
|
||||
public function emulate(string $code, array $tokens): array
|
||||
{
|
||||
// We need to manually iterate and manage a count because we'll change
|
||||
// the tokens array on the way
|
||||
$codeOffset = 0;
|
||||
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
|
||||
$token = $tokens[$i];
|
||||
$tokenLen = \strlen(\is_array($token) ? $token[1] : $token);
|
||||
|
||||
if ($token[0] !== T_LNUMBER && $token[0] !== T_DNUMBER) {
|
||||
$codeOffset += $tokenLen;
|
||||
continue;
|
||||
}
|
||||
|
||||
$res = preg_match(self::NUMBER, $code, $matches, 0, $codeOffset);
|
||||
assert($res, "No number at number token position");
|
||||
|
||||
$match = $matches[0];
|
||||
$matchLen = \strlen($match);
|
||||
if ($matchLen === $tokenLen) {
|
||||
// Original token already holds the full number.
|
||||
$codeOffset += $tokenLen;
|
||||
continue;
|
||||
}
|
||||
|
||||
$tokenKind = $this->resolveIntegerOrFloatToken($match);
|
||||
$newTokens = [[$tokenKind, $match, $token[2]]];
|
||||
|
||||
$numTokens = 1;
|
||||
$len = $tokenLen;
|
||||
while ($matchLen > $len) {
|
||||
$nextToken = $tokens[$i + $numTokens];
|
||||
$nextTokenText = \is_array($nextToken) ? $nextToken[1] : $nextToken;
|
||||
$nextTokenLen = \strlen($nextTokenText);
|
||||
|
||||
$numTokens++;
|
||||
if ($matchLen < $len + $nextTokenLen) {
|
||||
// Split trailing characters into a partial token.
|
||||
assert(is_array($nextToken), "Partial token should be an array token");
|
||||
$partialText = substr($nextTokenText, $matchLen - $len);
|
||||
$newTokens[] = [$nextToken[0], $partialText, $nextToken[2]];
|
||||
break;
|
||||
}
|
||||
|
||||
$len += $nextTokenLen;
|
||||
}
|
||||
|
||||
array_splice($tokens, $i, $numTokens, $newTokens);
|
||||
$c -= $numTokens - \count($newTokens);
|
||||
$codeOffset += $matchLen;
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
private function resolveIntegerOrFloatToken(string $str): int
|
||||
{
|
||||
$str = str_replace('_', '', $str);
|
||||
|
||||
if (stripos($str, '0b') === 0) {
|
||||
$num = bindec($str);
|
||||
} elseif (stripos($str, '0x') === 0) {
|
||||
$num = hexdec($str);
|
||||
} elseif (stripos($str, '0') === 0 && ctype_digit($str)) {
|
||||
$num = octdec($str);
|
||||
} else {
|
||||
$num = +$str;
|
||||
}
|
||||
|
||||
return is_float($num) ? T_DNUMBER : T_LNUMBER;
|
||||
}
|
||||
|
||||
public function reverseEmulate(string $code, array $tokens): array
|
||||
{
|
||||
// Numeric separators were not legal code previously, don't bother.
|
||||
return $tokens;
|
||||
}
|
||||
}
|
31
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.php
vendored
Normal file
31
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyFunctionTokenEmulator.php
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\Lexer\Emulative;
|
||||
|
||||
/*
|
||||
* In PHP 8.1, "readonly(" was special cased in the lexer in order to support functions with
|
||||
* name readonly. In PHP 8.2, this may conflict with readonly properties having a DNF type. For
|
||||
* this reason, PHP 8.2 instead treats this as T_READONLY and then handles it specially in the
|
||||
* parser. This emulator only exists to handle this special case, which is skipped by the
|
||||
* PHP 8.1 ReadonlyTokenEmulator.
|
||||
*/
|
||||
class ReadonlyFunctionTokenEmulator extends KeywordEmulator {
|
||||
public function getKeywordString(): string {
|
||||
return 'readonly';
|
||||
}
|
||||
|
||||
public function getKeywordToken(): int {
|
||||
return \T_READONLY;
|
||||
}
|
||||
|
||||
public function getPhpVersion(): string {
|
||||
return Emulative::PHP_8_2;
|
||||
}
|
||||
|
||||
public function reverseEmulate(string $code, array $tokens): array {
|
||||
// Don't bother
|
||||
return $tokens;
|
||||
}
|
||||
}
|
36
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php
vendored
Normal file
36
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReadonlyTokenEmulator.php
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
use PhpParser\Lexer\Emulative;
|
||||
|
||||
final class ReadonlyTokenEmulator extends KeywordEmulator
|
||||
{
|
||||
public function getPhpVersion(): string
|
||||
{
|
||||
return Emulative::PHP_8_1;
|
||||
}
|
||||
|
||||
public function getKeywordString(): string
|
||||
{
|
||||
return 'readonly';
|
||||
}
|
||||
|
||||
public function getKeywordToken(): int
|
||||
{
|
||||
return \T_READONLY;
|
||||
}
|
||||
|
||||
protected function isKeywordContext(array $tokens, int $pos): bool
|
||||
{
|
||||
if (!parent::isKeywordContext($tokens, $pos)) {
|
||||
return false;
|
||||
}
|
||||
// Support "function readonly("
|
||||
return !(isset($tokens[$pos + 1]) &&
|
||||
($tokens[$pos + 1][0] === '(' ||
|
||||
($tokens[$pos + 1][0] === \T_WHITESPACE &&
|
||||
isset($tokens[$pos + 2]) &&
|
||||
$tokens[$pos + 2][0] === '(')));
|
||||
}
|
||||
}
|
36
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php
vendored
Normal file
36
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/ReverseEmulator.php
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
/**
|
||||
* Reverses emulation direction of the inner emulator.
|
||||
*/
|
||||
final class ReverseEmulator extends TokenEmulator
|
||||
{
|
||||
/** @var TokenEmulator Inner emulator */
|
||||
private $emulator;
|
||||
|
||||
public function __construct(TokenEmulator $emulator) {
|
||||
$this->emulator = $emulator;
|
||||
}
|
||||
|
||||
public function getPhpVersion(): string {
|
||||
return $this->emulator->getPhpVersion();
|
||||
}
|
||||
|
||||
public function isEmulationNeeded(string $code): bool {
|
||||
return $this->emulator->isEmulationNeeded($code);
|
||||
}
|
||||
|
||||
public function emulate(string $code, array $tokens): array {
|
||||
return $this->emulator->reverseEmulate($code, $tokens);
|
||||
}
|
||||
|
||||
public function reverseEmulate(string $code, array $tokens): array {
|
||||
return $this->emulator->emulate($code, $tokens);
|
||||
}
|
||||
|
||||
public function preprocessCode(string $code, array &$patches): string {
|
||||
return $code;
|
||||
}
|
||||
}
|
25
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/TokenEmulator.php
vendored
Normal file
25
vendor/nikic/php-parser/lib/PhpParser/Lexer/TokenEmulator/TokenEmulator.php
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser\Lexer\TokenEmulator;
|
||||
|
||||
/** @internal */
|
||||
abstract class TokenEmulator
|
||||
{
|
||||
abstract public function getPhpVersion(): string;
|
||||
|
||||
abstract public function isEmulationNeeded(string $code): bool;
|
||||
|
||||
/**
|
||||
* @return array Modified Tokens
|
||||
*/
|
||||
abstract public function emulate(string $code, array $tokens): array;
|
||||
|
||||
/**
|
||||
* @return array Modified Tokens
|
||||
*/
|
||||
abstract public function reverseEmulate(string $code, array $tokens): array;
|
||||
|
||||
public function preprocessCode(string $code, array &$patches): string {
|
||||
return $code;
|
||||
}
|
||||
}
|
285
vendor/nikic/php-parser/lib/PhpParser/NameContext.php
vendored
Normal file
285
vendor/nikic/php-parser/lib/PhpParser/NameContext.php
vendored
Normal file
|
@ -0,0 +1,285 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser;
|
||||
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Stmt;
|
||||
|
||||
class NameContext
|
||||
{
|
||||
/** @var null|Name Current namespace */
|
||||
protected $namespace;
|
||||
|
||||
/** @var Name[][] Map of format [aliasType => [aliasName => originalName]] */
|
||||
protected $aliases = [];
|
||||
|
||||
/** @var Name[][] Same as $aliases but preserving original case */
|
||||
protected $origAliases = [];
|
||||
|
||||
/** @var ErrorHandler Error handler */
|
||||
protected $errorHandler;
|
||||
|
||||
/**
|
||||
* Create a name context.
|
||||
*
|
||||
* @param ErrorHandler $errorHandler Error handling used to report errors
|
||||
*/
|
||||
public function __construct(ErrorHandler $errorHandler) {
|
||||
$this->errorHandler = $errorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new namespace.
|
||||
*
|
||||
* This also resets the alias table.
|
||||
*
|
||||
* @param Name|null $namespace Null is the global namespace
|
||||
*/
|
||||
public function startNamespace(Name $namespace = null) {
|
||||
$this->namespace = $namespace;
|
||||
$this->origAliases = $this->aliases = [
|
||||
Stmt\Use_::TYPE_NORMAL => [],
|
||||
Stmt\Use_::TYPE_FUNCTION => [],
|
||||
Stmt\Use_::TYPE_CONSTANT => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an alias / import.
|
||||
*
|
||||
* @param Name $name Original name
|
||||
* @param string $aliasName Aliased name
|
||||
* @param int $type One of Stmt\Use_::TYPE_*
|
||||
* @param array $errorAttrs Attributes to use to report an error
|
||||
*/
|
||||
public function addAlias(Name $name, string $aliasName, int $type, array $errorAttrs = []) {
|
||||
// Constant names are case sensitive, everything else case insensitive
|
||||
if ($type === Stmt\Use_::TYPE_CONSTANT) {
|
||||
$aliasLookupName = $aliasName;
|
||||
} else {
|
||||
$aliasLookupName = strtolower($aliasName);
|
||||
}
|
||||
|
||||
if (isset($this->aliases[$type][$aliasLookupName])) {
|
||||
$typeStringMap = [
|
||||
Stmt\Use_::TYPE_NORMAL => '',
|
||||
Stmt\Use_::TYPE_FUNCTION => 'function ',
|
||||
Stmt\Use_::TYPE_CONSTANT => 'const ',
|
||||
];
|
||||
|
||||
$this->errorHandler->handleError(new Error(
|
||||
sprintf(
|
||||
'Cannot use %s%s as %s because the name is already in use',
|
||||
$typeStringMap[$type], $name, $aliasName
|
||||
),
|
||||
$errorAttrs
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
$this->aliases[$type][$aliasLookupName] = $name;
|
||||
$this->origAliases[$type][$aliasName] = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current namespace.
|
||||
*
|
||||
* @return null|Name Namespace (or null if global namespace)
|
||||
*/
|
||||
public function getNamespace() {
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get resolved name.
|
||||
*
|
||||
* @param Name $name Name to resolve
|
||||
* @param int $type One of Stmt\Use_::TYPE_{FUNCTION|CONSTANT}
|
||||
*
|
||||
* @return null|Name Resolved name, or null if static resolution is not possible
|
||||
*/
|
||||
public function getResolvedName(Name $name, int $type) {
|
||||
// don't resolve special class names
|
||||
if ($type === Stmt\Use_::TYPE_NORMAL && $name->isSpecialClassName()) {
|
||||
if (!$name->isUnqualified()) {
|
||||
$this->errorHandler->handleError(new Error(
|
||||
sprintf("'\\%s' is an invalid class name", $name->toString()),
|
||||
$name->getAttributes()
|
||||
));
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
// fully qualified names are already resolved
|
||||
if ($name->isFullyQualified()) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
// Try to resolve aliases
|
||||
if (null !== $resolvedName = $this->resolveAlias($name, $type)) {
|
||||
return $resolvedName;
|
||||
}
|
||||
|
||||
if ($type !== Stmt\Use_::TYPE_NORMAL && $name->isUnqualified()) {
|
||||
if (null === $this->namespace) {
|
||||
// outside of a namespace unaliased unqualified is same as fully qualified
|
||||
return new FullyQualified($name, $name->getAttributes());
|
||||
}
|
||||
|
||||
// Cannot resolve statically
|
||||
return null;
|
||||
}
|
||||
|
||||
// if no alias exists prepend current namespace
|
||||
return FullyQualified::concat($this->namespace, $name, $name->getAttributes());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get resolved class name.
|
||||
*
|
||||
* @param Name $name Class ame to resolve
|
||||
*
|
||||
* @return Name Resolved name
|
||||
*/
|
||||
public function getResolvedClassName(Name $name) : Name {
|
||||
return $this->getResolvedName($name, Stmt\Use_::TYPE_NORMAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get possible ways of writing a fully qualified name (e.g., by making use of aliases).
|
||||
*
|
||||
* @param string $name Fully-qualified name (without leading namespace separator)
|
||||
* @param int $type One of Stmt\Use_::TYPE_*
|
||||
*
|
||||
* @return Name[] Possible representations of the name
|
||||
*/
|
||||
public function getPossibleNames(string $name, int $type) : array {
|
||||
$lcName = strtolower($name);
|
||||
|
||||
if ($type === Stmt\Use_::TYPE_NORMAL) {
|
||||
// self, parent and static must always be unqualified
|
||||
if ($lcName === "self" || $lcName === "parent" || $lcName === "static") {
|
||||
return [new Name($name)];
|
||||
}
|
||||
}
|
||||
|
||||
// Collect possible ways to write this name, starting with the fully-qualified name
|
||||
$possibleNames = [new FullyQualified($name)];
|
||||
|
||||
if (null !== $nsRelativeName = $this->getNamespaceRelativeName($name, $lcName, $type)) {
|
||||
// Make sure there is no alias that makes the normally namespace-relative name
|
||||
// into something else
|
||||
if (null === $this->resolveAlias($nsRelativeName, $type)) {
|
||||
$possibleNames[] = $nsRelativeName;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for relevant namespace use statements
|
||||
foreach ($this->origAliases[Stmt\Use_::TYPE_NORMAL] as $alias => $orig) {
|
||||
$lcOrig = $orig->toLowerString();
|
||||
if (0 === strpos($lcName, $lcOrig . '\\')) {
|
||||
$possibleNames[] = new Name($alias . substr($name, strlen($lcOrig)));
|
||||
}
|
||||
}
|
||||
|
||||
// Check for relevant type-specific use statements
|
||||
foreach ($this->origAliases[$type] as $alias => $orig) {
|
||||
if ($type === Stmt\Use_::TYPE_CONSTANT) {
|
||||
// Constants are are complicated-sensitive
|
||||
$normalizedOrig = $this->normalizeConstName($orig->toString());
|
||||
if ($normalizedOrig === $this->normalizeConstName($name)) {
|
||||
$possibleNames[] = new Name($alias);
|
||||
}
|
||||
} else {
|
||||
// Everything else is case-insensitive
|
||||
if ($orig->toLowerString() === $lcName) {
|
||||
$possibleNames[] = new Name($alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $possibleNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get shortest representation of this fully-qualified name.
|
||||
*
|
||||
* @param string $name Fully-qualified name (without leading namespace separator)
|
||||
* @param int $type One of Stmt\Use_::TYPE_*
|
||||
*
|
||||
* @return Name Shortest representation
|
||||
*/
|
||||
public function getShortName(string $name, int $type) : Name {
|
||||
$possibleNames = $this->getPossibleNames($name, $type);
|
||||
|
||||
// Find shortest name
|
||||
$shortestName = null;
|
||||
$shortestLength = \INF;
|
||||
foreach ($possibleNames as $possibleName) {
|
||||
$length = strlen($possibleName->toCodeString());
|
||||
if ($length < $shortestLength) {
|
||||
$shortestName = $possibleName;
|
||||
$shortestLength = $length;
|
||||
}
|
||||
}
|
||||
|
||||
return $shortestName;
|
||||
}
|
||||
|
||||
private function resolveAlias(Name $name, $type) {
|
||||
$firstPart = $name->getFirst();
|
||||
|
||||
if ($name->isQualified()) {
|
||||
// resolve aliases for qualified names, always against class alias table
|
||||
$checkName = strtolower($firstPart);
|
||||
if (isset($this->aliases[Stmt\Use_::TYPE_NORMAL][$checkName])) {
|
||||
$alias = $this->aliases[Stmt\Use_::TYPE_NORMAL][$checkName];
|
||||
return FullyQualified::concat($alias, $name->slice(1), $name->getAttributes());
|
||||
}
|
||||
} elseif ($name->isUnqualified()) {
|
||||
// constant aliases are case-sensitive, function aliases case-insensitive
|
||||
$checkName = $type === Stmt\Use_::TYPE_CONSTANT ? $firstPart : strtolower($firstPart);
|
||||
if (isset($this->aliases[$type][$checkName])) {
|
||||
// resolve unqualified aliases
|
||||
return new FullyQualified($this->aliases[$type][$checkName], $name->getAttributes());
|
||||
}
|
||||
}
|
||||
|
||||
// No applicable aliases
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getNamespaceRelativeName(string $name, string $lcName, int $type) {
|
||||
if (null === $this->namespace) {
|
||||
return new Name($name);
|
||||
}
|
||||
|
||||
if ($type === Stmt\Use_::TYPE_CONSTANT) {
|
||||
// The constants true/false/null always resolve to the global symbols, even inside a
|
||||
// namespace, so they may be used without qualification
|
||||
if ($lcName === "true" || $lcName === "false" || $lcName === "null") {
|
||||
return new Name($name);
|
||||
}
|
||||
}
|
||||
|
||||
$namespacePrefix = strtolower($this->namespace . '\\');
|
||||
if (0 === strpos($lcName, $namespacePrefix)) {
|
||||
return new Name(substr($name, strlen($namespacePrefix)));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function normalizeConstName(string $name) {
|
||||
$nsSep = strrpos($name, '\\');
|
||||
if (false === $nsSep) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
// Constants have case-insensitive namespace and case-sensitive short-name
|
||||
$ns = substr($name, 0, $nsSep);
|
||||
$shortName = substr($name, $nsSep + 1);
|
||||
return strtolower($ns) . '\\' . $shortName;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue