├── .gitignore ├── resources └── cover.png ├── phpstan.neon ├── src ├── InjectableVersion.php ├── CheckInjectableVersion.php ├── Override.php ├── NamespaceVisibility.php ├── Package.php ├── TestTag.php ├── Sealed.php ├── Friend.php ├── RestrictTraitTo.php └── MustUseResult.php ├── examples ├── sealed │ ├── sealedClasses.php │ └── sealedInterfaces.php ├── testTag │ ├── testTagOnConstructorIgnoredInTestClass.php │ ├── testTagClassOnConstructorIgnoredInTestClass.php │ ├── testTagOnStaticMethodIgnoredInTestClass.php │ ├── testTagClassOnStaticMethodIgnoredInTestClass.php │ ├── testTagClassOnMethodIgnoredInTestClass.php │ ├── testTagOnMethodIgnoredInTestClass.php │ ├── testTagOnMethod.php │ ├── testTagOnConstructor.php │ ├── testTagOnStaticMethod.php │ ├── testTagClassOnMethod.php │ ├── testTagOnStaticMethodIgnoredInTestNamespace.php │ ├── testTagOnConstructorIgnoredInTestNamespace.php │ ├── testTagOnMethodIgnoredInTestNamespace.php │ ├── testTagClassOnConstructor.php │ └── testTagClassOnStaticMethod.php ├── friend │ ├── friendRulesIgnoredForTestClass.php │ ├── friendOnDifferentNamespace.php │ ├── multipleFriends.php │ ├── friendRulesIgnoredForTestNamespace.php │ ├── friendOnConstructor.php │ ├── friendOnClass.php │ ├── friendOnStaticMethod.php │ └── friendOnMethod.php ├── injectableVersion │ ├── InjectableVersionOnInterface.php │ ├── InjectableVersionOnExtendsThenImplements.php │ ├── InjectableVersionOnClass.php │ ├── InjectableVersionRulesIgnoredForTestNamespace.php │ ├── MultipleLevelsOfInheritanceNoInjectableVersionOnClass.php │ ├── MultipleLevelsOfInheritanceNoInjectableVersionOnInterface.php │ ├── IterableInjectableVersion.php │ ├── MultipleLevelsOfInheritanceInjectableVersionOnInterface.php │ └── InjectableVersionCheckOnMethod.php ├── mustUseResult │ ├── mustUseResultOnStaticMethod.php │ └── mustUseResultOnMethod.php ├── package │ ├── packageRulesIgnoredForTestClass.php │ ├── packageRulesIgnoredForTestNamespace.php │ ├── packageOnConstructor.php │ ├── packageOnMethod.php │ ├── packageOnStaticMethod.php │ └── packageOnClass.php ├── namespaceVisibility │ ├── namespaceVisibilityRulesIgnoredForTestClass.php │ ├── namespaceVisibilityRulesIgnoredForTestNamespace.php │ ├── namespaceVisibilityOnStaticMethodExcludeSubNamespaces.php │ ├── namespaceVisibilityOnConstructorForDifferentNamespaces.php │ ├── namespaceVisibilityOnStaticMethodForDifferentNamespaces.php │ ├── namespaceVisibilityOnMethodForDifferentNamespace.php │ ├── namespaceVisibilityOnClassForDifferentNamespaces.php │ ├── namespaceVisibilityOnClassExcludeSubNamespaces.php │ ├── namespaceVisibilityOnMethodExcludeSubNamespace.php │ ├── namespaceVisibilityOnConstructorExcludeSubNamespaces.php │ ├── namespaceVisibilityOnConstructor.php │ ├── namespaceVisibilityOnMethod.php │ ├── namespaceVisibilityOnStaticMethod.php │ └── namespaceVisibilityOnClass.php ├── override │ ├── overrideOnClass.php │ ├── overrideOnInterface.php │ └── overrideRfcExamples.php └── restrictTraitTo │ └── restrictTraitTo.php ├── psalm.xml ├── .php-cs-fixer.php ├── LICENSE.md ├── composer.json ├── .github └── workflows │ └── full-checks.yml ├── CONTRIBUTING.md ├── README.md └── composer.lock /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .php-cs-fixer.cache 3 | .vagrant 4 | Vagrantfile 5 | vendor/ 6 | -------------------------------------------------------------------------------- /resources/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DaveLiddament/php-language-extensions/HEAD/resources/cover.png -------------------------------------------------------------------------------- /phpstan.neon: -------------------------------------------------------------------------------- 1 | parameters: 2 | level: max 3 | paths: 4 | - src 5 | 6 | ignoreErrors: 7 | - '#^Constructor of class DaveLiddament\\PhpLanguageExtensions\\[a-zA-Z]+ has an unused parameter \$[a-zA-Z]+\.$#' 8 | -------------------------------------------------------------------------------- /src/InjectableVersion.php: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/testTag/testTagClassOnMethodIgnoredInTestClass.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK - Called from Test class 22 | } 23 | } 24 | 25 | 26 | -------------------------------------------------------------------------------- /examples/testTag/testTagOnMethodIgnoredInTestClass.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK - Called from Test class 23 | } 24 | } 25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/friend/friendRulesIgnoredForTestClass.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK: calling from a class with a name ending Test 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/injectableVersion/InjectableVersionOnInterface.php: -------------------------------------------------------------------------------- 1 | updateName(); // ERROR 19 | } 20 | } 21 | 22 | class Updater 23 | { 24 | public function updater(Person $person): void 25 | { 26 | $person->updateName(); // ERROR 27 | } 28 | } 29 | 30 | $person = new Person(); 31 | $person->updateName(); // ERROR 32 | 33 | -------------------------------------------------------------------------------- /examples/testTag/testTagOnConstructor.php: -------------------------------------------------------------------------------- 1 | update(); // OK: Builder a friend of Employee 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/testTag/testTagOnStaticMethod.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK - whole class is marked with TestTag, so OK to call methods within it. 19 | } 20 | } 21 | 22 | class Updater 23 | { 24 | public function updater(Person $person): void 25 | { 26 | $person->updateName(); // ERROR 27 | } 28 | } 29 | 30 | $person = new Person(); 31 | $person->updateName(); // ERROR 32 | 33 | -------------------------------------------------------------------------------- /examples/testTag/testTagOnStaticMethodIgnoredInTestNamespace.php: -------------------------------------------------------------------------------- 1 | dontNeedToUseResult(); // OK 27 | 28 | $class->mustUseResult(); // ERROR 29 | 30 | echo $class->mustUseResult(); // OK; 31 | 32 | $value = 1 + $class->mustUseResult(); // OK 33 | } -------------------------------------------------------------------------------- /examples/testTag/testTagOnConstructorIgnoredInTestNamespace.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK: calling from a class with a name ending Test 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /examples/testTag/testTagOnMethodIgnoredInTestNamespace.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK - Called from Test namespace 28 | } 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /examples/package/packageRulesIgnoredForTestNamespace.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK: calling from a test namespace 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /examples/friend/multipleFriends.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK: FriendUpdater a friend of Person::updateName 22 | } 23 | } 24 | 25 | class AnotherFriendUpdater 26 | { 27 | public function update(Person $person): void 28 | { 29 | $person->updateName(); // OK: AnotherFriendUpdater a friend of Person::updateName 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/injectableVersion/InjectableVersionOnExtendsThenImplements.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK: calling from a class with a name ending Test 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /examples/injectableVersion/InjectableVersionOnClass.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK: calling from a test namespace 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /examples/namespaceVisibility/namespaceVisibilityRulesIgnoredForTestNamespace.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK: calling from a test namespace 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /examples/injectableVersion/MultipleLevelsOfInheritanceNoInjectableVersionOnClass.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK 20 | } 21 | } 22 | 23 | class Updater 24 | { 25 | public function updater(Person $person): void 26 | { 27 | $person->updateName(); // ERROR: Updater is not a Friend of Person 28 | } 29 | } 30 | 31 | $person = new Person(); 32 | $person->updateName(); // ERROR 33 | 34 | class FriendUpdater 35 | { 36 | public function update(): void 37 | { 38 | $person = new Person(); 39 | $person->updateName(); // OK 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/friend/friendOnStaticMethod.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK, call to Person::updateName from allowed namespace. 29 | } 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /examples/injectableVersion/IterableInjectableVersion.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK: Call to Person::updateName is in the allowed namespace. 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/namespaceVisibility/namespaceVisibilityOnClassExcludeSubNamespaces.php: -------------------------------------------------------------------------------- 1 | updateName(); // ERROR: Call to Person::updateName in a sub namespace, where sub namespace is not allowed. 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.php-cs-fixer.php: -------------------------------------------------------------------------------- 1 | in(__DIR__ . '/src'); 5 | 6 | $config = new PhpCsFixer\Config(); 7 | return $config 8 | ->setRiskyAllowed(true) 9 | ->setRules( 10 | [ 11 | '@PSR1' => true, 12 | '@PSR2' => true, 13 | '@PSR12' => true, 14 | '@Symfony' => true, 15 | '@Symfony:risky' => true, 16 | 'array_syntax' => ['syntax' => 'short'], 17 | 'no_useless_else' => true, 18 | 'no_useless_return' => true, 19 | 'ordered_imports' => true, 20 | 'phpdoc_order' => true, 21 | 'no_unused_imports' => true, 22 | 'strict_comparison' => true, 23 | 'phpdoc_align' => false, 24 | 'phpdoc_to_comment' => false, 25 | 'native_function_invocation' => false, 26 | ] 27 | ) 28 | ->setFinder($finder) 29 | ; 30 | -------------------------------------------------------------------------------- /examples/injectableVersion/MultipleLevelsOfInheritanceInjectableVersionOnInterface.php: -------------------------------------------------------------------------------- 1 | repository = $repository; 27 | } 28 | } 29 | 30 | class InjectingWrongVersion 31 | { 32 | public Repository $repository; 33 | 34 | #[CheckInjectableVersion] 35 | public function setRepository(DoctrineRepository $repository): void // ERROR 36 | { 37 | $this->repository = $repository; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/MustUseResult.php: -------------------------------------------------------------------------------- 1 | pence); 21 | * } 22 | * } 23 | * ``` 24 | * 25 | * You might misuse the `add` method in this way: 26 | * 27 | * ``` 28 | * $cost = new Money(5); 29 | * $cost->add(6); // ERROR - This statement has no effect. 30 | * ``` 31 | * 32 | * But this would be OK: 33 | * 34 | * ``` 35 | * $cost = new Money(5); 36 | * $updatedCost = $cost->add(6); // OK - The return from add method is being used. 37 | * ``` 38 | */ 39 | #[\Attribute(\Attribute::TARGET_METHOD)] 40 | final class MustUseResult 41 | { 42 | } 43 | -------------------------------------------------------------------------------- /examples/namespaceVisibility/namespaceVisibilityOnMethodExcludeSubNamespace.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK 19 | } 20 | } 21 | } 22 | 23 | 24 | 25 | namespace NamespaceVisibilityOnMethodExluceSubNamespace\SubNamespace { 26 | 27 | use NamespaceVisibilityOnMethodExcludeSubNamespace\Person; 28 | class AnotherPersonUpdater 29 | { 30 | public function update(Person $person): void 31 | { 32 | $person->updateName(); // ERROR - Subnamespace of NamespaceVisibilityOnMethod, which is not allowed 33 | } 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /examples/friend/friendOnMethod.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK: Method calls on same class is always allowed 19 | } 20 | } 21 | 22 | class Updater 23 | { 24 | public function updater(Person $person): void 25 | { 26 | $person->updateName(); // ERROR: Updater is not a friend of Person::updateName 27 | } 28 | } 29 | 30 | $person = new Person(); 31 | $person->updateName(); // ERROR: Global namespace is not a friend of Person::updateName 32 | 33 | class FriendUpdater 34 | { 35 | public function update(): void 36 | { 37 | $person = new Person(); 38 | $person->updateName(); // OK: FriendUpdater is a friend of Person::updateName 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /examples/package/packageOnConstructor.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK 19 | } 20 | } 21 | 22 | class Updater 23 | { 24 | public function updater(Person $person): void 25 | { 26 | $person->updateName(); // OK 27 | } 28 | } 29 | 30 | $person = new Person(); 31 | $person->updateName(); // OK 32 | } 33 | 34 | namespace PackageOnMethod2 { 35 | 36 | use PackageOnMethod\Person; 37 | 38 | class AnotherUpdater 39 | { 40 | public function update(): void 41 | { 42 | $person = new Person(); 43 | $person->updateName(); // ERROR: Call to Person::updateName which has package visibility 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /examples/override/overrideOnClass.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK: Calls to same class allowed 20 | } 21 | } 22 | 23 | class Updater 24 | { 25 | public function updater(Person $person): void 26 | { 27 | $person->updateName(); // OK: Calls within same package allowed 28 | } 29 | } 30 | 31 | $person = new Person(); 32 | $person->updateName(); // OK: Calls within same package allowed 33 | 34 | } 35 | 36 | 37 | namespace PackageOnClass2 { 38 | 39 | use PackageOnClass\Person; 40 | 41 | class AnotherUpdater 42 | { 43 | public function update(): void 44 | { 45 | $person = new Person(); 46 | $person->updateName(); // ERROR: Call to Person::update method which has package visibility. 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/namespaceVisibility/namespaceVisibilityOnConstructorExcludeSubNamespaces.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK 19 | } 20 | } 21 | 22 | class Updater 23 | { 24 | public function updater(Person $person): void 25 | { 26 | $person->updateName(); // OK 27 | } 28 | } 29 | 30 | $person = new Person(); 31 | $person->updateName(); // OK 32 | } 33 | 34 | 35 | 36 | namespace NamespaceVisibilityOnMethod\SubNamespace { 37 | 38 | use NamespaceVisibilityOnMethod\Person; 39 | class AnotherPersonUpdater 40 | { 41 | public function update(Person $person): void 42 | { 43 | $person->updateName(); // OK - Subnamespace of NamespaceVisibilityOnMethod, which is allowed 44 | } 45 | } 46 | } 47 | 48 | 49 | namespace NamespaceVisibilityOnMethod2 { 50 | 51 | use NamespaceVisibilityOnMethod\Person; 52 | 53 | class AnotherUpdater 54 | { 55 | public function update(): void 56 | { 57 | $person = new Person(); 58 | $person->updateName(); // ERROR: Call to Person::updateName which has namespaceVisibility visibility 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /examples/namespaceVisibility/namespaceVisibilityOnStaticMethod.php: -------------------------------------------------------------------------------- 1 | updateName(); // OK: Calls to same class allowed 20 | } 21 | } 22 | 23 | class Updater 24 | { 25 | public function updater(Person $person): void 26 | { 27 | $person->updateName(); // OK: Calls within same namespace allowed 28 | } 29 | } 30 | 31 | $person = new Person(); 32 | $person->updateName(); // OK: Calls within same namespace allowed 33 | 34 | } 35 | 36 | 37 | namespace NamespaceVisibilityOnClass\SubNamesapce { 38 | 39 | use NamespaceVisibilityOnClass\Person; 40 | 41 | class AnotherClass 42 | { 43 | public function update(): void 44 | { 45 | $person = new Person(); 46 | $person->updateName(); // OK: Calls within the same subnamespace allowed. 47 | } 48 | } 49 | } 50 | 51 | 52 | namespace NamespaceOnClass2 { 53 | 54 | use PackageOnClass\Person; 55 | 56 | class AnotherUpdater 57 | { 58 | public function update(): void 59 | { 60 | $person = new Person(); 61 | $person->updateName(); // ERROR: Call to Person::update method which has namespace visibility. 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /.github/workflows/full-checks.yml: -------------------------------------------------------------------------------- 1 | name: "Full checks" 2 | 3 | on: 4 | schedule: 5 | - cron: '0 10 * * *' 6 | pull_request: 7 | push: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | full-checks: 13 | name: "Full CI checks for all supported PHP versions" 14 | 15 | runs-on: ${{ matrix.operating-system }} 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | dependencies: 21 | - "locked" 22 | - "highest" 23 | php-version: 24 | - "8.0" 25 | - "8.1" 26 | - "8.2" 27 | - "8.3" 28 | - "8.4" 29 | - "8.5" 30 | operating-system: 31 | - "ubuntu-22.04" 32 | 33 | steps: 34 | - name: "Checkout" 35 | uses: "actions/checkout@v2" 36 | 37 | - name: "Install PHP" 38 | uses: "shivammathur/setup-php@v2" 39 | with: 40 | coverage: Xdebug 41 | php-version: "${{ matrix.php-version }}" 42 | tools: composer 43 | 44 | - name: Get composer cache directory 45 | id: composercache 46 | run: echo "::set-output name=dir::$(composer config cache-files-dir)" 47 | 48 | - name: Cache dependencies 49 | uses: actions/cache@v4 50 | with: 51 | path: ${{ steps.composercache.outputs.dir }} 52 | key: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }}" 53 | restore-keys: "php-${{ matrix.php-version }}-${{ matrix.dependencies }}-${{ hashFiles('**/composer.lock') }}" 54 | 55 | - name: "Install locked dependencies" 56 | if: ${{ matrix.dependencies == 'locked' }} 57 | run: "composer install --no-interaction --no-progress" 58 | 59 | - name: "Install highest dependencies" 60 | if: ${{ matrix.dependencies == 'highest' }} 61 | run: "composer update --no-interaction --no-progress" 62 | 63 | - name: "Full CI" 64 | run: "composer ci" 65 | -------------------------------------------------------------------------------- /examples/override/overrideRfcExamples.php: -------------------------------------------------------------------------------- 1 | `. 52 | E.g. the `#[friend]` attribute examples MUST be in the `examples/friend` directory. 53 | 54 | Each example file MUST be prefixed with the attribute name. E.g. `friendOnClass.php`. 55 | Each example MUST have a unique namespace. This is the name of the file, optionally with a number suffix. 56 | E.g. the file `friendOnClass` can have namespaces `FriendOnClass`, `FriendOnClass1`, `FriendOnClass2`, etc. 57 | 58 | If code in the example is showing an error that should be found by static analysis the line must end with `// ERROR `. 59 | See 5th line in code snippet below: 60 | 61 | ```php 62 | class Updater 63 | { 64 | public function updater(Person $person): void 65 | { 66 | $person->updateName(); // ERROR: Updater is not a Friend of Person 67 | } 68 | } 69 | 70 | ``` 71 | 72 | To explicitly state where code is correct, add the following to the end of the line: `// OK ` 73 | See 10th line in code snippet below: 74 | 75 | ```php 76 | class Person 77 | { 78 | #[Friend(FriendUpdater::class)] 79 | public function updateName(): void 80 | { 81 | } 82 | 83 | public function update(): void 84 | { 85 | $this->updateName(); // OK: Method calls on same class is always allowed 86 | } 87 | } 88 | ``` 89 | 90 | 91 | ### Pre commit 92 | 93 | Prior to committing code please run the following: 94 | 95 | ```shell 96 | composer cs-fix # This fixes any coding style issues 97 | composer ci # This runs all the validation that is run by github actions. 98 | ``` 99 | 100 | ### Updating static analysis plugins 101 | 102 | If your contribution has created or updated an Attribute or a file in [examples](examples) then the static analysis extensions/plugins will also need updating. 103 | The current known implementations are: 104 | - [PHPStan](https://github.com/DaveLiddament/phpstan-php-language-extension) 105 | - [Psalm](https://github.com/DaveLiddament/psalm-php-language-extension) 106 | 107 | If you have the relevant knowledge please make the updates to the appropriate repo. If you don't then please create an issue on the static analysis plugin's repo with details of that needs updating. 108 | 109 | Any updates to files in the [examples](examples) directory in this repository will need copying to the relevant static analysis's implementation repository in its `tests\data` directory. 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Language Extensions (currently in BETA) 2 | 3 | [![PHP versions: 8.0 to 8.5](https://img.shields.io/badge/php-8.0|8.1|8.2|8.3|8.4|8.5-blue.svg)](https://packagist.org/packages/dave-liddament/php-language-extensions) 4 | [![Latest Stable Version](https://poser.pugx.org/dave-liddament/php-language-extensions/v/stable)](https://packagist.org/packages/dave-liddament/php-language-extensions) 5 | [![License](https://poser.pugx.org/dave-liddament/php-language-extensions/license)](https://github.com/DaveLiddament/php-language-extensions/blob/main/LICENSE.md) 6 | [![Total Downloads](https://poser.pugx.org/dave-liddament/php-language-extensions/downloads)](https://packagist.org/packages/dave-liddament/php-language-extensions/stats) 7 | 8 | [![Continuous Integration](https://github.com/DaveLiddament/php-language-extensions/workflows/Full%20checks/badge.svg)](https://github.com/DaveLiddament/php-language-extensions/actions) 9 | [![Psalm level 1](https://img.shields.io/badge/Psalm-max%20level-brightgreen.svg)](https://github.com/DaveLiddament/php-language-extensions/blob/main/psalm.xml) 10 | [![PHPStan level 8](https://img.shields.io/badge/PHPStan-max%20level-brightgreen.svg)](https://github.com/DaveLiddament/php-language-extensions/blob/main/phpstan.neon) 11 | 12 | 13 | This library provides [attributes](https://www.php.net/manual/en/language.attributes.overview.php) that are used by static analysers to enforce new language features. 14 | The intention, at least initially, is that these extra language features are enforced by static analysis tools (such as Psalm, PHPStan and, ideally, PhpStorm) and NOT at runtime. 15 | 16 | **Language feature added:** 17 | - [Friend](#friend) 18 | - [MustUseResult](#mustuseresult) 19 | - [NamespaceVisibility](#namespaceVisibility) 20 | - [InjectableVersion](#injectableVersion) 21 | - [Override](#override) 22 | - [RestrictTraitTo](#restricttraitto) 23 | - [Sealed](#sealed) 24 | - [TestTag](#testtag) 25 | 26 | 27 | ### Contents 28 | 29 | - [Installation](#installation) 30 | - [PHPStan](#phpstan) 31 | - [Psalm](#psalm) 32 | - [New Language Features](#new-language-features) 33 | - [Friend](#friend) 34 | - [MustUseResult](#mustuseresult) 35 | - [NamespaceVisibility](#namespaceVisibility) 36 | - [InjectableVersion](#injectableVersion) 37 | - [Override](#override) 38 | - [RestrictTraitTo](#restricttraitto) 39 | - [Sealed](#sealed) 40 | - [TestTag](#testtag) 41 | - Deprecated 42 | - [Package](#package) replace with [NamespaceVisibility](#namespaceVisibility) 43 | 44 | - [Further examples](#further-examples) 45 | - [Contributing](#contributing) 46 | 47 | 48 | ## Installation 49 | 50 | To make the attributes available for your codebase use: 51 | 52 | ```shell 53 | composer require dave-liddament/php-language-extensions 54 | ``` 55 | 56 | NOTE: This only installs the attributes. A static analysis tool is used to enforce these language extensions. 57 | Use one of these: 58 | 59 | ### PHPStan 60 | 61 | If you're using PHPStan then use [this extension](https://github.com/DaveLiddament/phpstan-php-language-extensions) to enforce the rules. 62 | 63 | ```shell 64 | composer require --dev dave-liddament/phpstan-php-language-extensions 65 | ``` 66 | 67 | ### Psalm 68 | 69 | Coming soon. 70 | 71 | 72 | 73 | 74 | ## New language features 75 | 76 | ## Friend 77 | 78 | A method or class can supply via a `#[Friend]` attribute a list of classes. Only these classes can call the method. 79 | This is loosely based on the C++ friend feature. 80 | 81 | In the example below the `Person::__construct` method can only be called from `PersonBuilder`: 82 | 83 | ```php 84 | 85 | class Person 86 | { 87 | #[Friend(PersonBuilder::class)] 88 | public function __construct() 89 | { 90 | // Some implementation 91 | } 92 | } 93 | 94 | 95 | class PersonBuilder 96 | { 97 | public function build(): Person 98 | { 99 | $person = new Person(): // OK as PersonBuilder is allowed to call Person's construct method. 100 | // set up Person 101 | return $person; 102 | } 103 | } 104 | 105 | 106 | // ERROR Call to Person::__construct is not from PersonBuilder 107 | $person = new Person(); 108 | 109 | ``` 110 | 111 | **NOTES:** 112 | - Multiple classes can be specified. E.g. `#[Friend(Foo::class, Bar::class)]` 113 | - A class can have a `#[Friend]` attribute, classes listed here are applied to every method. 114 | ```php 115 | #[Friend(Foo::class)] 116 | class Entity 117 | { 118 | public function ping(): void // ping has friend Foo 119 | { 120 | } 121 | } 122 | ``` 123 | - The `#[Friend]` attribute is additive. If a class and a method have the `#[Friend]` the method can be called from any of the classes listed. E.g. 124 | ```php 125 | #[Friend(Foo::class)] 126 | class Entity 127 | { 128 | #[Friend(Bar::class)] 129 | public function pong(): void // pong has friends Foo and Bar 130 | { 131 | } 132 | } 133 | ``` 134 | - This is currently limited to method calls (including `__construct`). 135 | 136 | ## MustUseResult 137 | 138 | A `#[MustUseResult]` attribute can be used on methods. This enforces the result from the method call must be used. 139 | 140 | E.g. if you have a class like this: 141 | 142 | ```php 143 | 144 | class Money { 145 | 146 | public function __construct(public readonly int $pence) 147 | {} 148 | 149 | #[MustUseResult] 150 | public function add(int $pence): self 151 | { 152 | return new self($pence + $this->pence); 153 | } 154 | } 155 | ``` 156 | 157 | You might misuse the `add` method in this way: 158 | 159 | ```php 160 | $cost = new Money(5); 161 | $cost->add(6); // ERROR - The call to the add method has no effect. 162 | ``` 163 | 164 | But this would be OK: 165 | 166 | ```php 167 | $cost = new Money(5); 168 | $updatedCost = $cost->add(6); // OK - The return from add method is being used. 169 | ``` 170 | 171 | ## NamespaceVisibility 172 | 173 | The `#[NamespaceVisibility]` attribute acts as extra visibility modifier like `public`, `protected` and `private`. 174 | By default, the `#[NamespaceVisibility]` attribute limits the visibility of a class or method to only being accessible from in the same namespace, or sub namespace. 175 | 176 | Example applying `#[NamespaceVisibility]` to the `Telephone::ring` method: 177 | 178 | ```php 179 | namespace Foo { 180 | 181 | class Telephone 182 | { 183 | #[NamespaceVisibility] 184 | public function ring(): void 185 | { 186 | } 187 | } 188 | 189 | class Ringer 190 | { 191 | public function ring(Telephone $telephone): Person 192 | { 193 | $telephone->ring(); // OK calling Telephone::ring() from same namespace 194 | } 195 | } 196 | } 197 | 198 | namespace Foo\SubNamespace { 199 | 200 | use Foo\Telephone; 201 | 202 | class SubNamespaceRinger 203 | { 204 | public function ring(Telephone $telephone): Person 205 | { 206 | $telephone->ring(); // OK calling Telephone::ring() from sub namespace 207 | } 208 | } 209 | } 210 | 211 | 212 | namespace Bar { 213 | 214 | use Foo\Telephone; 215 | 216 | class DifferentNamespaceRinger 217 | { 218 | public function ring(Telephone $telephone): Person 219 | { 220 | $telephone->ring(); // ERROR calling Telephone::ring() from different namespace 221 | } 222 | } 223 | } 224 | ``` 225 | 226 | The `#[NamespaceVisibility]` attribute has 2 optional arguments: 227 | 228 | #### excludeSubNamespaces option 229 | 230 | This is a boolean value. Its default value is false. 231 | If set to true then calls to methods from sub namespaces are not allowed. 232 | E.g. 233 | 234 | ```php 235 | namespace Foo { 236 | 237 | class Telephone 238 | { 239 | #[NamespaceVisibility(excludeSubNamespaces: true)] 240 | public function ring(): void 241 | { 242 | } 243 | } 244 | 245 | } 246 | 247 | namespace Foo\SubNamespace { 248 | 249 | use Foo\Telephone; 250 | 251 | class SubNamespaceRinger 252 | { 253 | public function ring(Telephone $telephone): Person 254 | { 255 | $telephone->ring(); // ERROR - Not allowed to call Telephone::ring() from a sub namespace 256 | } 257 | } 258 | } 259 | ``` 260 | 261 | #### namespace option 262 | 263 | This is a string or null value. Its default value is null. 264 | If it is set, then this is the namespace that you are allowed to call the method on. 265 | 266 | In the example below you can only call `Telephone::ring` from the `Bar` namespace. 267 | 268 | 269 | ```php 270 | namespace Foo { 271 | 272 | class Telephone 273 | { 274 | #[NamespaceVisibility(namespace: "Bar")] 275 | public function ring(): void 276 | { 277 | } 278 | } 279 | 280 | class Ringer 281 | { 282 | public function ring(Telephone $telephone): void 283 | { 284 | $telephone->ring(); // ERROR - Can only all Telephone::ring() from namespace Bar 285 | } 286 | } 287 | } 288 | 289 | namespace Bar { 290 | 291 | use Foo\Telephone; 292 | 293 | class AnotherRinger 294 | { 295 | public function ring(Telephone $telephone): void 296 | { 297 | $telephone->ring(); // OK - Allowed to call Telephone::ring() from namespace Bar 298 | } 299 | } 300 | } 301 | ``` 302 | 303 | #### NamespaceVisibility on classes 304 | 305 | If a class was the `#[NamespaceVisibility]` Attribute, then all its public methods are treated as Namespace visibility. 306 | 307 | E.g. 308 | 309 | ```php 310 | namespace Foo { 311 | 312 | #[NamespaceVisibility()] 313 | class Telephone 314 | { 315 | public function ring(): void // This method has NamespaceVisibility 316 | { } 317 | } 318 | } 319 | ``` 320 | 321 | If both the class and one of the class's methods has a `#[NamespaceVisibility]` attribute, then the method's attribute 322 | takes precedence. 323 | 324 | ```php 325 | namespace Foo { 326 | 327 | #[NamespaceVisibility(namespace: 'Bar')] 328 | class Telephone 329 | { 330 | #[NamespaceVisibility(namespace: 'Baz')] 331 | public function ring(): void // This method can only be called from the namespace Baz 332 | { } 333 | } 334 | } 335 | ``` 336 | 337 | 338 | #### NOTES: 339 | 340 | - If adding the `#[NamespaceVisibility]` to a method, this method MUST have public visibility. 341 | - This is currently limited to method calls (including `__construct`). 342 | 343 | 344 | 345 | ## InjectableVersion 346 | 347 | The `#[InjectableVersion]` is used in conjunction with dependency injection. 348 | `#[InjectableVersion]` is applied to a class or interface. 349 | It denotes that it is this version and not any classes that implement/extend that should be used in the codebase. 350 | 351 | E.g. 352 | 353 | ```php 354 | 355 | #[InjectableVersion] 356 | class PersonRepository {...} // This is the version that should be type hinted in constructors. 357 | 358 | class DoctrinePersonRepository extends PersonRepository {...} 359 | 360 | class PersonCreator { 361 | public function __construct( 362 | private PersonRepository $personRepository, // OK - using the injectable version 363 | ) 364 | } 365 | class PersonUpdater { 366 | public function __construct( 367 | private DoctrinePersonRepository $personRepository, // ERROR - not using the InjectableVersion 368 | ) 369 | } 370 | ``` 371 | 372 | This also works for collections: 373 | 374 | ```php 375 | 376 | #[InjectableVersion] 377 | interface Validator {...} // This is the version that should be type hinted in constructors. 378 | 379 | class NameValidator implements Validator {...} 380 | class AddressValidator implements Validator {...} 381 | 382 | class PersonValidator { 383 | /** @param Validator[] $validators */ 384 | public function __construct( 385 | private array $validators, // OK - using the injectable version 386 | ) 387 | } 388 | ``` 389 | 390 | By default, only constructor arguments are checked. Most DI should be done via constructor injection. 391 | 392 | In cases where dependencies are injected by methods that aren't constructors, the method must be marked with a `#[CheckInjectableVersion]`: 393 | 394 | ```php 395 | 396 | #[InjectableVersion] 397 | interface Logger {...} 398 | 399 | class FileLogger implements Logger {...} 400 | 401 | class MyService 402 | { 403 | #[CheckInjectableVersion] 404 | public function setLogger(Logger $logger): void {} // OK - Injectable Version injected 405 | 406 | public function addLogger(FileLogger $logger): void {} // No issue raised because addLogger doesn't have the #[CheckInjectableVersion] attribute. 407 | } 408 | 409 | ``` 410 | 411 | ## Override 412 | 413 | The `#[Override]` attribute is used to denote that a method is overriding a method in a parent class. This is the functionality is similar to the `@override` annotation in Java. 414 | 415 | This is temporary until PHP 8.3 is released. See the [RFC](https://wiki.php.net/rfc/marking_overriden_methods) that will be implemented in PHP 8.3. 416 | 417 | NOTE: 418 | 419 | - If you are using PHP 8.3 then use the real `#[Override]` attribute. 420 | - This implementation doesn't consider traits. 421 | 422 | ## RestrictTraitTo 423 | 424 | This limits the use of a Trait to only be used by a specified class of a child of that class. 425 | 426 | E.g. this trait is limited to classes that are or extend `Controller` 427 | 428 | ```php 429 | #[RestrictTraitTo(Controller::class)] 430 | trait ControllerHelpers {} 431 | ``` 432 | 433 | This would be allowed: 434 | ```php 435 | class LoginController extends Controller { 436 | use ControllerHelpers; 437 | } 438 | ``` 439 | 440 | But this would NOT be allowed: 441 | ```php 442 | class Repository { 443 | use ControllerHelpers; 444 | } 445 | ``` 446 | 447 | 448 | ## Sealed 449 | 450 | This is inspired by the rejected [sealed classes RFC](https://wiki.php.net/rfc/sealed_classes) 451 | 452 | The `#[Sealed]` attribute takes a list of classes or interfaces that can extend/implement the class/interface. 453 | 454 | E.g. 455 | 456 | ```php 457 | 458 | #[Sealed([Success::class, Failure::class])] 459 | abstract class Result {} // Result can only be extended by Success or Failure 460 | 461 | // OK 462 | class Success extends Result {} 463 | 464 | // OK 465 | class Failure extends Result {} 466 | 467 | // ERROR AnotherClass is not allowed to extend Result 468 | class AnotherClass extends Result {} 469 | ``` 470 | 471 | 472 | ## TestTag 473 | 474 | The `#[TestTag]` attribute is an idea borrowed from hardware testing. Classes or methods marked with this attribute are only available to test code. 475 | 476 | E.g. 477 | 478 | ```php 479 | class Person { 480 | 481 | #[TestTag] 482 | public function setId(int $id) 483 | { 484 | $this->id = $id; 485 | } 486 | } 487 | 488 | 489 | function updatePersonId(Person $person): void 490 | { 491 | $person->setId(10); // ERROR - not test code. 492 | } 493 | 494 | 495 | class PersonTest 496 | { 497 | public function setup(): void 498 | { 499 | $person = new Person(); 500 | $person->setId(10); // OK - This is test code. 501 | } 502 | } 503 | ``` 504 | 505 | NOTES: 506 | - Classes with the `#[TestTag]` will have an error when any interaction with the class is done. 507 | - Methods with the `#[TestTag]` MUST have public visibility. 508 | - For determining what is "test code" see the relevant plugin. E.g. the [PHPStan extension](https://github.com/DaveLiddament/phpstan-php-language-extensions) can be setup to either: 509 | - Assume all classes that end `Test` is test code. See [className config option](https://github.com/DaveLiddament/phpstan-php-language-extensions#exclude-checks-on-class-names-ending-with-test). 510 | - Assume all classes within a given namespace is test code. See [namespace config option](https://github.com/DaveLiddament/phpstan-php-language-extensions#exclude-checks-based-on-test-namespace). 511 | 512 | 513 | ## Deprecated Attributes 514 | 515 | ### Package (deprecated) 516 | 517 | The `#[Package]` attribute acts like an extra visibility modifier like `public`, `protected` and `private`. It is inspired by Java's `package` visibility modifier. 518 | The `#[Package]` attribute limits the visibility of a class or method to only being accessible from code in the same namespace. 519 | 520 | This has been replaced by the `#[NamespaceVisibility]` attribute. To upgrade replace: 521 | 522 | `#[Package]` with `#[NamespaceVisibility(excludeSubNamespaces=true)]` 523 | 524 | 525 | **NOTES:** 526 | 527 | - If adding the `#[Package]` to a method, this method MUST have public visibility. 528 | - If a class is marked with `#[Package]` then all its public methods are treated as having package visibility. 529 | - This is currently limited to method calls (including `__construct`). 530 | - Namespaces must match exactly. E.g. a package level method in `Foo\Bar` is only accessible from `Foo\Bar`. It is not accessible from `Foo` or `Foo\Bar\Baz` 531 | 532 | 533 | 534 | 535 | ## Further examples 536 | 537 | More detailed examples of how to use attributes is found in [examples](examples/). 538 | 539 | 540 | ## Contributing 541 | 542 | See [Contributing](CONTRIBUTING.md). 543 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "eafc029cb42c297b53caada885dfcca3", 8 | "packages": [], 9 | "packages-dev": [ 10 | { 11 | "name": "composer/pcre", 12 | "version": "3.1.0", 13 | "source": { 14 | "type": "git", 15 | "url": "https://github.com/composer/pcre.git", 16 | "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" 17 | }, 18 | "dist": { 19 | "type": "zip", 20 | "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", 21 | "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", 22 | "shasum": "" 23 | }, 24 | "require": { 25 | "php": "^7.4 || ^8.0" 26 | }, 27 | "require-dev": { 28 | "phpstan/phpstan": "^1.3", 29 | "phpstan/phpstan-strict-rules": "^1.1", 30 | "symfony/phpunit-bridge": "^5" 31 | }, 32 | "type": "library", 33 | "extra": { 34 | "branch-alias": { 35 | "dev-main": "3.x-dev" 36 | } 37 | }, 38 | "autoload": { 39 | "psr-4": { 40 | "Composer\\Pcre\\": "src" 41 | } 42 | }, 43 | "notification-url": "https://packagist.org/downloads/", 44 | "license": [ 45 | "MIT" 46 | ], 47 | "authors": [ 48 | { 49 | "name": "Jordi Boggiano", 50 | "email": "j.boggiano@seld.be", 51 | "homepage": "http://seld.be" 52 | } 53 | ], 54 | "description": "PCRE wrapping library that offers type-safe preg_* replacements.", 55 | "keywords": [ 56 | "PCRE", 57 | "preg", 58 | "regex", 59 | "regular expression" 60 | ], 61 | "support": { 62 | "issues": "https://github.com/composer/pcre/issues", 63 | "source": "https://github.com/composer/pcre/tree/3.1.0" 64 | }, 65 | "funding": [ 66 | { 67 | "url": "https://packagist.com", 68 | "type": "custom" 69 | }, 70 | { 71 | "url": "https://github.com/composer", 72 | "type": "github" 73 | }, 74 | { 75 | "url": "https://tidelift.com/funding/github/packagist/composer/composer", 76 | "type": "tidelift" 77 | } 78 | ], 79 | "time": "2022-11-17T09:50:14+00:00" 80 | }, 81 | { 82 | "name": "composer/semver", 83 | "version": "3.3.2", 84 | "source": { 85 | "type": "git", 86 | "url": "https://github.com/composer/semver.git", 87 | "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9" 88 | }, 89 | "dist": { 90 | "type": "zip", 91 | "url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9", 92 | "reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9", 93 | "shasum": "" 94 | }, 95 | "require": { 96 | "php": "^5.3.2 || ^7.0 || ^8.0" 97 | }, 98 | "require-dev": { 99 | "phpstan/phpstan": "^1.4", 100 | "symfony/phpunit-bridge": "^4.2 || ^5" 101 | }, 102 | "type": "library", 103 | "extra": { 104 | "branch-alias": { 105 | "dev-main": "3.x-dev" 106 | } 107 | }, 108 | "autoload": { 109 | "psr-4": { 110 | "Composer\\Semver\\": "src" 111 | } 112 | }, 113 | "notification-url": "https://packagist.org/downloads/", 114 | "license": [ 115 | "MIT" 116 | ], 117 | "authors": [ 118 | { 119 | "name": "Nils Adermann", 120 | "email": "naderman@naderman.de", 121 | "homepage": "http://www.naderman.de" 122 | }, 123 | { 124 | "name": "Jordi Boggiano", 125 | "email": "j.boggiano@seld.be", 126 | "homepage": "http://seld.be" 127 | }, 128 | { 129 | "name": "Rob Bast", 130 | "email": "rob.bast@gmail.com", 131 | "homepage": "http://robbast.nl" 132 | } 133 | ], 134 | "description": "Semver library that offers utilities, version constraint parsing and validation.", 135 | "keywords": [ 136 | "semantic", 137 | "semver", 138 | "validation", 139 | "versioning" 140 | ], 141 | "support": { 142 | "irc": "irc://irc.freenode.org/composer", 143 | "issues": "https://github.com/composer/semver/issues", 144 | "source": "https://github.com/composer/semver/tree/3.3.2" 145 | }, 146 | "funding": [ 147 | { 148 | "url": "https://packagist.com", 149 | "type": "custom" 150 | }, 151 | { 152 | "url": "https://github.com/composer", 153 | "type": "github" 154 | }, 155 | { 156 | "url": "https://tidelift.com/funding/github/packagist/composer/composer", 157 | "type": "tidelift" 158 | } 159 | ], 160 | "time": "2022-04-01T19:23:25+00:00" 161 | }, 162 | { 163 | "name": "composer/xdebug-handler", 164 | "version": "3.0.3", 165 | "source": { 166 | "type": "git", 167 | "url": "https://github.com/composer/xdebug-handler.git", 168 | "reference": "ced299686f41dce890debac69273b47ffe98a40c" 169 | }, 170 | "dist": { 171 | "type": "zip", 172 | "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", 173 | "reference": "ced299686f41dce890debac69273b47ffe98a40c", 174 | "shasum": "" 175 | }, 176 | "require": { 177 | "composer/pcre": "^1 || ^2 || ^3", 178 | "php": "^7.2.5 || ^8.0", 179 | "psr/log": "^1 || ^2 || ^3" 180 | }, 181 | "require-dev": { 182 | "phpstan/phpstan": "^1.0", 183 | "phpstan/phpstan-strict-rules": "^1.1", 184 | "symfony/phpunit-bridge": "^6.0" 185 | }, 186 | "type": "library", 187 | "autoload": { 188 | "psr-4": { 189 | "Composer\\XdebugHandler\\": "src" 190 | } 191 | }, 192 | "notification-url": "https://packagist.org/downloads/", 193 | "license": [ 194 | "MIT" 195 | ], 196 | "authors": [ 197 | { 198 | "name": "John Stevenson", 199 | "email": "john-stevenson@blueyonder.co.uk" 200 | } 201 | ], 202 | "description": "Restarts a process without Xdebug.", 203 | "keywords": [ 204 | "Xdebug", 205 | "performance" 206 | ], 207 | "support": { 208 | "irc": "irc://irc.freenode.org/composer", 209 | "issues": "https://github.com/composer/xdebug-handler/issues", 210 | "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" 211 | }, 212 | "funding": [ 213 | { 214 | "url": "https://packagist.com", 215 | "type": "custom" 216 | }, 217 | { 218 | "url": "https://github.com/composer", 219 | "type": "github" 220 | }, 221 | { 222 | "url": "https://tidelift.com/funding/github/packagist/composer/composer", 223 | "type": "tidelift" 224 | } 225 | ], 226 | "time": "2022-02-25T21:32:43+00:00" 227 | }, 228 | { 229 | "name": "doctrine/annotations", 230 | "version": "1.14.2", 231 | "source": { 232 | "type": "git", 233 | "url": "https://github.com/doctrine/annotations.git", 234 | "reference": "ad785217c1e9555a7d6c6c8c9f406395a5e2882b" 235 | }, 236 | "dist": { 237 | "type": "zip", 238 | "url": "https://api.github.com/repos/doctrine/annotations/zipball/ad785217c1e9555a7d6c6c8c9f406395a5e2882b", 239 | "reference": "ad785217c1e9555a7d6c6c8c9f406395a5e2882b", 240 | "shasum": "" 241 | }, 242 | "require": { 243 | "doctrine/lexer": "^1 || ^2", 244 | "ext-tokenizer": "*", 245 | "php": "^7.1 || ^8.0", 246 | "psr/cache": "^1 || ^2 || ^3" 247 | }, 248 | "require-dev": { 249 | "doctrine/cache": "^1.11 || ^2.0", 250 | "doctrine/coding-standard": "^9 || ^10", 251 | "phpstan/phpstan": "~1.4.10 || ^1.8.0", 252 | "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", 253 | "symfony/cache": "^4.4 || ^5.4 || ^6", 254 | "vimeo/psalm": "^4.10" 255 | }, 256 | "suggest": { 257 | "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" 258 | }, 259 | "type": "library", 260 | "autoload": { 261 | "psr-4": { 262 | "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" 263 | } 264 | }, 265 | "notification-url": "https://packagist.org/downloads/", 266 | "license": [ 267 | "MIT" 268 | ], 269 | "authors": [ 270 | { 271 | "name": "Guilherme Blanco", 272 | "email": "guilhermeblanco@gmail.com" 273 | }, 274 | { 275 | "name": "Roman Borschel", 276 | "email": "roman@code-factory.org" 277 | }, 278 | { 279 | "name": "Benjamin Eberlei", 280 | "email": "kontakt@beberlei.de" 281 | }, 282 | { 283 | "name": "Jonathan Wage", 284 | "email": "jonwage@gmail.com" 285 | }, 286 | { 287 | "name": "Johannes Schmitt", 288 | "email": "schmittjoh@gmail.com" 289 | } 290 | ], 291 | "description": "Docblock Annotations Parser", 292 | "homepage": "https://www.doctrine-project.org/projects/annotations.html", 293 | "keywords": [ 294 | "annotations", 295 | "docblock", 296 | "parser" 297 | ], 298 | "support": { 299 | "issues": "https://github.com/doctrine/annotations/issues", 300 | "source": "https://github.com/doctrine/annotations/tree/1.14.2" 301 | }, 302 | "time": "2022-12-15T06:48:22+00:00" 303 | }, 304 | { 305 | "name": "doctrine/deprecations", 306 | "version": "v1.0.0", 307 | "source": { 308 | "type": "git", 309 | "url": "https://github.com/doctrine/deprecations.git", 310 | "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de" 311 | }, 312 | "dist": { 313 | "type": "zip", 314 | "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", 315 | "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de", 316 | "shasum": "" 317 | }, 318 | "require": { 319 | "php": "^7.1|^8.0" 320 | }, 321 | "require-dev": { 322 | "doctrine/coding-standard": "^9", 323 | "phpunit/phpunit": "^7.5|^8.5|^9.5", 324 | "psr/log": "^1|^2|^3" 325 | }, 326 | "suggest": { 327 | "psr/log": "Allows logging deprecations via PSR-3 logger implementation" 328 | }, 329 | "type": "library", 330 | "autoload": { 331 | "psr-4": { 332 | "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" 333 | } 334 | }, 335 | "notification-url": "https://packagist.org/downloads/", 336 | "license": [ 337 | "MIT" 338 | ], 339 | "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", 340 | "homepage": "https://www.doctrine-project.org/", 341 | "support": { 342 | "issues": "https://github.com/doctrine/deprecations/issues", 343 | "source": "https://github.com/doctrine/deprecations/tree/v1.0.0" 344 | }, 345 | "time": "2022-05-02T15:47:09+00:00" 346 | }, 347 | { 348 | "name": "doctrine/lexer", 349 | "version": "2.1.0", 350 | "source": { 351 | "type": "git", 352 | "url": "https://github.com/doctrine/lexer.git", 353 | "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124" 354 | }, 355 | "dist": { 356 | "type": "zip", 357 | "url": "https://api.github.com/repos/doctrine/lexer/zipball/39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", 358 | "reference": "39ab8fcf5a51ce4b85ca97c7a7d033eb12831124", 359 | "shasum": "" 360 | }, 361 | "require": { 362 | "doctrine/deprecations": "^1.0", 363 | "php": "^7.1 || ^8.0" 364 | }, 365 | "require-dev": { 366 | "doctrine/coding-standard": "^9 || ^10", 367 | "phpstan/phpstan": "^1.3", 368 | "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", 369 | "psalm/plugin-phpunit": "^0.18.3", 370 | "vimeo/psalm": "^4.11 || ^5.0" 371 | }, 372 | "type": "library", 373 | "autoload": { 374 | "psr-4": { 375 | "Doctrine\\Common\\Lexer\\": "src" 376 | } 377 | }, 378 | "notification-url": "https://packagist.org/downloads/", 379 | "license": [ 380 | "MIT" 381 | ], 382 | "authors": [ 383 | { 384 | "name": "Guilherme Blanco", 385 | "email": "guilhermeblanco@gmail.com" 386 | }, 387 | { 388 | "name": "Roman Borschel", 389 | "email": "roman@code-factory.org" 390 | }, 391 | { 392 | "name": "Johannes Schmitt", 393 | "email": "schmittjoh@gmail.com" 394 | } 395 | ], 396 | "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", 397 | "homepage": "https://www.doctrine-project.org/projects/lexer.html", 398 | "keywords": [ 399 | "annotations", 400 | "docblock", 401 | "lexer", 402 | "parser", 403 | "php" 404 | ], 405 | "support": { 406 | "issues": "https://github.com/doctrine/lexer/issues", 407 | "source": "https://github.com/doctrine/lexer/tree/2.1.0" 408 | }, 409 | "funding": [ 410 | { 411 | "url": "https://www.doctrine-project.org/sponsorship.html", 412 | "type": "custom" 413 | }, 414 | { 415 | "url": "https://www.patreon.com/phpdoctrine", 416 | "type": "patreon" 417 | }, 418 | { 419 | "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", 420 | "type": "tidelift" 421 | } 422 | ], 423 | "time": "2022-12-14T08:49:07+00:00" 424 | }, 425 | { 426 | "name": "friendsofphp/php-cs-fixer", 427 | "version": "v3.13.2", 428 | "source": { 429 | "type": "git", 430 | "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", 431 | "reference": "3952f08a81bd3b1b15e11c3de0b6bf037faa8496" 432 | }, 433 | "dist": { 434 | "type": "zip", 435 | "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/3952f08a81bd3b1b15e11c3de0b6bf037faa8496", 436 | "reference": "3952f08a81bd3b1b15e11c3de0b6bf037faa8496", 437 | "shasum": "" 438 | }, 439 | "require": { 440 | "composer/semver": "^3.2", 441 | "composer/xdebug-handler": "^3.0.3", 442 | "doctrine/annotations": "^1.13", 443 | "ext-json": "*", 444 | "ext-tokenizer": "*", 445 | "php": "^7.4 || ^8.0", 446 | "sebastian/diff": "^4.0", 447 | "symfony/console": "^5.4 || ^6.0", 448 | "symfony/event-dispatcher": "^5.4 || ^6.0", 449 | "symfony/filesystem": "^5.4 || ^6.0", 450 | "symfony/finder": "^5.4 || ^6.0", 451 | "symfony/options-resolver": "^5.4 || ^6.0", 452 | "symfony/polyfill-mbstring": "^1.23", 453 | "symfony/polyfill-php80": "^1.25", 454 | "symfony/polyfill-php81": "^1.25", 455 | "symfony/process": "^5.4 || ^6.0", 456 | "symfony/stopwatch": "^5.4 || ^6.0" 457 | }, 458 | "require-dev": { 459 | "justinrainbow/json-schema": "^5.2", 460 | "keradus/cli-executor": "^2.0", 461 | "mikey179/vfsstream": "^1.6.10", 462 | "php-coveralls/php-coveralls": "^2.5.2", 463 | "php-cs-fixer/accessible-object": "^1.1", 464 | "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", 465 | "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", 466 | "phpspec/prophecy": "^1.15", 467 | "phpspec/prophecy-phpunit": "^2.0", 468 | "phpunit/phpunit": "^9.5", 469 | "phpunitgoodpractices/polyfill": "^1.6", 470 | "phpunitgoodpractices/traits": "^1.9.2", 471 | "symfony/phpunit-bridge": "^6.0", 472 | "symfony/yaml": "^5.4 || ^6.0" 473 | }, 474 | "suggest": { 475 | "ext-dom": "For handling output formats in XML", 476 | "ext-mbstring": "For handling non-UTF8 characters." 477 | }, 478 | "bin": [ 479 | "php-cs-fixer" 480 | ], 481 | "type": "application", 482 | "autoload": { 483 | "psr-4": { 484 | "PhpCsFixer\\": "src/" 485 | } 486 | }, 487 | "notification-url": "https://packagist.org/downloads/", 488 | "license": [ 489 | "MIT" 490 | ], 491 | "authors": [ 492 | { 493 | "name": "Fabien Potencier", 494 | "email": "fabien@symfony.com" 495 | }, 496 | { 497 | "name": "Dariusz Rumiński", 498 | "email": "dariusz.ruminski@gmail.com" 499 | } 500 | ], 501 | "description": "A tool to automatically fix PHP code style", 502 | "support": { 503 | "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", 504 | "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.13.2" 505 | }, 506 | "funding": [ 507 | { 508 | "url": "https://github.com/keradus", 509 | "type": "github" 510 | } 511 | ], 512 | "time": "2023-01-02T23:53:50+00:00" 513 | }, 514 | { 515 | "name": "php-parallel-lint/php-parallel-lint", 516 | "version": "v1.3.2", 517 | "source": { 518 | "type": "git", 519 | "url": "https://github.com/php-parallel-lint/PHP-Parallel-Lint.git", 520 | "reference": "6483c9832e71973ed29cf71bd6b3f4fde438a9de" 521 | }, 522 | "dist": { 523 | "type": "zip", 524 | "url": "https://api.github.com/repos/php-parallel-lint/PHP-Parallel-Lint/zipball/6483c9832e71973ed29cf71bd6b3f4fde438a9de", 525 | "reference": "6483c9832e71973ed29cf71bd6b3f4fde438a9de", 526 | "shasum": "" 527 | }, 528 | "require": { 529 | "ext-json": "*", 530 | "php": ">=5.3.0" 531 | }, 532 | "replace": { 533 | "grogy/php-parallel-lint": "*", 534 | "jakub-onderka/php-parallel-lint": "*" 535 | }, 536 | "require-dev": { 537 | "nette/tester": "^1.3 || ^2.0", 538 | "php-parallel-lint/php-console-highlighter": "0.* || ^1.0", 539 | "squizlabs/php_codesniffer": "^3.6" 540 | }, 541 | "suggest": { 542 | "php-parallel-lint/php-console-highlighter": "Highlight syntax in code snippet" 543 | }, 544 | "bin": [ 545 | "parallel-lint" 546 | ], 547 | "type": "library", 548 | "autoload": { 549 | "classmap": [ 550 | "./src/" 551 | ] 552 | }, 553 | "notification-url": "https://packagist.org/downloads/", 554 | "license": [ 555 | "BSD-2-Clause" 556 | ], 557 | "authors": [ 558 | { 559 | "name": "Jakub Onderka", 560 | "email": "ahoj@jakubonderka.cz" 561 | } 562 | ], 563 | "description": "This tool check syntax of PHP files about 20x faster than serial check.", 564 | "homepage": "https://github.com/php-parallel-lint/PHP-Parallel-Lint", 565 | "support": { 566 | "issues": "https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues", 567 | "source": "https://github.com/php-parallel-lint/PHP-Parallel-Lint/tree/v1.3.2" 568 | }, 569 | "time": "2022-02-21T12:50:22+00:00" 570 | }, 571 | { 572 | "name": "phpstan/phpstan", 573 | "version": "1.12.6", 574 | "source": { 575 | "type": "git", 576 | "url": "https://github.com/phpstan/phpstan.git", 577 | "reference": "dc4d2f145a88ea7141ae698effd64d9df46527ae" 578 | }, 579 | "dist": { 580 | "type": "zip", 581 | "url": "https://api.github.com/repos/phpstan/phpstan/zipball/dc4d2f145a88ea7141ae698effd64d9df46527ae", 582 | "reference": "dc4d2f145a88ea7141ae698effd64d9df46527ae", 583 | "shasum": "" 584 | }, 585 | "require": { 586 | "php": "^7.2|^8.0" 587 | }, 588 | "conflict": { 589 | "phpstan/phpstan-shim": "*" 590 | }, 591 | "bin": [ 592 | "phpstan", 593 | "phpstan.phar" 594 | ], 595 | "type": "library", 596 | "autoload": { 597 | "files": [ 598 | "bootstrap.php" 599 | ] 600 | }, 601 | "notification-url": "https://packagist.org/downloads/", 602 | "license": [ 603 | "MIT" 604 | ], 605 | "description": "PHPStan - PHP Static Analysis Tool", 606 | "keywords": [ 607 | "dev", 608 | "static analysis" 609 | ], 610 | "support": { 611 | "docs": "https://phpstan.org/user-guide/getting-started", 612 | "forum": "https://github.com/phpstan/phpstan/discussions", 613 | "issues": "https://github.com/phpstan/phpstan/issues", 614 | "security": "https://github.com/phpstan/phpstan/security/policy", 615 | "source": "https://github.com/phpstan/phpstan-src" 616 | }, 617 | "funding": [ 618 | { 619 | "url": "https://github.com/ondrejmirtes", 620 | "type": "github" 621 | }, 622 | { 623 | "url": "https://github.com/phpstan", 624 | "type": "github" 625 | } 626 | ], 627 | "time": "2024-10-06T15:03:59+00:00" 628 | }, 629 | { 630 | "name": "psr/cache", 631 | "version": "3.0.0", 632 | "source": { 633 | "type": "git", 634 | "url": "https://github.com/php-fig/cache.git", 635 | "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" 636 | }, 637 | "dist": { 638 | "type": "zip", 639 | "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", 640 | "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", 641 | "shasum": "" 642 | }, 643 | "require": { 644 | "php": ">=8.0.0" 645 | }, 646 | "type": "library", 647 | "extra": { 648 | "branch-alias": { 649 | "dev-master": "1.0.x-dev" 650 | } 651 | }, 652 | "autoload": { 653 | "psr-4": { 654 | "Psr\\Cache\\": "src/" 655 | } 656 | }, 657 | "notification-url": "https://packagist.org/downloads/", 658 | "license": [ 659 | "MIT" 660 | ], 661 | "authors": [ 662 | { 663 | "name": "PHP-FIG", 664 | "homepage": "https://www.php-fig.org/" 665 | } 666 | ], 667 | "description": "Common interface for caching libraries", 668 | "keywords": [ 669 | "cache", 670 | "psr", 671 | "psr-6" 672 | ], 673 | "support": { 674 | "source": "https://github.com/php-fig/cache/tree/3.0.0" 675 | }, 676 | "time": "2021-02-03T23:26:27+00:00" 677 | }, 678 | { 679 | "name": "psr/container", 680 | "version": "2.0.2", 681 | "source": { 682 | "type": "git", 683 | "url": "https://github.com/php-fig/container.git", 684 | "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" 685 | }, 686 | "dist": { 687 | "type": "zip", 688 | "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", 689 | "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", 690 | "shasum": "" 691 | }, 692 | "require": { 693 | "php": ">=7.4.0" 694 | }, 695 | "type": "library", 696 | "extra": { 697 | "branch-alias": { 698 | "dev-master": "2.0.x-dev" 699 | } 700 | }, 701 | "autoload": { 702 | "psr-4": { 703 | "Psr\\Container\\": "src/" 704 | } 705 | }, 706 | "notification-url": "https://packagist.org/downloads/", 707 | "license": [ 708 | "MIT" 709 | ], 710 | "authors": [ 711 | { 712 | "name": "PHP-FIG", 713 | "homepage": "https://www.php-fig.org/" 714 | } 715 | ], 716 | "description": "Common Container Interface (PHP FIG PSR-11)", 717 | "homepage": "https://github.com/php-fig/container", 718 | "keywords": [ 719 | "PSR-11", 720 | "container", 721 | "container-interface", 722 | "container-interop", 723 | "psr" 724 | ], 725 | "support": { 726 | "issues": "https://github.com/php-fig/container/issues", 727 | "source": "https://github.com/php-fig/container/tree/2.0.2" 728 | }, 729 | "time": "2021-11-05T16:47:00+00:00" 730 | }, 731 | { 732 | "name": "psr/event-dispatcher", 733 | "version": "1.0.0", 734 | "source": { 735 | "type": "git", 736 | "url": "https://github.com/php-fig/event-dispatcher.git", 737 | "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" 738 | }, 739 | "dist": { 740 | "type": "zip", 741 | "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", 742 | "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", 743 | "shasum": "" 744 | }, 745 | "require": { 746 | "php": ">=7.2.0" 747 | }, 748 | "type": "library", 749 | "extra": { 750 | "branch-alias": { 751 | "dev-master": "1.0.x-dev" 752 | } 753 | }, 754 | "autoload": { 755 | "psr-4": { 756 | "Psr\\EventDispatcher\\": "src/" 757 | } 758 | }, 759 | "notification-url": "https://packagist.org/downloads/", 760 | "license": [ 761 | "MIT" 762 | ], 763 | "authors": [ 764 | { 765 | "name": "PHP-FIG", 766 | "homepage": "http://www.php-fig.org/" 767 | } 768 | ], 769 | "description": "Standard interfaces for event handling.", 770 | "keywords": [ 771 | "events", 772 | "psr", 773 | "psr-14" 774 | ], 775 | "support": { 776 | "issues": "https://github.com/php-fig/event-dispatcher/issues", 777 | "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" 778 | }, 779 | "time": "2019-01-08T18:20:26+00:00" 780 | }, 781 | { 782 | "name": "psr/log", 783 | "version": "3.0.0", 784 | "source": { 785 | "type": "git", 786 | "url": "https://github.com/php-fig/log.git", 787 | "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" 788 | }, 789 | "dist": { 790 | "type": "zip", 791 | "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", 792 | "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", 793 | "shasum": "" 794 | }, 795 | "require": { 796 | "php": ">=8.0.0" 797 | }, 798 | "type": "library", 799 | "extra": { 800 | "branch-alias": { 801 | "dev-master": "3.x-dev" 802 | } 803 | }, 804 | "autoload": { 805 | "psr-4": { 806 | "Psr\\Log\\": "src" 807 | } 808 | }, 809 | "notification-url": "https://packagist.org/downloads/", 810 | "license": [ 811 | "MIT" 812 | ], 813 | "authors": [ 814 | { 815 | "name": "PHP-FIG", 816 | "homepage": "https://www.php-fig.org/" 817 | } 818 | ], 819 | "description": "Common interface for logging libraries", 820 | "homepage": "https://github.com/php-fig/log", 821 | "keywords": [ 822 | "log", 823 | "psr", 824 | "psr-3" 825 | ], 826 | "support": { 827 | "source": "https://github.com/php-fig/log/tree/3.0.0" 828 | }, 829 | "time": "2021-07-14T16:46:02+00:00" 830 | }, 831 | { 832 | "name": "sebastian/diff", 833 | "version": "4.0.4", 834 | "source": { 835 | "type": "git", 836 | "url": "https://github.com/sebastianbergmann/diff.git", 837 | "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" 838 | }, 839 | "dist": { 840 | "type": "zip", 841 | "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", 842 | "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", 843 | "shasum": "" 844 | }, 845 | "require": { 846 | "php": ">=7.3" 847 | }, 848 | "require-dev": { 849 | "phpunit/phpunit": "^9.3", 850 | "symfony/process": "^4.2 || ^5" 851 | }, 852 | "type": "library", 853 | "extra": { 854 | "branch-alias": { 855 | "dev-master": "4.0-dev" 856 | } 857 | }, 858 | "autoload": { 859 | "classmap": [ 860 | "src/" 861 | ] 862 | }, 863 | "notification-url": "https://packagist.org/downloads/", 864 | "license": [ 865 | "BSD-3-Clause" 866 | ], 867 | "authors": [ 868 | { 869 | "name": "Sebastian Bergmann", 870 | "email": "sebastian@phpunit.de" 871 | }, 872 | { 873 | "name": "Kore Nordmann", 874 | "email": "mail@kore-nordmann.de" 875 | } 876 | ], 877 | "description": "Diff implementation", 878 | "homepage": "https://github.com/sebastianbergmann/diff", 879 | "keywords": [ 880 | "diff", 881 | "udiff", 882 | "unidiff", 883 | "unified diff" 884 | ], 885 | "support": { 886 | "issues": "https://github.com/sebastianbergmann/diff/issues", 887 | "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" 888 | }, 889 | "funding": [ 890 | { 891 | "url": "https://github.com/sebastianbergmann", 892 | "type": "github" 893 | } 894 | ], 895 | "time": "2020-10-26T13:10:38+00:00" 896 | }, 897 | { 898 | "name": "symfony/console", 899 | "version": "v6.0.17", 900 | "source": { 901 | "type": "git", 902 | "url": "https://github.com/symfony/console.git", 903 | "reference": "2ab307342a7233b9a260edd5ef94087aaca57d18" 904 | }, 905 | "dist": { 906 | "type": "zip", 907 | "url": "https://api.github.com/repos/symfony/console/zipball/2ab307342a7233b9a260edd5ef94087aaca57d18", 908 | "reference": "2ab307342a7233b9a260edd5ef94087aaca57d18", 909 | "shasum": "" 910 | }, 911 | "require": { 912 | "php": ">=8.0.2", 913 | "symfony/polyfill-mbstring": "~1.0", 914 | "symfony/service-contracts": "^1.1|^2|^3", 915 | "symfony/string": "^5.4|^6.0" 916 | }, 917 | "conflict": { 918 | "symfony/dependency-injection": "<5.4", 919 | "symfony/dotenv": "<5.4", 920 | "symfony/event-dispatcher": "<5.4", 921 | "symfony/lock": "<5.4", 922 | "symfony/process": "<5.4" 923 | }, 924 | "provide": { 925 | "psr/log-implementation": "1.0|2.0|3.0" 926 | }, 927 | "require-dev": { 928 | "psr/log": "^1|^2|^3", 929 | "symfony/config": "^5.4|^6.0", 930 | "symfony/dependency-injection": "^5.4|^6.0", 931 | "symfony/event-dispatcher": "^5.4|^6.0", 932 | "symfony/lock": "^5.4|^6.0", 933 | "symfony/process": "^5.4|^6.0", 934 | "symfony/var-dumper": "^5.4|^6.0" 935 | }, 936 | "suggest": { 937 | "psr/log": "For using the console logger", 938 | "symfony/event-dispatcher": "", 939 | "symfony/lock": "", 940 | "symfony/process": "" 941 | }, 942 | "type": "library", 943 | "autoload": { 944 | "psr-4": { 945 | "Symfony\\Component\\Console\\": "" 946 | }, 947 | "exclude-from-classmap": [ 948 | "/Tests/" 949 | ] 950 | }, 951 | "notification-url": "https://packagist.org/downloads/", 952 | "license": [ 953 | "MIT" 954 | ], 955 | "authors": [ 956 | { 957 | "name": "Fabien Potencier", 958 | "email": "fabien@symfony.com" 959 | }, 960 | { 961 | "name": "Symfony Community", 962 | "homepage": "https://symfony.com/contributors" 963 | } 964 | ], 965 | "description": "Eases the creation of beautiful and testable command line interfaces", 966 | "homepage": "https://symfony.com", 967 | "keywords": [ 968 | "cli", 969 | "command line", 970 | "console", 971 | "terminal" 972 | ], 973 | "support": { 974 | "source": "https://github.com/symfony/console/tree/v6.0.17" 975 | }, 976 | "funding": [ 977 | { 978 | "url": "https://symfony.com/sponsor", 979 | "type": "custom" 980 | }, 981 | { 982 | "url": "https://github.com/fabpot", 983 | "type": "github" 984 | }, 985 | { 986 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 987 | "type": "tidelift" 988 | } 989 | ], 990 | "time": "2022-12-28T14:21:34+00:00" 991 | }, 992 | { 993 | "name": "symfony/deprecation-contracts", 994 | "version": "v3.0.2", 995 | "source": { 996 | "type": "git", 997 | "url": "https://github.com/symfony/deprecation-contracts.git", 998 | "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c" 999 | }, 1000 | "dist": { 1001 | "type": "zip", 1002 | "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", 1003 | "reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c", 1004 | "shasum": "" 1005 | }, 1006 | "require": { 1007 | "php": ">=8.0.2" 1008 | }, 1009 | "type": "library", 1010 | "extra": { 1011 | "branch-alias": { 1012 | "dev-main": "3.0-dev" 1013 | }, 1014 | "thanks": { 1015 | "name": "symfony/contracts", 1016 | "url": "https://github.com/symfony/contracts" 1017 | } 1018 | }, 1019 | "autoload": { 1020 | "files": [ 1021 | "function.php" 1022 | ] 1023 | }, 1024 | "notification-url": "https://packagist.org/downloads/", 1025 | "license": [ 1026 | "MIT" 1027 | ], 1028 | "authors": [ 1029 | { 1030 | "name": "Nicolas Grekas", 1031 | "email": "p@tchwork.com" 1032 | }, 1033 | { 1034 | "name": "Symfony Community", 1035 | "homepage": "https://symfony.com/contributors" 1036 | } 1037 | ], 1038 | "description": "A generic function and convention to trigger deprecation notices", 1039 | "homepage": "https://symfony.com", 1040 | "support": { 1041 | "source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.2" 1042 | }, 1043 | "funding": [ 1044 | { 1045 | "url": "https://symfony.com/sponsor", 1046 | "type": "custom" 1047 | }, 1048 | { 1049 | "url": "https://github.com/fabpot", 1050 | "type": "github" 1051 | }, 1052 | { 1053 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1054 | "type": "tidelift" 1055 | } 1056 | ], 1057 | "time": "2022-01-02T09:55:41+00:00" 1058 | }, 1059 | { 1060 | "name": "symfony/event-dispatcher", 1061 | "version": "v6.0.17", 1062 | "source": { 1063 | "type": "git", 1064 | "url": "https://github.com/symfony/event-dispatcher.git", 1065 | "reference": "42b3985aa07837c9df36013ec5b965e9f2d480bc" 1066 | }, 1067 | "dist": { 1068 | "type": "zip", 1069 | "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/42b3985aa07837c9df36013ec5b965e9f2d480bc", 1070 | "reference": "42b3985aa07837c9df36013ec5b965e9f2d480bc", 1071 | "shasum": "" 1072 | }, 1073 | "require": { 1074 | "php": ">=8.0.2", 1075 | "symfony/event-dispatcher-contracts": "^2|^3" 1076 | }, 1077 | "conflict": { 1078 | "symfony/dependency-injection": "<5.4" 1079 | }, 1080 | "provide": { 1081 | "psr/event-dispatcher-implementation": "1.0", 1082 | "symfony/event-dispatcher-implementation": "2.0|3.0" 1083 | }, 1084 | "require-dev": { 1085 | "psr/log": "^1|^2|^3", 1086 | "symfony/config": "^5.4|^6.0", 1087 | "symfony/dependency-injection": "^5.4|^6.0", 1088 | "symfony/error-handler": "^5.4|^6.0", 1089 | "symfony/expression-language": "^5.4|^6.0", 1090 | "symfony/http-foundation": "^5.4|^6.0", 1091 | "symfony/service-contracts": "^1.1|^2|^3", 1092 | "symfony/stopwatch": "^5.4|^6.0" 1093 | }, 1094 | "suggest": { 1095 | "symfony/dependency-injection": "", 1096 | "symfony/http-kernel": "" 1097 | }, 1098 | "type": "library", 1099 | "autoload": { 1100 | "psr-4": { 1101 | "Symfony\\Component\\EventDispatcher\\": "" 1102 | }, 1103 | "exclude-from-classmap": [ 1104 | "/Tests/" 1105 | ] 1106 | }, 1107 | "notification-url": "https://packagist.org/downloads/", 1108 | "license": [ 1109 | "MIT" 1110 | ], 1111 | "authors": [ 1112 | { 1113 | "name": "Fabien Potencier", 1114 | "email": "fabien@symfony.com" 1115 | }, 1116 | { 1117 | "name": "Symfony Community", 1118 | "homepage": "https://symfony.com/contributors" 1119 | } 1120 | ], 1121 | "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", 1122 | "homepage": "https://symfony.com", 1123 | "support": { 1124 | "source": "https://github.com/symfony/event-dispatcher/tree/v6.0.17" 1125 | }, 1126 | "funding": [ 1127 | { 1128 | "url": "https://symfony.com/sponsor", 1129 | "type": "custom" 1130 | }, 1131 | { 1132 | "url": "https://github.com/fabpot", 1133 | "type": "github" 1134 | }, 1135 | { 1136 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1137 | "type": "tidelift" 1138 | } 1139 | ], 1140 | "time": "2022-12-14T15:52:41+00:00" 1141 | }, 1142 | { 1143 | "name": "symfony/event-dispatcher-contracts", 1144 | "version": "v3.0.2", 1145 | "source": { 1146 | "type": "git", 1147 | "url": "https://github.com/symfony/event-dispatcher-contracts.git", 1148 | "reference": "7bc61cc2db649b4637d331240c5346dcc7708051" 1149 | }, 1150 | "dist": { 1151 | "type": "zip", 1152 | "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7bc61cc2db649b4637d331240c5346dcc7708051", 1153 | "reference": "7bc61cc2db649b4637d331240c5346dcc7708051", 1154 | "shasum": "" 1155 | }, 1156 | "require": { 1157 | "php": ">=8.0.2", 1158 | "psr/event-dispatcher": "^1" 1159 | }, 1160 | "suggest": { 1161 | "symfony/event-dispatcher-implementation": "" 1162 | }, 1163 | "type": "library", 1164 | "extra": { 1165 | "branch-alias": { 1166 | "dev-main": "3.0-dev" 1167 | }, 1168 | "thanks": { 1169 | "name": "symfony/contracts", 1170 | "url": "https://github.com/symfony/contracts" 1171 | } 1172 | }, 1173 | "autoload": { 1174 | "psr-4": { 1175 | "Symfony\\Contracts\\EventDispatcher\\": "" 1176 | } 1177 | }, 1178 | "notification-url": "https://packagist.org/downloads/", 1179 | "license": [ 1180 | "MIT" 1181 | ], 1182 | "authors": [ 1183 | { 1184 | "name": "Nicolas Grekas", 1185 | "email": "p@tchwork.com" 1186 | }, 1187 | { 1188 | "name": "Symfony Community", 1189 | "homepage": "https://symfony.com/contributors" 1190 | } 1191 | ], 1192 | "description": "Generic abstractions related to dispatching event", 1193 | "homepage": "https://symfony.com", 1194 | "keywords": [ 1195 | "abstractions", 1196 | "contracts", 1197 | "decoupling", 1198 | "interfaces", 1199 | "interoperability", 1200 | "standards" 1201 | ], 1202 | "support": { 1203 | "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.0.2" 1204 | }, 1205 | "funding": [ 1206 | { 1207 | "url": "https://symfony.com/sponsor", 1208 | "type": "custom" 1209 | }, 1210 | { 1211 | "url": "https://github.com/fabpot", 1212 | "type": "github" 1213 | }, 1214 | { 1215 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1216 | "type": "tidelift" 1217 | } 1218 | ], 1219 | "time": "2022-01-02T09:55:41+00:00" 1220 | }, 1221 | { 1222 | "name": "symfony/filesystem", 1223 | "version": "v6.0.13", 1224 | "source": { 1225 | "type": "git", 1226 | "url": "https://github.com/symfony/filesystem.git", 1227 | "reference": "3adca49133bd055ebe6011ed1e012be3c908af79" 1228 | }, 1229 | "dist": { 1230 | "type": "zip", 1231 | "url": "https://api.github.com/repos/symfony/filesystem/zipball/3adca49133bd055ebe6011ed1e012be3c908af79", 1232 | "reference": "3adca49133bd055ebe6011ed1e012be3c908af79", 1233 | "shasum": "" 1234 | }, 1235 | "require": { 1236 | "php": ">=8.0.2", 1237 | "symfony/polyfill-ctype": "~1.8", 1238 | "symfony/polyfill-mbstring": "~1.8" 1239 | }, 1240 | "type": "library", 1241 | "autoload": { 1242 | "psr-4": { 1243 | "Symfony\\Component\\Filesystem\\": "" 1244 | }, 1245 | "exclude-from-classmap": [ 1246 | "/Tests/" 1247 | ] 1248 | }, 1249 | "notification-url": "https://packagist.org/downloads/", 1250 | "license": [ 1251 | "MIT" 1252 | ], 1253 | "authors": [ 1254 | { 1255 | "name": "Fabien Potencier", 1256 | "email": "fabien@symfony.com" 1257 | }, 1258 | { 1259 | "name": "Symfony Community", 1260 | "homepage": "https://symfony.com/contributors" 1261 | } 1262 | ], 1263 | "description": "Provides basic utilities for the filesystem", 1264 | "homepage": "https://symfony.com", 1265 | "support": { 1266 | "source": "https://github.com/symfony/filesystem/tree/v6.0.13" 1267 | }, 1268 | "funding": [ 1269 | { 1270 | "url": "https://symfony.com/sponsor", 1271 | "type": "custom" 1272 | }, 1273 | { 1274 | "url": "https://github.com/fabpot", 1275 | "type": "github" 1276 | }, 1277 | { 1278 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1279 | "type": "tidelift" 1280 | } 1281 | ], 1282 | "time": "2022-09-21T20:25:27+00:00" 1283 | }, 1284 | { 1285 | "name": "symfony/finder", 1286 | "version": "v6.0.17", 1287 | "source": { 1288 | "type": "git", 1289 | "url": "https://github.com/symfony/finder.git", 1290 | "reference": "d467d625fc88f7cebf96f495e588a7196a669db1" 1291 | }, 1292 | "dist": { 1293 | "type": "zip", 1294 | "url": "https://api.github.com/repos/symfony/finder/zipball/d467d625fc88f7cebf96f495e588a7196a669db1", 1295 | "reference": "d467d625fc88f7cebf96f495e588a7196a669db1", 1296 | "shasum": "" 1297 | }, 1298 | "require": { 1299 | "php": ">=8.0.2" 1300 | }, 1301 | "type": "library", 1302 | "autoload": { 1303 | "psr-4": { 1304 | "Symfony\\Component\\Finder\\": "" 1305 | }, 1306 | "exclude-from-classmap": [ 1307 | "/Tests/" 1308 | ] 1309 | }, 1310 | "notification-url": "https://packagist.org/downloads/", 1311 | "license": [ 1312 | "MIT" 1313 | ], 1314 | "authors": [ 1315 | { 1316 | "name": "Fabien Potencier", 1317 | "email": "fabien@symfony.com" 1318 | }, 1319 | { 1320 | "name": "Symfony Community", 1321 | "homepage": "https://symfony.com/contributors" 1322 | } 1323 | ], 1324 | "description": "Finds files and directories via an intuitive fluent interface", 1325 | "homepage": "https://symfony.com", 1326 | "support": { 1327 | "source": "https://github.com/symfony/finder/tree/v6.0.17" 1328 | }, 1329 | "funding": [ 1330 | { 1331 | "url": "https://symfony.com/sponsor", 1332 | "type": "custom" 1333 | }, 1334 | { 1335 | "url": "https://github.com/fabpot", 1336 | "type": "github" 1337 | }, 1338 | { 1339 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1340 | "type": "tidelift" 1341 | } 1342 | ], 1343 | "time": "2022-12-22T17:53:58+00:00" 1344 | }, 1345 | { 1346 | "name": "symfony/options-resolver", 1347 | "version": "v6.0.3", 1348 | "source": { 1349 | "type": "git", 1350 | "url": "https://github.com/symfony/options-resolver.git", 1351 | "reference": "51f7006670febe4cbcbae177cbffe93ff833250d" 1352 | }, 1353 | "dist": { 1354 | "type": "zip", 1355 | "url": "https://api.github.com/repos/symfony/options-resolver/zipball/51f7006670febe4cbcbae177cbffe93ff833250d", 1356 | "reference": "51f7006670febe4cbcbae177cbffe93ff833250d", 1357 | "shasum": "" 1358 | }, 1359 | "require": { 1360 | "php": ">=8.0.2", 1361 | "symfony/deprecation-contracts": "^2.1|^3" 1362 | }, 1363 | "type": "library", 1364 | "autoload": { 1365 | "psr-4": { 1366 | "Symfony\\Component\\OptionsResolver\\": "" 1367 | }, 1368 | "exclude-from-classmap": [ 1369 | "/Tests/" 1370 | ] 1371 | }, 1372 | "notification-url": "https://packagist.org/downloads/", 1373 | "license": [ 1374 | "MIT" 1375 | ], 1376 | "authors": [ 1377 | { 1378 | "name": "Fabien Potencier", 1379 | "email": "fabien@symfony.com" 1380 | }, 1381 | { 1382 | "name": "Symfony Community", 1383 | "homepage": "https://symfony.com/contributors" 1384 | } 1385 | ], 1386 | "description": "Provides an improved replacement for the array_replace PHP function", 1387 | "homepage": "https://symfony.com", 1388 | "keywords": [ 1389 | "config", 1390 | "configuration", 1391 | "options" 1392 | ], 1393 | "support": { 1394 | "source": "https://github.com/symfony/options-resolver/tree/v6.0.3" 1395 | }, 1396 | "funding": [ 1397 | { 1398 | "url": "https://symfony.com/sponsor", 1399 | "type": "custom" 1400 | }, 1401 | { 1402 | "url": "https://github.com/fabpot", 1403 | "type": "github" 1404 | }, 1405 | { 1406 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1407 | "type": "tidelift" 1408 | } 1409 | ], 1410 | "time": "2022-01-02T09:55:41+00:00" 1411 | }, 1412 | { 1413 | "name": "symfony/polyfill-ctype", 1414 | "version": "v1.27.0", 1415 | "source": { 1416 | "type": "git", 1417 | "url": "https://github.com/symfony/polyfill-ctype.git", 1418 | "reference": "5bbc823adecdae860bb64756d639ecfec17b050a" 1419 | }, 1420 | "dist": { 1421 | "type": "zip", 1422 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/5bbc823adecdae860bb64756d639ecfec17b050a", 1423 | "reference": "5bbc823adecdae860bb64756d639ecfec17b050a", 1424 | "shasum": "" 1425 | }, 1426 | "require": { 1427 | "php": ">=7.1" 1428 | }, 1429 | "provide": { 1430 | "ext-ctype": "*" 1431 | }, 1432 | "suggest": { 1433 | "ext-ctype": "For best performance" 1434 | }, 1435 | "type": "library", 1436 | "extra": { 1437 | "branch-alias": { 1438 | "dev-main": "1.27-dev" 1439 | }, 1440 | "thanks": { 1441 | "name": "symfony/polyfill", 1442 | "url": "https://github.com/symfony/polyfill" 1443 | } 1444 | }, 1445 | "autoload": { 1446 | "files": [ 1447 | "bootstrap.php" 1448 | ], 1449 | "psr-4": { 1450 | "Symfony\\Polyfill\\Ctype\\": "" 1451 | } 1452 | }, 1453 | "notification-url": "https://packagist.org/downloads/", 1454 | "license": [ 1455 | "MIT" 1456 | ], 1457 | "authors": [ 1458 | { 1459 | "name": "Gert de Pagter", 1460 | "email": "BackEndTea@gmail.com" 1461 | }, 1462 | { 1463 | "name": "Symfony Community", 1464 | "homepage": "https://symfony.com/contributors" 1465 | } 1466 | ], 1467 | "description": "Symfony polyfill for ctype functions", 1468 | "homepage": "https://symfony.com", 1469 | "keywords": [ 1470 | "compatibility", 1471 | "ctype", 1472 | "polyfill", 1473 | "portable" 1474 | ], 1475 | "support": { 1476 | "source": "https://github.com/symfony/polyfill-ctype/tree/v1.27.0" 1477 | }, 1478 | "funding": [ 1479 | { 1480 | "url": "https://symfony.com/sponsor", 1481 | "type": "custom" 1482 | }, 1483 | { 1484 | "url": "https://github.com/fabpot", 1485 | "type": "github" 1486 | }, 1487 | { 1488 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1489 | "type": "tidelift" 1490 | } 1491 | ], 1492 | "time": "2022-11-03T14:55:06+00:00" 1493 | }, 1494 | { 1495 | "name": "symfony/polyfill-intl-grapheme", 1496 | "version": "v1.27.0", 1497 | "source": { 1498 | "type": "git", 1499 | "url": "https://github.com/symfony/polyfill-intl-grapheme.git", 1500 | "reference": "511a08c03c1960e08a883f4cffcacd219b758354" 1501 | }, 1502 | "dist": { 1503 | "type": "zip", 1504 | "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/511a08c03c1960e08a883f4cffcacd219b758354", 1505 | "reference": "511a08c03c1960e08a883f4cffcacd219b758354", 1506 | "shasum": "" 1507 | }, 1508 | "require": { 1509 | "php": ">=7.1" 1510 | }, 1511 | "suggest": { 1512 | "ext-intl": "For best performance" 1513 | }, 1514 | "type": "library", 1515 | "extra": { 1516 | "branch-alias": { 1517 | "dev-main": "1.27-dev" 1518 | }, 1519 | "thanks": { 1520 | "name": "symfony/polyfill", 1521 | "url": "https://github.com/symfony/polyfill" 1522 | } 1523 | }, 1524 | "autoload": { 1525 | "files": [ 1526 | "bootstrap.php" 1527 | ], 1528 | "psr-4": { 1529 | "Symfony\\Polyfill\\Intl\\Grapheme\\": "" 1530 | } 1531 | }, 1532 | "notification-url": "https://packagist.org/downloads/", 1533 | "license": [ 1534 | "MIT" 1535 | ], 1536 | "authors": [ 1537 | { 1538 | "name": "Nicolas Grekas", 1539 | "email": "p@tchwork.com" 1540 | }, 1541 | { 1542 | "name": "Symfony Community", 1543 | "homepage": "https://symfony.com/contributors" 1544 | } 1545 | ], 1546 | "description": "Symfony polyfill for intl's grapheme_* functions", 1547 | "homepage": "https://symfony.com", 1548 | "keywords": [ 1549 | "compatibility", 1550 | "grapheme", 1551 | "intl", 1552 | "polyfill", 1553 | "portable", 1554 | "shim" 1555 | ], 1556 | "support": { 1557 | "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.27.0" 1558 | }, 1559 | "funding": [ 1560 | { 1561 | "url": "https://symfony.com/sponsor", 1562 | "type": "custom" 1563 | }, 1564 | { 1565 | "url": "https://github.com/fabpot", 1566 | "type": "github" 1567 | }, 1568 | { 1569 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1570 | "type": "tidelift" 1571 | } 1572 | ], 1573 | "time": "2022-11-03T14:55:06+00:00" 1574 | }, 1575 | { 1576 | "name": "symfony/polyfill-intl-normalizer", 1577 | "version": "v1.27.0", 1578 | "source": { 1579 | "type": "git", 1580 | "url": "https://github.com/symfony/polyfill-intl-normalizer.git", 1581 | "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" 1582 | }, 1583 | "dist": { 1584 | "type": "zip", 1585 | "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", 1586 | "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", 1587 | "shasum": "" 1588 | }, 1589 | "require": { 1590 | "php": ">=7.1" 1591 | }, 1592 | "suggest": { 1593 | "ext-intl": "For best performance" 1594 | }, 1595 | "type": "library", 1596 | "extra": { 1597 | "branch-alias": { 1598 | "dev-main": "1.27-dev" 1599 | }, 1600 | "thanks": { 1601 | "name": "symfony/polyfill", 1602 | "url": "https://github.com/symfony/polyfill" 1603 | } 1604 | }, 1605 | "autoload": { 1606 | "files": [ 1607 | "bootstrap.php" 1608 | ], 1609 | "psr-4": { 1610 | "Symfony\\Polyfill\\Intl\\Normalizer\\": "" 1611 | }, 1612 | "classmap": [ 1613 | "Resources/stubs" 1614 | ] 1615 | }, 1616 | "notification-url": "https://packagist.org/downloads/", 1617 | "license": [ 1618 | "MIT" 1619 | ], 1620 | "authors": [ 1621 | { 1622 | "name": "Nicolas Grekas", 1623 | "email": "p@tchwork.com" 1624 | }, 1625 | { 1626 | "name": "Symfony Community", 1627 | "homepage": "https://symfony.com/contributors" 1628 | } 1629 | ], 1630 | "description": "Symfony polyfill for intl's Normalizer class and related functions", 1631 | "homepage": "https://symfony.com", 1632 | "keywords": [ 1633 | "compatibility", 1634 | "intl", 1635 | "normalizer", 1636 | "polyfill", 1637 | "portable", 1638 | "shim" 1639 | ], 1640 | "support": { 1641 | "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" 1642 | }, 1643 | "funding": [ 1644 | { 1645 | "url": "https://symfony.com/sponsor", 1646 | "type": "custom" 1647 | }, 1648 | { 1649 | "url": "https://github.com/fabpot", 1650 | "type": "github" 1651 | }, 1652 | { 1653 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1654 | "type": "tidelift" 1655 | } 1656 | ], 1657 | "time": "2022-11-03T14:55:06+00:00" 1658 | }, 1659 | { 1660 | "name": "symfony/polyfill-mbstring", 1661 | "version": "v1.27.0", 1662 | "source": { 1663 | "type": "git", 1664 | "url": "https://github.com/symfony/polyfill-mbstring.git", 1665 | "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" 1666 | }, 1667 | "dist": { 1668 | "type": "zip", 1669 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", 1670 | "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", 1671 | "shasum": "" 1672 | }, 1673 | "require": { 1674 | "php": ">=7.1" 1675 | }, 1676 | "provide": { 1677 | "ext-mbstring": "*" 1678 | }, 1679 | "suggest": { 1680 | "ext-mbstring": "For best performance" 1681 | }, 1682 | "type": "library", 1683 | "extra": { 1684 | "branch-alias": { 1685 | "dev-main": "1.27-dev" 1686 | }, 1687 | "thanks": { 1688 | "name": "symfony/polyfill", 1689 | "url": "https://github.com/symfony/polyfill" 1690 | } 1691 | }, 1692 | "autoload": { 1693 | "files": [ 1694 | "bootstrap.php" 1695 | ], 1696 | "psr-4": { 1697 | "Symfony\\Polyfill\\Mbstring\\": "" 1698 | } 1699 | }, 1700 | "notification-url": "https://packagist.org/downloads/", 1701 | "license": [ 1702 | "MIT" 1703 | ], 1704 | "authors": [ 1705 | { 1706 | "name": "Nicolas Grekas", 1707 | "email": "p@tchwork.com" 1708 | }, 1709 | { 1710 | "name": "Symfony Community", 1711 | "homepage": "https://symfony.com/contributors" 1712 | } 1713 | ], 1714 | "description": "Symfony polyfill for the Mbstring extension", 1715 | "homepage": "https://symfony.com", 1716 | "keywords": [ 1717 | "compatibility", 1718 | "mbstring", 1719 | "polyfill", 1720 | "portable", 1721 | "shim" 1722 | ], 1723 | "support": { 1724 | "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" 1725 | }, 1726 | "funding": [ 1727 | { 1728 | "url": "https://symfony.com/sponsor", 1729 | "type": "custom" 1730 | }, 1731 | { 1732 | "url": "https://github.com/fabpot", 1733 | "type": "github" 1734 | }, 1735 | { 1736 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1737 | "type": "tidelift" 1738 | } 1739 | ], 1740 | "time": "2022-11-03T14:55:06+00:00" 1741 | }, 1742 | { 1743 | "name": "symfony/polyfill-php80", 1744 | "version": "v1.27.0", 1745 | "source": { 1746 | "type": "git", 1747 | "url": "https://github.com/symfony/polyfill-php80.git", 1748 | "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" 1749 | }, 1750 | "dist": { 1751 | "type": "zip", 1752 | "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", 1753 | "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", 1754 | "shasum": "" 1755 | }, 1756 | "require": { 1757 | "php": ">=7.1" 1758 | }, 1759 | "type": "library", 1760 | "extra": { 1761 | "branch-alias": { 1762 | "dev-main": "1.27-dev" 1763 | }, 1764 | "thanks": { 1765 | "name": "symfony/polyfill", 1766 | "url": "https://github.com/symfony/polyfill" 1767 | } 1768 | }, 1769 | "autoload": { 1770 | "files": [ 1771 | "bootstrap.php" 1772 | ], 1773 | "psr-4": { 1774 | "Symfony\\Polyfill\\Php80\\": "" 1775 | }, 1776 | "classmap": [ 1777 | "Resources/stubs" 1778 | ] 1779 | }, 1780 | "notification-url": "https://packagist.org/downloads/", 1781 | "license": [ 1782 | "MIT" 1783 | ], 1784 | "authors": [ 1785 | { 1786 | "name": "Ion Bazan", 1787 | "email": "ion.bazan@gmail.com" 1788 | }, 1789 | { 1790 | "name": "Nicolas Grekas", 1791 | "email": "p@tchwork.com" 1792 | }, 1793 | { 1794 | "name": "Symfony Community", 1795 | "homepage": "https://symfony.com/contributors" 1796 | } 1797 | ], 1798 | "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", 1799 | "homepage": "https://symfony.com", 1800 | "keywords": [ 1801 | "compatibility", 1802 | "polyfill", 1803 | "portable", 1804 | "shim" 1805 | ], 1806 | "support": { 1807 | "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" 1808 | }, 1809 | "funding": [ 1810 | { 1811 | "url": "https://symfony.com/sponsor", 1812 | "type": "custom" 1813 | }, 1814 | { 1815 | "url": "https://github.com/fabpot", 1816 | "type": "github" 1817 | }, 1818 | { 1819 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1820 | "type": "tidelift" 1821 | } 1822 | ], 1823 | "time": "2022-11-03T14:55:06+00:00" 1824 | }, 1825 | { 1826 | "name": "symfony/polyfill-php81", 1827 | "version": "v1.27.0", 1828 | "source": { 1829 | "type": "git", 1830 | "url": "https://github.com/symfony/polyfill-php81.git", 1831 | "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a" 1832 | }, 1833 | "dist": { 1834 | "type": "zip", 1835 | "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/707403074c8ea6e2edaf8794b0157a0bfa52157a", 1836 | "reference": "707403074c8ea6e2edaf8794b0157a0bfa52157a", 1837 | "shasum": "" 1838 | }, 1839 | "require": { 1840 | "php": ">=7.1" 1841 | }, 1842 | "type": "library", 1843 | "extra": { 1844 | "branch-alias": { 1845 | "dev-main": "1.27-dev" 1846 | }, 1847 | "thanks": { 1848 | "name": "symfony/polyfill", 1849 | "url": "https://github.com/symfony/polyfill" 1850 | } 1851 | }, 1852 | "autoload": { 1853 | "files": [ 1854 | "bootstrap.php" 1855 | ], 1856 | "psr-4": { 1857 | "Symfony\\Polyfill\\Php81\\": "" 1858 | }, 1859 | "classmap": [ 1860 | "Resources/stubs" 1861 | ] 1862 | }, 1863 | "notification-url": "https://packagist.org/downloads/", 1864 | "license": [ 1865 | "MIT" 1866 | ], 1867 | "authors": [ 1868 | { 1869 | "name": "Nicolas Grekas", 1870 | "email": "p@tchwork.com" 1871 | }, 1872 | { 1873 | "name": "Symfony Community", 1874 | "homepage": "https://symfony.com/contributors" 1875 | } 1876 | ], 1877 | "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", 1878 | "homepage": "https://symfony.com", 1879 | "keywords": [ 1880 | "compatibility", 1881 | "polyfill", 1882 | "portable", 1883 | "shim" 1884 | ], 1885 | "support": { 1886 | "source": "https://github.com/symfony/polyfill-php81/tree/v1.27.0" 1887 | }, 1888 | "funding": [ 1889 | { 1890 | "url": "https://symfony.com/sponsor", 1891 | "type": "custom" 1892 | }, 1893 | { 1894 | "url": "https://github.com/fabpot", 1895 | "type": "github" 1896 | }, 1897 | { 1898 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1899 | "type": "tidelift" 1900 | } 1901 | ], 1902 | "time": "2022-11-03T14:55:06+00:00" 1903 | }, 1904 | { 1905 | "name": "symfony/process", 1906 | "version": "v6.0.11", 1907 | "source": { 1908 | "type": "git", 1909 | "url": "https://github.com/symfony/process.git", 1910 | "reference": "44270a08ccb664143dede554ff1c00aaa2247a43" 1911 | }, 1912 | "dist": { 1913 | "type": "zip", 1914 | "url": "https://api.github.com/repos/symfony/process/zipball/44270a08ccb664143dede554ff1c00aaa2247a43", 1915 | "reference": "44270a08ccb664143dede554ff1c00aaa2247a43", 1916 | "shasum": "" 1917 | }, 1918 | "require": { 1919 | "php": ">=8.0.2" 1920 | }, 1921 | "type": "library", 1922 | "autoload": { 1923 | "psr-4": { 1924 | "Symfony\\Component\\Process\\": "" 1925 | }, 1926 | "exclude-from-classmap": [ 1927 | "/Tests/" 1928 | ] 1929 | }, 1930 | "notification-url": "https://packagist.org/downloads/", 1931 | "license": [ 1932 | "MIT" 1933 | ], 1934 | "authors": [ 1935 | { 1936 | "name": "Fabien Potencier", 1937 | "email": "fabien@symfony.com" 1938 | }, 1939 | { 1940 | "name": "Symfony Community", 1941 | "homepage": "https://symfony.com/contributors" 1942 | } 1943 | ], 1944 | "description": "Executes commands in sub-processes", 1945 | "homepage": "https://symfony.com", 1946 | "support": { 1947 | "source": "https://github.com/symfony/process/tree/v6.0.11" 1948 | }, 1949 | "funding": [ 1950 | { 1951 | "url": "https://symfony.com/sponsor", 1952 | "type": "custom" 1953 | }, 1954 | { 1955 | "url": "https://github.com/fabpot", 1956 | "type": "github" 1957 | }, 1958 | { 1959 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 1960 | "type": "tidelift" 1961 | } 1962 | ], 1963 | "time": "2022-06-27T17:10:44+00:00" 1964 | }, 1965 | { 1966 | "name": "symfony/service-contracts", 1967 | "version": "v3.0.2", 1968 | "source": { 1969 | "type": "git", 1970 | "url": "https://github.com/symfony/service-contracts.git", 1971 | "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66" 1972 | }, 1973 | "dist": { 1974 | "type": "zip", 1975 | "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d78d39c1599bd1188b8e26bb341da52c3c6d8a66", 1976 | "reference": "d78d39c1599bd1188b8e26bb341da52c3c6d8a66", 1977 | "shasum": "" 1978 | }, 1979 | "require": { 1980 | "php": ">=8.0.2", 1981 | "psr/container": "^2.0" 1982 | }, 1983 | "conflict": { 1984 | "ext-psr": "<1.1|>=2" 1985 | }, 1986 | "suggest": { 1987 | "symfony/service-implementation": "" 1988 | }, 1989 | "type": "library", 1990 | "extra": { 1991 | "branch-alias": { 1992 | "dev-main": "3.0-dev" 1993 | }, 1994 | "thanks": { 1995 | "name": "symfony/contracts", 1996 | "url": "https://github.com/symfony/contracts" 1997 | } 1998 | }, 1999 | "autoload": { 2000 | "psr-4": { 2001 | "Symfony\\Contracts\\Service\\": "" 2002 | } 2003 | }, 2004 | "notification-url": "https://packagist.org/downloads/", 2005 | "license": [ 2006 | "MIT" 2007 | ], 2008 | "authors": [ 2009 | { 2010 | "name": "Nicolas Grekas", 2011 | "email": "p@tchwork.com" 2012 | }, 2013 | { 2014 | "name": "Symfony Community", 2015 | "homepage": "https://symfony.com/contributors" 2016 | } 2017 | ], 2018 | "description": "Generic abstractions related to writing services", 2019 | "homepage": "https://symfony.com", 2020 | "keywords": [ 2021 | "abstractions", 2022 | "contracts", 2023 | "decoupling", 2024 | "interfaces", 2025 | "interoperability", 2026 | "standards" 2027 | ], 2028 | "support": { 2029 | "source": "https://github.com/symfony/service-contracts/tree/v3.0.2" 2030 | }, 2031 | "funding": [ 2032 | { 2033 | "url": "https://symfony.com/sponsor", 2034 | "type": "custom" 2035 | }, 2036 | { 2037 | "url": "https://github.com/fabpot", 2038 | "type": "github" 2039 | }, 2040 | { 2041 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2042 | "type": "tidelift" 2043 | } 2044 | ], 2045 | "time": "2022-05-30T19:17:58+00:00" 2046 | }, 2047 | { 2048 | "name": "symfony/stopwatch", 2049 | "version": "v6.0.13", 2050 | "source": { 2051 | "type": "git", 2052 | "url": "https://github.com/symfony/stopwatch.git", 2053 | "reference": "7554fde6848af5ef1178f8ccbdbdb8ae1092c70a" 2054 | }, 2055 | "dist": { 2056 | "type": "zip", 2057 | "url": "https://api.github.com/repos/symfony/stopwatch/zipball/7554fde6848af5ef1178f8ccbdbdb8ae1092c70a", 2058 | "reference": "7554fde6848af5ef1178f8ccbdbdb8ae1092c70a", 2059 | "shasum": "" 2060 | }, 2061 | "require": { 2062 | "php": ">=8.0.2", 2063 | "symfony/service-contracts": "^1|^2|^3" 2064 | }, 2065 | "type": "library", 2066 | "autoload": { 2067 | "psr-4": { 2068 | "Symfony\\Component\\Stopwatch\\": "" 2069 | }, 2070 | "exclude-from-classmap": [ 2071 | "/Tests/" 2072 | ] 2073 | }, 2074 | "notification-url": "https://packagist.org/downloads/", 2075 | "license": [ 2076 | "MIT" 2077 | ], 2078 | "authors": [ 2079 | { 2080 | "name": "Fabien Potencier", 2081 | "email": "fabien@symfony.com" 2082 | }, 2083 | { 2084 | "name": "Symfony Community", 2085 | "homepage": "https://symfony.com/contributors" 2086 | } 2087 | ], 2088 | "description": "Provides a way to profile code", 2089 | "homepage": "https://symfony.com", 2090 | "support": { 2091 | "source": "https://github.com/symfony/stopwatch/tree/v6.0.13" 2092 | }, 2093 | "funding": [ 2094 | { 2095 | "url": "https://symfony.com/sponsor", 2096 | "type": "custom" 2097 | }, 2098 | { 2099 | "url": "https://github.com/fabpot", 2100 | "type": "github" 2101 | }, 2102 | { 2103 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2104 | "type": "tidelift" 2105 | } 2106 | ], 2107 | "time": "2022-09-28T15:52:47+00:00" 2108 | }, 2109 | { 2110 | "name": "symfony/string", 2111 | "version": "v6.0.17", 2112 | "source": { 2113 | "type": "git", 2114 | "url": "https://github.com/symfony/string.git", 2115 | "reference": "3f57003dd8a67ed76870cc03092f8501db7788d9" 2116 | }, 2117 | "dist": { 2118 | "type": "zip", 2119 | "url": "https://api.github.com/repos/symfony/string/zipball/3f57003dd8a67ed76870cc03092f8501db7788d9", 2120 | "reference": "3f57003dd8a67ed76870cc03092f8501db7788d9", 2121 | "shasum": "" 2122 | }, 2123 | "require": { 2124 | "php": ">=8.0.2", 2125 | "symfony/polyfill-ctype": "~1.8", 2126 | "symfony/polyfill-intl-grapheme": "~1.0", 2127 | "symfony/polyfill-intl-normalizer": "~1.0", 2128 | "symfony/polyfill-mbstring": "~1.0" 2129 | }, 2130 | "conflict": { 2131 | "symfony/translation-contracts": "<2.0" 2132 | }, 2133 | "require-dev": { 2134 | "symfony/error-handler": "^5.4|^6.0", 2135 | "symfony/http-client": "^5.4|^6.0", 2136 | "symfony/translation-contracts": "^2.0|^3.0", 2137 | "symfony/var-exporter": "^5.4|^6.0" 2138 | }, 2139 | "type": "library", 2140 | "autoload": { 2141 | "files": [ 2142 | "Resources/functions.php" 2143 | ], 2144 | "psr-4": { 2145 | "Symfony\\Component\\String\\": "" 2146 | }, 2147 | "exclude-from-classmap": [ 2148 | "/Tests/" 2149 | ] 2150 | }, 2151 | "notification-url": "https://packagist.org/downloads/", 2152 | "license": [ 2153 | "MIT" 2154 | ], 2155 | "authors": [ 2156 | { 2157 | "name": "Nicolas Grekas", 2158 | "email": "p@tchwork.com" 2159 | }, 2160 | { 2161 | "name": "Symfony Community", 2162 | "homepage": "https://symfony.com/contributors" 2163 | } 2164 | ], 2165 | "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", 2166 | "homepage": "https://symfony.com", 2167 | "keywords": [ 2168 | "grapheme", 2169 | "i18n", 2170 | "string", 2171 | "unicode", 2172 | "utf-8", 2173 | "utf8" 2174 | ], 2175 | "support": { 2176 | "source": "https://github.com/symfony/string/tree/v6.0.17" 2177 | }, 2178 | "funding": [ 2179 | { 2180 | "url": "https://symfony.com/sponsor", 2181 | "type": "custom" 2182 | }, 2183 | { 2184 | "url": "https://github.com/fabpot", 2185 | "type": "github" 2186 | }, 2187 | { 2188 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 2189 | "type": "tidelift" 2190 | } 2191 | ], 2192 | "time": "2022-12-14T15:52:41+00:00" 2193 | } 2194 | ], 2195 | "aliases": [], 2196 | "minimum-stability": "stable", 2197 | "stability-flags": {}, 2198 | "prefer-stable": false, 2199 | "prefer-lowest": false, 2200 | "platform": { 2201 | "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0" 2202 | }, 2203 | "platform-dev": {}, 2204 | "plugin-api-version": "2.6.0" 2205 | } 2206 | --------------------------------------------------------------------------------