}
},
"require": {
- "cuyz/valinor": "^1.5.0",
+ "cuyz/valinor": "^1.6.0",
"dragonmantank/cron-expression": "^3.3.3",
"erusev/parsedown": "^1.7.4",
"ezyang/htmlpurifier": "^4.16",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "01e02021c96e84abaabbe0d212001568",
+ "content-hash": "7044d6daf69850dcfd60f482ab45327c",
"packages": [
{
"name": "cuyz/valinor",
- "version": "1.5.0",
+ "version": "1.6.0",
"source": {
"type": "git",
"url": "https://github.com/CuyZ/Valinor.git",
- "reference": "668cd3f0f95c57d75981a31d63b5b1422606bc7e"
+ "reference": "f3f3429d90be77f59903923f9bb5ce93c484dfd7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/CuyZ/Valinor/zipball/668cd3f0f95c57d75981a31d63b5b1422606bc7e",
- "reference": "668cd3f0f95c57d75981a31d63b5b1422606bc7e",
+ "url": "https://api.github.com/repos/CuyZ/Valinor/zipball/f3f3429d90be77f59903923f9bb5ce93c484dfd7",
+ "reference": "f3f3429d90be77f59903923f9bb5ce93c484dfd7",
"shasum": ""
},
"require": {
"composer-runtime-api": "^2.0",
- "php": "~8.0.0 || ~8.1.0 || ~8.2.0",
+ "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0",
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
},
"require-dev": {
"phpstan/phpstan-phpunit": "^1.0",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^9.5",
- "rector/rector": "~0.15.0",
+ "rector/rector": "~0.17.0",
"vimeo/psalm": "^5.0"
},
"type": "library",
],
"support": {
"issues": "https://github.com/CuyZ/Valinor/issues",
- "source": "https://github.com/CuyZ/Valinor/tree/1.5.0"
+ "source": "https://github.com/CuyZ/Valinor/tree/1.6.0"
},
"funding": [
{
"type": "github"
}
],
- "time": "2023-08-07T18:29:08+00:00"
+ "time": "2023-08-25T10:26:38+00:00"
},
{
"name": "dragonmantank/cron-expression",
'CuyZ\\Valinor\\Cache\\FileWatchingCache' => $vendorDir . '/cuyz/valinor/src/Cache/FileWatchingCache.php',
'CuyZ\\Valinor\\Cache\\KeySanitizerCache' => $vendorDir . '/cuyz/valinor/src/Cache/KeySanitizerCache.php',
'CuyZ\\Valinor\\Cache\\RuntimeCache' => $vendorDir . '/cuyz/valinor/src/Cache/RuntimeCache.php',
+ 'CuyZ\\Valinor\\Cache\\WarmupCache' => $vendorDir . '/cuyz/valinor/src/Cache/WarmupCache.php',
'CuyZ\\Valinor\\Cache\\Warmup\\RecursiveCacheWarmupService' => $vendorDir . '/cuyz/valinor/src/Cache/Warmup/RecursiveCacheWarmupService.php',
'CuyZ\\Valinor\\Definition\\Attributes' => $vendorDir . '/cuyz/valinor/src/Definition/Attributes.php',
'CuyZ\\Valinor\\Definition\\AttributesContainer' => $vendorDir . '/cuyz/valinor/src/Definition/AttributesContainer.php',
'CuyZ\\Valinor\\Type\\Parser\\CachedParser' => $vendorDir . '/cuyz/valinor/src/Type/Parser/CachedParser.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Constant\\ClassConstantCaseNotFound' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Constant/ClassConstantCaseNotFound.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Constant\\MissingClassConstantCase' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Constant/MissingClassConstantCase.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Constant\\MissingClassConstantColon' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Constant/MissingClassConstantColon.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Constant\\MissingSpecificClassConstantCase' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Constant/MissingSpecificClassConstantCase.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Enum\\EnumCaseNotFound' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Enum/EnumCaseNotFound.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Enum\\MissingEnumCase' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Enum/MissingEnumCase.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Enum\\MissingEnumColon' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Enum/MissingEnumColon.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Enum\\MissingSpecificEnumCase' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Enum/MissingSpecificEnumCase.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\AssignedGenericNotFound' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Generic/AssignedGenericNotFound.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\CannotAssignGeneric' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Generic/CannotAssignGeneric.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Scalar\\SameValueForIntegerRange' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Scalar/SameValueForIntegerRange.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Template\\DuplicatedTemplateName' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Template/DuplicatedTemplateName.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Template\\InvalidClassTemplate' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Template/InvalidClassTemplate.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Template\\InvalidTemplate' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Template/InvalidTemplate.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Template\\InvalidTemplateType' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Template/InvalidTemplateType.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\UnknownSymbol' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/UnknownSymbol.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\LexingTypeParserFactory' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Factory/LexingTypeParserFactory.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\Specifications\\AliasSpecification' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Factory/Specifications/AliasSpecification.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\Specifications\\TypeAliasAssignerSpecification' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Factory/Specifications/TypeAliasAssignerSpecification.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\Specifications\\TypeParserSpecification' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Factory/Specifications/TypeParserSpecification.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\TypeParserFactory' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Factory/TypeParserFactory.php',
- 'CuyZ\\Valinor\\Type\\Parser\\LazyParser' => $vendorDir . '/cuyz/valinor/src/Type/Parser/LazyParser.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\AdvancedClassLexer' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/AdvancedClassLexer.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\AliasLexer' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/AliasLexer.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\ClassContextLexer' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/ClassContextLexer.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\ClosingSquareBracketToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/ClosingSquareBracketToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\ColonToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/ColonToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\CommaToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/CommaToken.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\DoubleColonToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/DoubleColonToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\EnumNameToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/EnumNameToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\FloatValueToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/FloatValueToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\IntegerToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/IntegerToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\TypeAliasLexer' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/TypeAliasLexer.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\TypeLexer' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/TypeLexer.php',
'CuyZ\\Valinor\\Type\\Parser\\LexingParser' => $vendorDir . '/cuyz/valinor/src/Type/Parser/LexingParser.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Template\\BasicTemplateParser' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Template/BasicTemplateParser.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Template\\TemplateParser' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Template/TemplateParser.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\ParserSymbols' => $vendorDir . '/cuyz/valinor/src/Type/Parser/ParserSymbols.php',
'CuyZ\\Valinor\\Type\\Parser\\TypeParser' => $vendorDir . '/cuyz/valinor/src/Type/Parser/TypeParser.php',
'CuyZ\\Valinor\\Type\\ScalarType' => $vendorDir . '/cuyz/valinor/src/Type/ScalarType.php',
'CuyZ\\Valinor\\Type\\StringType' => $vendorDir . '/cuyz/valinor/src/Type/StringType.php',
'CuyZ\\Valinor\\Utility\\PermissiveTypeFound' => $vendorDir . '/cuyz/valinor/src/Utility/PermissiveTypeFound.php',
'CuyZ\\Valinor\\Utility\\Priority\\HasPriority' => $vendorDir . '/cuyz/valinor/src/Utility/Priority/HasPriority.php',
'CuyZ\\Valinor\\Utility\\Priority\\PrioritizedList' => $vendorDir . '/cuyz/valinor/src/Utility/Priority/PrioritizedList.php',
+ 'CuyZ\\Valinor\\Utility\\Reflection\\DocParser' => $vendorDir . '/cuyz/valinor/src/Utility/Reflection/DocParser.php',
'CuyZ\\Valinor\\Utility\\Reflection\\PhpParser' => $vendorDir . '/cuyz/valinor/src/Utility/Reflection/PhpParser.php',
'CuyZ\\Valinor\\Utility\\Reflection\\Reflection' => $vendorDir . '/cuyz/valinor/src/Utility/Reflection/Reflection.php',
'CuyZ\\Valinor\\Utility\\Reflection\\TokenParser' => $vendorDir . '/cuyz/valinor/src/Utility/Reflection/TokenParser.php',
'CuyZ\\Valinor\\Cache\\FileWatchingCache' => __DIR__ . '/..' . '/cuyz/valinor/src/Cache/FileWatchingCache.php',
'CuyZ\\Valinor\\Cache\\KeySanitizerCache' => __DIR__ . '/..' . '/cuyz/valinor/src/Cache/KeySanitizerCache.php',
'CuyZ\\Valinor\\Cache\\RuntimeCache' => __DIR__ . '/..' . '/cuyz/valinor/src/Cache/RuntimeCache.php',
+ 'CuyZ\\Valinor\\Cache\\WarmupCache' => __DIR__ . '/..' . '/cuyz/valinor/src/Cache/WarmupCache.php',
'CuyZ\\Valinor\\Cache\\Warmup\\RecursiveCacheWarmupService' => __DIR__ . '/..' . '/cuyz/valinor/src/Cache/Warmup/RecursiveCacheWarmupService.php',
'CuyZ\\Valinor\\Definition\\Attributes' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Attributes.php',
'CuyZ\\Valinor\\Definition\\AttributesContainer' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/AttributesContainer.php',
'CuyZ\\Valinor\\Type\\Parser\\CachedParser' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/CachedParser.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Constant\\ClassConstantCaseNotFound' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Constant/ClassConstantCaseNotFound.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Constant\\MissingClassConstantCase' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Constant/MissingClassConstantCase.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Constant\\MissingClassConstantColon' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Constant/MissingClassConstantColon.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Constant\\MissingSpecificClassConstantCase' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Constant/MissingSpecificClassConstantCase.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Enum\\EnumCaseNotFound' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Enum/EnumCaseNotFound.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Enum\\MissingEnumCase' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Enum/MissingEnumCase.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Enum\\MissingEnumColon' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Enum/MissingEnumColon.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Enum\\MissingSpecificEnumCase' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Enum/MissingSpecificEnumCase.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\AssignedGenericNotFound' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Generic/AssignedGenericNotFound.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\CannotAssignGeneric' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Generic/CannotAssignGeneric.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Scalar\\SameValueForIntegerRange' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Scalar/SameValueForIntegerRange.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Template\\DuplicatedTemplateName' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Template/DuplicatedTemplateName.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Template\\InvalidClassTemplate' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Template/InvalidClassTemplate.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Template\\InvalidTemplate' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Template/InvalidTemplate.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Template\\InvalidTemplateType' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Template/InvalidTemplateType.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\UnknownSymbol' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/UnknownSymbol.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\LexingTypeParserFactory' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Factory/LexingTypeParserFactory.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\Specifications\\AliasSpecification' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Factory/Specifications/AliasSpecification.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\Specifications\\TypeAliasAssignerSpecification' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Factory/Specifications/TypeAliasAssignerSpecification.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\Specifications\\TypeParserSpecification' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Factory/Specifications/TypeParserSpecification.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\TypeParserFactory' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Factory/TypeParserFactory.php',
- 'CuyZ\\Valinor\\Type\\Parser\\LazyParser' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/LazyParser.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\AdvancedClassLexer' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/AdvancedClassLexer.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\AliasLexer' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/AliasLexer.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\ClassContextLexer' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/ClassContextLexer.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\ClosingSquareBracketToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/ClosingSquareBracketToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\ColonToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/ColonToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\CommaToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/CommaToken.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\DoubleColonToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/DoubleColonToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\EnumNameToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/EnumNameToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\FloatValueToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/FloatValueToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\IntegerToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/IntegerToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\TypeAliasLexer' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/TypeAliasLexer.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\TypeLexer' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/TypeLexer.php',
'CuyZ\\Valinor\\Type\\Parser\\LexingParser' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/LexingParser.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Template\\BasicTemplateParser' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Template/BasicTemplateParser.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Template\\TemplateParser' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Template/TemplateParser.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\ParserSymbols' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/ParserSymbols.php',
'CuyZ\\Valinor\\Type\\Parser\\TypeParser' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/TypeParser.php',
'CuyZ\\Valinor\\Type\\ScalarType' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/ScalarType.php',
'CuyZ\\Valinor\\Type\\StringType' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/StringType.php',
'CuyZ\\Valinor\\Utility\\PermissiveTypeFound' => __DIR__ . '/..' . '/cuyz/valinor/src/Utility/PermissiveTypeFound.php',
'CuyZ\\Valinor\\Utility\\Priority\\HasPriority' => __DIR__ . '/..' . '/cuyz/valinor/src/Utility/Priority/HasPriority.php',
'CuyZ\\Valinor\\Utility\\Priority\\PrioritizedList' => __DIR__ . '/..' . '/cuyz/valinor/src/Utility/Priority/PrioritizedList.php',
+ 'CuyZ\\Valinor\\Utility\\Reflection\\DocParser' => __DIR__ . '/..' . '/cuyz/valinor/src/Utility/Reflection/DocParser.php',
'CuyZ\\Valinor\\Utility\\Reflection\\PhpParser' => __DIR__ . '/..' . '/cuyz/valinor/src/Utility/Reflection/PhpParser.php',
'CuyZ\\Valinor\\Utility\\Reflection\\Reflection' => __DIR__ . '/..' . '/cuyz/valinor/src/Utility/Reflection/Reflection.php',
'CuyZ\\Valinor\\Utility\\Reflection\\TokenParser' => __DIR__ . '/..' . '/cuyz/valinor/src/Utility/Reflection/TokenParser.php',
"packages": [
{
"name": "cuyz/valinor",
- "version": "1.5.0",
- "version_normalized": "1.5.0.0",
+ "version": "1.6.0",
+ "version_normalized": "1.6.0.0",
"source": {
"type": "git",
"url": "https://github.com/CuyZ/Valinor.git",
- "reference": "668cd3f0f95c57d75981a31d63b5b1422606bc7e"
+ "reference": "f3f3429d90be77f59903923f9bb5ce93c484dfd7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/CuyZ/Valinor/zipball/668cd3f0f95c57d75981a31d63b5b1422606bc7e",
- "reference": "668cd3f0f95c57d75981a31d63b5b1422606bc7e",
+ "url": "https://api.github.com/repos/CuyZ/Valinor/zipball/f3f3429d90be77f59903923f9bb5ce93c484dfd7",
+ "reference": "f3f3429d90be77f59903923f9bb5ce93c484dfd7",
"shasum": ""
},
"require": {
"composer-runtime-api": "^2.0",
- "php": "~8.0.0 || ~8.1.0 || ~8.2.0",
+ "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0",
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
},
"require-dev": {
"phpstan/phpstan-phpunit": "^1.0",
"phpstan/phpstan-strict-rules": "^1.0",
"phpunit/phpunit": "^9.5",
- "rector/rector": "~0.15.0",
+ "rector/rector": "~0.17.0",
"vimeo/psalm": "^5.0"
},
- "time": "2023-08-07T18:29:08+00:00",
+ "time": "2023-08-25T10:26:38+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
],
"support": {
"issues": "https://github.com/CuyZ/Valinor/issues",
- "source": "https://github.com/CuyZ/Valinor/tree/1.5.0"
+ "source": "https://github.com/CuyZ/Valinor/tree/1.6.0"
},
"funding": [
{
'dev_requirement' => false,
),
'cuyz/valinor' => array(
- 'pretty_version' => '1.5.0',
- 'version' => '1.5.0.0',
- 'reference' => '668cd3f0f95c57d75981a31d63b5b1422606bc7e',
+ 'pretty_version' => '1.6.0',
+ 'version' => '1.6.0.0',
+ 'reference' => 'f3f3429d90be77f59903923f9bb5ce93c484dfd7',
'type' => 'library',
'install_path' => __DIR__ . '/../cuyz/valinor',
'aliases' => array(),
}
],
"require": {
- "php": "~8.0.0 || ~8.1.0 || ~8.2.0",
+ "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0",
"composer-runtime-api": "^2.0",
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
},
"marcocesarato/php-conventional-changelog": "^1.12",
"vimeo/psalm": "^5.0",
"mikey179/vfsstream": "^1.6.10",
- "rector/rector": "~0.15.0"
+ "rector/rector": "~0.17.0"
},
"autoload": {
"psr-4": {
"rector"
],
"mutation": [
+ "@putenv XDEBUG_MODE=off",
"infection --threads=max --git-diff-lines"
],
"doc": [
* @internal
*
* @template EntryType
- * @implements CacheInterface<EntryType>
+ * @implements WarmupCache<EntryType>
*/
-final class ChainCache implements CacheInterface
+final class ChainCache implements WarmupCache
{
/** @var array<CacheInterface<EntryType>> */
private array $delegates;
$this->count = count($delegates);
}
+ public function warmup(): void
+ {
+ foreach ($this->delegates as $delegate) {
+ if ($delegate instanceof WarmupCache) {
+ $delegate->warmup();
+ }
+ }
+ }
+
public function get($key, $default = null): mixed
{
foreach ($this->delegates as $i => $delegate) {
use CuyZ\Valinor\Cache\Exception\CacheDirectoryNotWritable;
use CuyZ\Valinor\Cache\Exception\CompiledPhpCacheFileNotWritten;
use CuyZ\Valinor\Cache\Exception\CorruptedCompiledPhpCacheFile;
+use CuyZ\Valinor\Cache\WarmupCache;
use DateInterval;
use DateTime;
use Error;
use FilesystemIterator;
-use Psr\SimpleCache\CacheInterface;
use Traversable;
use function bin2hex;
* @internal
*
* @template EntryType
- * @implements CacheInterface<EntryType>
+ * @implements WarmupCache<EntryType>
*/
-final class CompiledPhpFileCache implements CacheInterface
+final class CompiledPhpFileCache implements WarmupCache
{
private const TEMPORARY_DIR_PERMISSION = 510;
private CacheCompiler $compiler
) {}
+ public function warmup(): void
+ {
+ $this->createTemporaryDir();
+ }
+
public function has($key): bool
{
$filename = $this->path($key);
$code = $this->compile($value, $ttl);
- $tmpDir = $this->cacheDir . DIRECTORY_SEPARATOR . '.valinor.tmp';
-
- if (! is_dir($tmpDir) && ! @mkdir($tmpDir, self::TEMPORARY_DIR_PERMISSION, true)) {
- throw new CacheDirectoryNotWritable($this->cacheDir);
- }
+ $tmpDir = $this->createTemporaryDir();
/** @infection-ignore-all */
$tmpFilename = $tmpDir . DIRECTORY_SEPARATOR . bin2hex(random_bytes(16));
return $this->files[$filename];
}
+ private function createTemporaryDir(): string
+ {
+ $tmpDir = $this->cacheDir . DIRECTORY_SEPARATOR . '.valinor.tmp';
+
+ if (! is_dir($tmpDir) && ! @mkdir($tmpDir, self::TEMPORARY_DIR_PERMISSION, true)) {
+ throw new CacheDirectoryNotWritable($this->cacheDir);
+ }
+
+ return $tmpDir;
+ }
+
private function path(string $key): string
{
/** @infection-ignore-all */
* @api
*
* @template EntryType
- * @implements CacheInterface<EntryType>
+ * @implements WarmupCache<EntryType>
*/
-final class FileSystemCache implements CacheInterface
+final class FileSystemCache implements WarmupCache
{
/** @var array<string, CacheInterface<EntryType>> */
private array $delegates;
];
}
+ public function warmup(): void
+ {
+ foreach ($this->delegates as $delegate) {
+ if ($delegate instanceof WarmupCache) {
+ $delegate->warmup();
+ }
+ }
+ }
+
public function has($key): bool
{
foreach ($this->delegates as $delegate) {
*
* @phpstan-type TimestampsArray = array<string, int>
* @template EntryType
- * @implements CacheInterface<EntryType|TimestampsArray>
+ * @implements WarmupCache<EntryType|TimestampsArray>
*/
-final class FileWatchingCache implements CacheInterface
+final class FileWatchingCache implements WarmupCache
{
/** @var array<string, TimestampsArray> */
private array $timestamps = [];
private CacheInterface $delegate
) {}
+ public function warmup(): void
+ {
+ if ($this->delegate instanceof WarmupCache) {
+ $this->delegate->warmup();
+ }
+ }
+
public function has($key): bool
{
foreach ($this->timestamps($key) as $fileName => $timestamp) {
* @internal
*
* @template EntryType
- * @implements CacheInterface<EntryType>
+ * @implements WarmupCache<EntryType>
*/
-final class KeySanitizerCache implements CacheInterface
+final class KeySanitizerCache implements WarmupCache
{
private static string $version;
$this->sanitize = static fn (string $key) => sha1("$key." . self::$version ??= PHP_VERSION . '/' . Package::version());
}
+ public function warmup(): void
+ {
+ if ($this->delegate instanceof WarmupCache) {
+ $this->delegate->warmup();
+ }
+ }
+
public function get($key, $default = null): mixed
{
return $this->delegate->get(($this->sanitize)($key), $default);
namespace CuyZ\Valinor\Cache\Warmup;
use CuyZ\Valinor\Cache\Exception\InvalidSignatureToWarmup;
+use CuyZ\Valinor\Cache\WarmupCache;
use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
use CuyZ\Valinor\Mapper\Object\Factory\ObjectBuilderFactory;
use CuyZ\Valinor\Mapper\Tree\Builder\ObjectImplementations;
+use CuyZ\Valinor\Type\ClassType;
use CuyZ\Valinor\Type\CompositeType;
use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
use CuyZ\Valinor\Type\Parser\TypeParser;
use CuyZ\Valinor\Type\Type;
-use CuyZ\Valinor\Type\ClassType;
use CuyZ\Valinor\Type\Types\InterfaceType;
+use Psr\SimpleCache\CacheInterface;
use function in_array;
/** @var list<class-string> */
private array $classesWarmedUp = [];
+ private bool $warmupWasDone = false;
+
public function __construct(
private TypeParser $parser,
+ /** @var CacheInterface<mixed> */
+ private CacheInterface $cache,
private ObjectImplementations $implementations,
private ClassDefinitionRepository $classDefinitionRepository,
private ObjectBuilderFactory $objectBuilderFactory
public function warmup(string ...$signatures): void
{
+ if (! $this->warmupWasDone) {
+ $this->warmupWasDone = true;
+
+ if ($this->cache instanceof WarmupCache) {
+ $this->cache->warmup();
+ }
+ }
+
foreach ($signatures as $signature) {
try {
$this->warmupType($this->parser->parse($signature));
--- /dev/null
+<?php
+
+namespace CuyZ\Valinor\Cache;
+
+use Psr\SimpleCache\CacheInterface;
+
+/**
+ * @internal
+ *
+ * @template T
+ * @extends CacheInterface<T>
+ */
+interface WarmupCache extends CacheInterface
+{
+ public function warmup(): void;
+}
use Traversable;
use function array_filter;
+use function array_map;
use function array_values;
use function count;
+use function is_a;
-/** @internal */
+/**
+ * @phpstan-type AttributeParam = array{class: class-string, callback: callable(): object}
+ *
+ * @internal
+ */
final class AttributesContainer implements Attributes
{
private static self $empty;
- /** @var array<object> */
+ /** @var list<AttributeParam> */
private array $attributes;
- public function __construct(object ...$attributes)
+ /**
+ * @no-named-arguments
+ * @param AttributeParam ...$attributes
+ */
+ public function __construct(array ...$attributes)
{
$this->attributes = $attributes;
}
public function has(string $className): bool
{
foreach ($this->attributes as $attribute) {
- if ($attribute instanceof $className) {
+ if (is_a($attribute['class'], $className, true)) {
return true;
}
}
public function ofType(string $className): array
{
- return array_values(array_filter(
+ $attributes = array_filter(
$this->attributes,
- static fn (object $attribute): bool => $attribute instanceof $className
+ static fn (array $attribute): bool => is_a($attribute['class'], $className, true)
+ );
+
+ /** @phpstan-ignore-next-line */
+ return array_values(array_map(
+ fn (array $attribute) => $attribute['callback'](),
+ $attributes
));
}
*/
public function getIterator(): Traversable
{
- return yield from $this->attributes;
+ foreach ($this->attributes as $attribute) {
+ yield $attribute['callback']();
+ }
}
}
array_map(
static function (ReflectionAttribute $attribute) {
try {
- return $attribute->newInstance();
+ $instance = $attribute->newInstance();
+
+ return [
+ 'class' => $attribute->getName(),
+ 'callback' => fn () => $instance
+ ];
} catch (Error) {
// Race condition when the attribute is affected to a property/parameter
// that was PROMOTED, in this case the attribute will be applied to both
use function count;
use function implode;
+use function is_array;
+use function is_object;
use function var_export;
/** @internal */
private function compileNativeAttributes(NativeAttributes $attributes): string
{
- $attributes = $attributes->definition();
+ $attributesListCode = [];
- if (count($attributes) === 0) {
- return '[]';
+ foreach ($attributes->definition() as $className => $arguments) {
+ $argumentsCode = $this->compileAttributeArguments($arguments);
+
+ $attributesListCode[] = "['class' => '$className', 'callback' => fn () => new $className($argumentsCode)]";
}
- $attributesListCode = [];
+ return implode(', ', $attributesListCode);
+ }
- foreach ($attributes as $className => $arguments) {
- if (count($arguments) === 0) {
- $argumentsCode = '';
+ /**
+ * @param array<mixed> $arguments
+ */
+ private function compileAttributeArguments(array $arguments): string
+ {
+ if (count($arguments) === 0) {
+ return '';
+ }
+
+ $argumentsCode = [];
+
+ foreach ($arguments as $argument) {
+ if (is_object($argument)) {
+ $argumentsCode[] = 'unserialize(' . var_export(serialize($argument), true) . ')';
+ } elseif (is_array($argument)) {
+ $argumentsCode[] = '[' . $this->compileAttributeArguments($argument) . ']';
} else {
- $argumentsCode = '...unserialize(' . var_export(serialize($arguments), true) . ')';
+ $argumentsCode[] = var_export($argument, true);
}
-
- $attributesListCode[] = "new $className($argumentsCode)";
}
- return '...[' . implode(",\n", $attributesListCode) . ']';
+ return implode(', ', $argumentsCode);
}
}
use CuyZ\Valinor\Definition\PropertyDefinition;
use CuyZ\Valinor\Definition\Repository\AttributesRepository;
use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
+use CuyZ\Valinor\Type\ClassType;
use CuyZ\Valinor\Type\GenericType;
use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
use CuyZ\Valinor\Type\Parser\Factory\Specifications\AliasSpecification;
use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
use CuyZ\Valinor\Type\Parser\TypeParser;
use CuyZ\Valinor\Type\Type;
-use CuyZ\Valinor\Type\ClassType;
use CuyZ\Valinor\Type\Types\UnresolvableType;
use CuyZ\Valinor\Utility\Reflection\Reflection;
+use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
+use CuyZ\Valinor\Utility\Reflection\DocParser;
use function array_filter;
use function array_keys;
{
return array_map(
function (ReflectionProperty $property) use ($type) {
- $typeResolver = $this->typeResolver($type, $property->class);
+ $typeResolver = $this->typeResolver($type, $property->getDeclaringClass());
return $this->propertyBuilder->for($property, $typeResolver);
},
}
return array_map(function (ReflectionMethod $method) use ($type) {
- $typeResolver = $this->typeResolver($type, $method->class);
+ $typeResolver = $this->typeResolver($type, $method->getDeclaringClass());
return $this->methodBuilder->for($method, $typeResolver);
}, $methods);
}
- private function typeResolver(ClassType $type, string $targetClass): ReflectionTypeResolver
+ /**
+ * @param ReflectionClass<object> $target
+ */
+ private function typeResolver(ClassType $type, ReflectionClass $target): ReflectionTypeResolver
{
- $typeKey = "{$type->toString()}/$targetClass";
+ $typeKey = $target->isInterface()
+ ? "{$type->toString()}/{$type->className()}"
+ : "{$type->toString()}/$target->name";
if (isset($this->typeResolver[$typeKey])) {
return $this->typeResolver[$typeKey];
}
- while ($type->className() !== $targetClass) {
+ while ($type->className() !== $target->name) {
$type = $type->parent();
}
private function localTypeAliases(ClassType $type): array
{
$reflection = Reflection::class($type->className());
- $rawTypes = Reflection::localTypeAliases($reflection);
+ $rawTypes = DocParser::localTypeAliases($reflection);
$typeParser = $this->typeParser($type);
private function importedTypeAliases(ClassType $type): array
{
$reflection = Reflection::class($type->className());
- $importedTypesRaw = Reflection::importedTypeAliases($reflection);
+ $importedTypesRaw = DocParser::importedTypeAliases($reflection);
$typeParser = $this->typeParser($type);
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\MixedType;
use CuyZ\Valinor\Type\Types\UnresolvableType;
+use CuyZ\Valinor\Utility\Reflection\DocParser;
use CuyZ\Valinor\Utility\Reflection\Reflection;
use ReflectionFunctionAbstract;
use ReflectionParameter;
private TypeParser $advancedParser
) {}
- public function resolveType(\ReflectionProperty|\ReflectionParameter|\ReflectionFunctionAbstract $reflection): Type
+ public function resolveType(ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection): Type
{
$nativeType = $this->nativeType($reflection);
$typeFromDocBlock = $this->typeFromDocBlock($reflection);
return $typeFromDocBlock;
}
- private function typeFromDocBlock(\ReflectionProperty|\ReflectionParameter|\ReflectionFunctionAbstract $reflection): ?Type
+ private function typeFromDocBlock(ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection): ?Type
{
- $type = $reflection instanceof ReflectionFunctionAbstract
- ? Reflection::docBlockReturnType($reflection)
- : Reflection::docBlockType($reflection);
+ if ($reflection instanceof ReflectionFunctionAbstract) {
+ $type = DocParser::functionReturnType($reflection);
+ } elseif ($reflection instanceof ReflectionProperty) {
+ $type = DocParser::propertyType($reflection);
+ } else {
+ $type = null;
+
+ if ($reflection->isPromoted()) {
+ // @phpstan-ignore-next-line / parameter is promoted so class exists for sure
+ $type = DocParser::propertyType($reflection->getDeclaringClass()->getProperty($reflection->name));
+ }
+
+ if ($type === null) {
+ $type = DocParser::parameterType($reflection);
+ }
+ }
if ($type === null) {
return null;
return $this->parseType($type, $reflection, $this->advancedParser);
}
- private function nativeType(\ReflectionProperty|\ReflectionParameter|\ReflectionFunctionAbstract $reflection): ?Type
+ private function nativeType(ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection): ?Type
{
$reflectionType = $reflection instanceof ReflectionFunctionAbstract
? $reflection->getReturnType()
return $this->parseType($type, $reflection, $this->nativeParser);
}
- private function parseType(string $raw, \ReflectionProperty|\ReflectionParameter|\ReflectionFunctionAbstract $reflection, TypeParser $parser): Type
+ private function parseType(string $raw, ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection, TypeParser $parser): Type
{
try {
return $parser->parse($raw);
use CuyZ\Valinor\Type\ClassType;
use CuyZ\Valinor\Type\Parser\Factory\LexingTypeParserFactory;
use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
-use CuyZ\Valinor\Type\Parser\Template\BasicTemplateParser;
-use CuyZ\Valinor\Type\Parser\Template\TemplateParser;
use CuyZ\Valinor\Type\Parser\TypeParser;
use CuyZ\Valinor\Type\ScalarType;
use CuyZ\Valinor\Type\Types\ArrayType;
AttributesRepository::class => fn () => new NativeAttributesRepository(),
- TypeParserFactory::class => fn () => new LexingTypeParserFactory(
- $this->get(TemplateParser::class)
- ),
+ TypeParserFactory::class => fn () => new LexingTypeParserFactory(),
TypeParser::class => fn () => $this->get(TypeParserFactory::class)->get(),
- TemplateParser::class => fn () => new BasicTemplateParser(),
-
RecursiveCacheWarmupService::class => fn () => new RecursiveCacheWarmupService(
$this->get(TypeParser::class),
+ $this->get(CacheInterface::class),
$this->get(ObjectImplementations::class),
$this->get(ClassDefinitionRepository::class),
$this->get(ObjectBuilderFactory::class)
final class Settings
{
/** @var non-empty-array<non-empty-string> */
- public const DEFAULT_SUPPORTED_DATETIME_FORMATS = [DATE_ATOM, 'U'];
+ public const DEFAULT_SUPPORTED_DATETIME_FORMATS = [
+ 'Y-m-d\\TH:i:sP', // RFC 3339
+ 'Y-m-d\\TH:i:s.uP', // RFC 3339 with microseconds
+ 'U', // Unix Timestamp
+ ];
/** @var array<class-string|interface-string, callable> */
public array $inferredMapping = [];
* date formats should be allowed during mapping.
*
* By default, if this constructor is never registered, the dates will accept
- * any valid timestamp or ATOM-formatted value.
+ * any valid timestamp or RFC 3339-formatted value.
*
* Usage:
*
$children = [];
foreach ($values as $key => $value) {
+ $child = $shell->child((string)$key, $subType);
+
if (! $keyType->accepts($key)) {
- throw new InvalidTraversableKey($key, $keyType);
+ $children[$key] = TreeNode::error($child, new InvalidTraversableKey($key, $keyType));
+ } else {
+ $children[$key] = $rootBuilder->build($child->withValue($value));
}
-
- $child = $shell->child((string)$key, $subType)->withValue($value);
- $children[$key] = $rootBuilder->build($child);
}
return $children;
/**
* Describes which date formats will be supported during mapping.
*
- * By default, the dates will accept any valid timestamp or ATOM-formatted
+ * By default, the dates will accept any valid timestamp or RFC 3339-formatted
* value.
*
* ```php
/**
* Returns the date formats supported during mapping.
*
- * By default, any valid timestamp or ATOM-formatted value are accepted.
+ * By default, any valid timestamp or RFC 3339-formatted value are accepted.
* Custom formats can be set using method `supportDateFormats()`.
*
* @return non-empty-array<non-empty-string>
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Type\Parser\Exception\Constant;
-
-use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
-use RuntimeException;
-
-/** @internal */
-final class MissingClassConstantColon extends RuntimeException implements InvalidType
-{
- /**
- * @param class-string $className
- */
- public function __construct(string $className, string $case)
- {
- if ($case === ':') {
- $case = '?';
- }
-
- parent::__construct(
- "Missing second colon symbol for class constant `$className::$case`.",
- 1652189143
- );
- }
-}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Type\Parser\Exception\Enum;
-
-use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
-use RuntimeException;
-use UnitEnum;
-
-/** @internal */
-final class MissingEnumColon extends RuntimeException implements InvalidType
-{
- /**
- * @param class-string<UnitEnum> $enumName
- */
- public function __construct(string $enumName, string $case)
- {
- if ($case === ':') {
- $case = '?';
- }
-
- parent::__construct(
- "Missing second colon symbol for enum `$enumName::$case`.",
- 1653468435
- );
- }
-}
use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
use CuyZ\Valinor\Type\Type;
-use CuyZ\Valinor\Type\Types\ArrayType;
-use CuyZ\Valinor\Type\Types\NonEmptyArrayType;
use RuntimeException;
/** @internal */
final class InvalidArrayKey extends RuntimeException implements InvalidType
{
- /**
- * @param class-string<ArrayType|NonEmptyArrayType> $arrayType
- */
- public function __construct(string $arrayType, Type $keyType, Type $subType)
+ public function __construct(Type $keyType)
{
- $signature = "array<{$keyType->toString()}, {$subType->toString()}>";
-
- if ($arrayType === NonEmptyArrayType::class) {
- $signature = "non-empty-array<{$keyType->toString()}, {$subType->toString()}>";
- }
-
parent::__construct(
- "Invalid key type `{$keyType->toString()}` for `$signature`. It must be one of `array-key`, `int` or `string`.",
+ "Invalid array key type `{$keyType->toString()}`, it must be a valid string or integer.",
1604335007
);
}
use LogicException;
/** @internal */
-final class DuplicatedTemplateName extends LogicException implements InvalidTemplate
+final class DuplicatedTemplateName extends LogicException
{
- public function __construct(string $template)
+ /**
+ * @param class-string $className
+ */
+ public function __construct(string $className, string $template)
{
parent::__construct(
- "The template `$template` was defined at least twice.",
+ "The template `$template` in class `$className` was defined at least twice.",
1604612898
);
}
namespace CuyZ\Valinor\Type\Parser\Exception\Template;
+use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
use LogicException;
/** @internal */
-final class InvalidClassTemplate extends LogicException implements InvalidTemplate
+final class InvalidClassTemplate extends LogicException
{
/**
* @param class-string $className
*/
- public function __construct(string $className, InvalidTemplate $exception)
+ public function __construct(string $className, string $template, InvalidType $exception)
{
parent::__construct(
- "Template error for class `$className`: {$exception->getMessage()}",
+ "Invalid template `$template` for class `$className`: {$exception->getMessage()}",
1630092678,
$exception
);
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Type\Parser\Exception\Template;
-
-use Throwable;
-
-/** @internal */
-interface InvalidTemplate extends Throwable {}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Type\Parser\Exception\Template;
-
-use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
-use LogicException;
-
-/** @internal */
-final class InvalidTemplateType extends LogicException implements InvalidTemplate
-{
- public function __construct(string $type, string $template, InvalidType $exception)
- {
- parent::__construct(
- "Invalid type `$type` for the template `$template`: {$exception->getMessage()}",
- 1607445951,
- $exception
- );
- }
-}
use CuyZ\Valinor\Type\Parser\Lexer\AdvancedClassLexer;
use CuyZ\Valinor\Type\Parser\Lexer\NativeLexer;
use CuyZ\Valinor\Type\Parser\LexingParser;
-use CuyZ\Valinor\Type\Parser\Template\TemplateParser;
use CuyZ\Valinor\Type\Parser\TypeParser;
/** @internal */
{
private TypeParser $nativeParser;
- public function __construct(private TemplateParser $templateParser) {}
-
public function get(TypeParserSpecification ...$specifications): TypeParser
{
if (empty($specifications)) {
}
$lexer = new NativeLexer();
- $lexer = new AdvancedClassLexer($lexer, $this, $this->templateParser);
+ $lexer = new AdvancedClassLexer($lexer, $this);
foreach ($specifications as $specification) {
$lexer = $specification->transform($lexer);
private function nativeParser(): TypeParser
{
$lexer = new NativeLexer();
- $lexer = new AdvancedClassLexer($lexer, $this, $this->templateParser);
- $lexer = new LexingParser($lexer);
+ $lexer = new AdvancedClassLexer($lexer, $this);
+ $parser = new LexingParser($lexer);
- return new CachedParser($lexer);
+ return new CachedParser($parser);
}
}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Type\Parser;
-
-use CuyZ\Valinor\Type\Type;
-
-/** @internal */
-final class LazyParser implements TypeParser
-{
- /** @var callable(): TypeParser */
- private $callback;
-
- private TypeParser $delegate;
-
- /**
- * @param callable(): TypeParser $callback
- */
- public function __construct(callable $callback)
- {
- $this->callback = $callback;
- }
-
- public function parse(string $raw): Type
- {
- $this->delegate ??= ($this->callback)();
-
- return $this->delegate->parse($raw);
- }
-}
use CuyZ\Valinor\Type\Parser\Lexer\Token\ClassNameToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\AdvancedClassNameToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\Token;
-use CuyZ\Valinor\Type\Parser\Template\TemplateParser;
/** @internal */
final class AdvancedClassLexer implements TypeLexer
public function __construct(
private TypeLexer $delegate,
private TypeParserFactory $typeParserFactory,
- private TemplateParser $templateParser
) {}
public function tokenize(string $symbol): Token
$token = $this->delegate->tokenize($symbol);
if ($token instanceof ClassNameToken) {
- return new AdvancedClassNameToken($token, $this->typeParserFactory, $this->templateParser);
+ return new AdvancedClassNameToken($token, $this->typeParserFactory);
}
return $token;
private function resolve(string $symbol): string
{
- if (Reflection::classOrInterfaceExists($symbol)) {
+ // Matches the case where a class extends a class with the same name but
+ // in a different namespace.
+ if ($symbol === $this->reflection->getShortName() && Reflection::classOrInterfaceExists($symbol)) {
return $symbol;
}
use CuyZ\Valinor\Type\Parser\Lexer\Token\ClosingSquareBracketToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\ColonToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\CommaToken;
+use CuyZ\Valinor\Type\Parser\Lexer\Token\DoubleColonToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\EnumNameToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\FloatValueToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\IntegerToken;
']' => ClosingSquareBracketToken::get(),
'{' => OpeningCurlyBracketToken::get(),
'}' => ClosingCurlyBracketToken::get(),
+ '::' => DoubleColonToken::get(),
':' => ColonToken::get(),
'?' => NullableToken::get(),
',' => CommaToken::get(),
use CuyZ\Valinor\Type\Parser\Exception\Generic\SeveralExtendTagsFound;
use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
use CuyZ\Valinor\Type\Parser\Exception\Template\InvalidClassTemplate;
-use CuyZ\Valinor\Type\Parser\Exception\Template\InvalidTemplate;
use CuyZ\Valinor\Type\Parser\Factory\Specifications\AliasSpecification;
use CuyZ\Valinor\Type\Parser\Factory\Specifications\ClassContextSpecification;
use CuyZ\Valinor\Type\Parser\Factory\Specifications\TypeAliasAssignerSpecification;
+use CuyZ\Valinor\Type\Parser\Factory\Specifications\TypeParserSpecification;
use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
-use CuyZ\Valinor\Type\Parser\LazyParser;
use CuyZ\Valinor\Type\Parser\Lexer\TokenStream;
-use CuyZ\Valinor\Type\Parser\Template\TemplateParser;
use CuyZ\Valinor\Type\Parser\TypeParser;
use CuyZ\Valinor\Type\StringType;
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\ArrayKeyType;
use CuyZ\Valinor\Type\ClassType;
+use CuyZ\Valinor\Type\Types\MixedType;
use CuyZ\Valinor\Type\Types\NativeClassType;
+use CuyZ\Valinor\Utility\Reflection\DocParser;
use CuyZ\Valinor\Utility\Reflection\Reflection;
use ReflectionClass;
public function __construct(
private ClassNameToken $delegate,
private TypeParserFactory $typeParserFactory,
- private TemplateParser $templateParser
) {}
public function traverse(TokenStream $stream): Type
new AliasSpecification($reflection),
];
- try {
- $docComment = $reflection->getDocComment() ?: '';
- $parser = new LazyParser(
- fn () => $this->typeParserFactory->get(...$specifications)
- );
-
- $templates = $this->templateParser->templates($docComment, $parser);
- } catch (InvalidTemplate $exception) {
- throw new InvalidClassTemplate($className, $exception);
- }
+ $templates = $this->templatesTypes($reflection, ...$specifications);
$generics = $this->generics($stream, $className, $templates);
$generics = $this->assignGenerics($className, $templates, $generics);
- $parserWithGenerics = new LazyParser(
- fn () => $this->typeParserFactory->get(new TypeAliasAssignerSpecification($generics), ...$specifications)
- );
-
if ($parentReflection) {
+ $parserWithGenerics = $this->typeParserFactory->get(new TypeAliasAssignerSpecification($generics), ...$specifications);
+
$parentType = $this->parentType($reflection, $parentReflection, $parserWithGenerics);
}
return $this->delegate->symbol();
}
+ /**
+ * @param ReflectionClass<object> $reflection
+ * @return array<string, Type>
+ */
+ private function templatesTypes(ReflectionClass $reflection, TypeParserSpecification ...$specifications): array
+ {
+ $templates = DocParser::classTemplates($reflection);
+
+ if ($templates === []) {
+ return [];
+ }
+
+ $types = [];
+
+ foreach ($templates as $templateName => $type) {
+ try {
+ if ($type === '') {
+ $types[$templateName] = MixedType::get();
+ } else {
+ /** @infection-ignore-all */
+ $parser ??= $this->typeParserFactory->get(...$specifications);
+
+ $types[$templateName] = $parser->parse($type);
+ }
+ } catch (InvalidType $invalidType) {
+ throw new InvalidClassTemplate($reflection->name, $templateName, $invalidType);
+ }
+ }
+
+ return $types;
+ }
+
/**
* @param array<string, Type> $templates
* @param class-string $className
*/
private function parentType(ReflectionClass $reflection, ReflectionClass $parentReflection, TypeParser $typeParser): NativeClassType
{
- $extendedClass = Reflection::extendedClassAnnotation($reflection);
+ $extendedClass = DocParser::classExtendsTypes($reflection);
if (count($extendedClass) > 1) {
throw new SeveralExtendTagsFound($reflection);
namespace CuyZ\Valinor\Type\Parser\Lexer\Token;
use CuyZ\Valinor\Type\CompositeTraversableType;
-use CuyZ\Valinor\Type\IntegerType;
use CuyZ\Valinor\Type\Parser\Exception\Iterable\ArrayClosingBracketMissing;
use CuyZ\Valinor\Type\Parser\Exception\Iterable\ArrayCommaMissing;
-use CuyZ\Valinor\Type\Parser\Exception\Iterable\InvalidArrayKey;
use CuyZ\Valinor\Type\Parser\Exception\Iterable\ShapedArrayClosingBracketMissing;
use CuyZ\Valinor\Type\Parser\Exception\Iterable\ShapedArrayColonTokenMissing;
use CuyZ\Valinor\Type\Parser\Exception\Iterable\ShapedArrayCommaMissing;
use CuyZ\Valinor\Type\Parser\Exception\Iterable\ShapedArrayElementTypeMissing;
use CuyZ\Valinor\Type\Parser\Exception\Iterable\ShapedArrayEmptyElements;
use CuyZ\Valinor\Type\Parser\Lexer\TokenStream;
-use CuyZ\Valinor\Type\StringType;
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\ArrayKeyType;
use CuyZ\Valinor\Type\Types\ArrayType;
throw new ArrayCommaMissing($this->arrayType, $type);
}
+ $keyType = ArrayKeyType::from($type);
$subType = $stream->read();
- if ($type instanceof ArrayKeyType) {
- $arrayType = new ($this->arrayType)($type, $subType);
- } elseif ($type instanceof IntegerType) {
- $arrayType = new ($this->arrayType)(ArrayKeyType::integer(), $subType);
- } elseif ($type instanceof StringType) {
- $arrayType = new ($this->arrayType)(ArrayKeyType::string(), $subType);
- } else {
- throw new InvalidArrayKey($this->arrayType, $type, $subType);
- }
+ $arrayType = new ($this->arrayType)($keyType, $subType);
if ($stream->done() || ! $stream->forward() instanceof ClosingBracketToken) {
throw new ArrayClosingBracketMissing($arrayType);
throw new ShapedArrayCommaMissing($elements);
}
+ if ($stream->done()) {
+ throw new ShapedArrayClosingBracketMissing($elements);
+ }
+
if ($stream->next() instanceof ClosingCurlyBracketToken) {
$stream->forward();
break;
}
if ($stream->done()) {
- $elements[] = new ShapedArrayElement(new StringValueType((string)$index), $type);
+ $elements[] = new ShapedArrayElement(new IntegerValueType($index), $type);
throw new ShapedArrayClosingBracketMissing($elements);
}
use CuyZ\Valinor\Type\Parser\Exception\Constant\ClassConstantCaseNotFound;
use CuyZ\Valinor\Type\Parser\Exception\Constant\MissingClassConstantCase;
-use CuyZ\Valinor\Type\Parser\Exception\Constant\MissingClassConstantColon;
use CuyZ\Valinor\Type\Parser\Exception\Constant\MissingSpecificClassConstantCase;
use CuyZ\Valinor\Type\Parser\Lexer\TokenStream;
use CuyZ\Valinor\Type\Type;
private function classConstant(TokenStream $stream): ?Type
{
- if ($stream->done() || ! $stream->next() instanceof ColonToken) {
+ if ($stream->done() || ! $stream->next() instanceof DoubleColonToken) {
return null;
}
- $case = $stream->forward();
- $missingColon = true;
+ $stream->forward();
- if (! $stream->done()) {
- $case = $stream->forward();
-
- $missingColon = ! $case instanceof ColonToken;
- }
-
- if (! $missingColon) {
- if ($stream->done()) {
- throw new MissingClassConstantCase($this->reflection->name);
- }
-
- $case = $stream->forward();
+ if ($stream->done()) {
+ throw new MissingClassConstantCase($this->reflection->name);
}
- $symbol = $case->symbol();
+ $symbol = $stream->forward()->symbol();
if ($symbol === '*') {
throw new MissingSpecificClassConstantCase($this->reflection->name);
}
- if ($missingColon) {
- throw new MissingClassConstantColon($this->reflection->name, $symbol);
- }
-
$cases = [];
if (! preg_match('/\*\s*\*/', $symbol)) {
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Type\Parser\Lexer\Token;
+
+use CuyZ\Valinor\Utility\IsSingleton;
+
+/** @internal */
+final class DoubleColonToken implements Token
+{
+ use IsSingleton;
+
+ public function symbol(): string
+ {
+ return '::';
+ }
+}
namespace CuyZ\Valinor\Type\Parser\Lexer\Token;
use CuyZ\Valinor\Type\Parser\Exception\Enum\MissingEnumCase;
-use CuyZ\Valinor\Type\Parser\Exception\Enum\MissingEnumColon;
use CuyZ\Valinor\Type\Parser\Exception\Enum\MissingSpecificEnumCase;
use CuyZ\Valinor\Type\Parser\Lexer\TokenStream;
use CuyZ\Valinor\Type\Type;
private function findPatternEnumType(TokenStream $stream): ?Type
{
- if ($stream->done() || ! $stream->next() instanceof ColonToken) {
+ if ($stream->done() || ! $stream->next() instanceof DoubleColonToken) {
return null;
}
- $case = $stream->forward();
- $missingColon = true;
+ $stream->forward();
- if (! $stream->done()) {
- $case = $stream->forward();
-
- $missingColon = ! $case instanceof ColonToken;
- }
-
- if (! $missingColon) {
- if ($stream->done()) {
- throw new MissingEnumCase($this->enumName);
- }
-
- $case = $stream->forward();
+ if ($stream->done()) {
+ throw new MissingEnumCase($this->enumName);
}
- $symbol = $case->symbol();
+ $symbol = $stream->forward()->symbol();
if ($symbol === '*') {
throw new MissingSpecificEnumCase($this->enumName);
}
- if ($missingColon) {
- throw new MissingEnumColon($this->enumName, $symbol);
- }
-
return EnumType::fromPattern($this->enumName, $symbol);
}
<?php
-declare(strict_types=1);
-
namespace CuyZ\Valinor\Type\Parser;
use CuyZ\Valinor\Type\Parser\Lexer\TokenStream;
use CuyZ\Valinor\Type\Parser\Lexer\TypeLexer;
use CuyZ\Valinor\Type\Type;
-use function array_filter;
-use function array_map;
-use function preg_split;
-use function str_contains;
-
/** @internal */
-final class LexingParser implements TypeParser
+class LexingParser implements TypeParser
{
public function __construct(private TypeLexer $lexer) {}
public function parse(string $raw): Type
{
- $symbols = $this->splitTokens($raw);
- $symbols = array_map('trim', $symbols);
- $symbols = array_filter($symbols, static fn ($value) => $value !== '');
+ $symbols = new ParserSymbols($raw);
$tokens = array_map(
fn (string $symbol) => $this->lexer->tokenize($symbol),
- $symbols
+ $symbols->all()
);
return (new TokenStream(...$tokens))->read();
}
-
- /**
- * @return string[]
- */
- private function splitTokens(string $raw): array
- {
- if (str_contains($raw, "@anonymous\0")) {
- return $this->splitTokensContainingAnonymousClass($raw);
- }
-
- /** @phpstan-ignore-next-line */
- return preg_split('/([\s?|&<>,\[\]{}:\'"])/', $raw, -1, PREG_SPLIT_DELIM_CAPTURE);
- }
-
- /**
- * @return string[]
- */
- private function splitTokensContainingAnonymousClass(string $raw): array
- {
- /** @var string[] $splits */
- $splits = preg_split('/([a-zA-Z_\x7f-\xff][\\\\\w\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:\d++\$)[\da-fA-F]++)/', $raw, -1, PREG_SPLIT_DELIM_CAPTURE);
- $symbols = [];
-
- foreach ($splits as $symbol) {
- if (str_contains($symbol, "@anonymous\0")) {
- $symbols[] = $symbol;
- } else {
- $symbols = [...$symbols, ...$this->splitTokens($symbol)];
- }
- }
-
- return $symbols;
- }
}
--- /dev/null
+<?php
+
+namespace CuyZ\Valinor\Type\Parser;
+
+/** @internal */
+final class ParserSymbols
+{
+ private const OPERATORS = [' ', '|', '&', '<', '>', '[', ']', '{', '}', ':', '?', ',', "'", '"'];
+
+ /** @var list<string> */
+ private array $symbols = [];
+
+ public function __construct(string $string)
+ {
+ $current = null;
+ $quote = null;
+
+ foreach (str_split($string) as $char) {
+ if ($char === $quote) {
+ $quote = null;
+ } elseif ($char === '"' || $char === "'") {
+ $quote = $char;
+ } elseif ($quote !== null || ! in_array($char, self::OPERATORS, true)) {
+ $current .= $char;
+ continue;
+ }
+
+ if ($current !== null) {
+ $this->symbols[] = $current;
+ $current = null;
+ }
+
+ $this->symbols[] = $char;
+ }
+
+ if ($current !== null) {
+ $this->symbols[] = $current;
+ }
+
+ $this->symbols = array_map('trim', $this->symbols);
+ $this->symbols = array_filter($this->symbols, static fn ($value) => $value !== '');
+
+ $this->mergeDoubleColons();
+ $this->detectAnonymousClass();
+ }
+
+ /**
+ * @return list<string>
+ */
+ public function all(): array
+ {
+ return $this->symbols;
+ }
+
+ private function mergeDoubleColons(): void
+ {
+ foreach ($this->symbols as $key => $symbol) {
+ /** @infection-ignore-all should not happen so it is not tested */
+ if ($key === 0) {
+ continue;
+ }
+
+ if ($symbol === ':' && $this->symbols[$key - 1] === ':') {
+ $this->symbols[$key - 1] = '::';
+ unset($this->symbols[$key]);
+ }
+ }
+ }
+
+ private function detectAnonymousClass(): void
+ {
+ foreach ($this->symbols as $key => $symbol) {
+ if (! str_starts_with($symbol, "class@anonymous\0")) {
+ continue;
+ }
+
+ $this->symbols[$key] = $symbol . $this->symbols[$key + 1] . $this->symbols[$key + 2];
+
+ array_splice($this->symbols, $key + 1, 2);
+ }
+ }
+}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Type\Parser\Template;
-
-use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
-use CuyZ\Valinor\Type\Parser\Exception\Template\DuplicatedTemplateName;
-use CuyZ\Valinor\Type\Parser\Exception\Template\InvalidTemplateType;
-use CuyZ\Valinor\Type\Parser\TypeParser;
-use CuyZ\Valinor\Type\Types\MixedType;
-
-use function preg_match_all;
-use function trim;
-
-/** @internal */
-final class BasicTemplateParser implements TemplateParser
-{
- public function templates(string $source, TypeParser $typeParser): array
- {
- $templates = [];
-
- preg_match_all("/@(phpstan-|psalm-)?template\s+(\w+)(\s+of\s+([\w\s?|&<>'\",-:\\\\\[\]{}]+))?/", $source, $raw);
-
- /** @var string[] $list */
- $list = $raw[2];
-
- if (empty($list)) {
- return [];
- }
-
- foreach ($list as $key => $name) {
- if (isset($templates[$name])) {
- throw new DuplicatedTemplateName($name);
- }
-
- $boundTypeSymbol = trim($raw[4][$key]);
-
- if (empty($boundTypeSymbol)) {
- $templates[$name] = MixedType::get();
- continue;
- }
-
- try {
- $templates[$name] = $typeParser->parse($boundTypeSymbol);
- } catch (InvalidType $invalidType) {
- throw new InvalidTemplateType($boundTypeSymbol, $name, $invalidType);
- }
- }
-
- return $templates;
- }
-}
+++ /dev/null
-<?php
-
-namespace CuyZ\Valinor\Type\Parser\Template;
-
-use CuyZ\Valinor\Type\Parser\Exception\Template\InvalidTemplate;
-use CuyZ\Valinor\Type\Parser\TypeParser;
-use CuyZ\Valinor\Type\Type;
-
-/** @internal */
-interface TemplateParser
-{
- /**
- * @return array<string, Type>
- *
- * @throws InvalidTemplate
- */
- public function templates(string $source, TypeParser $typeParser): array;
-}
namespace CuyZ\Valinor\Type\Types;
+use CuyZ\Valinor\Type\CombiningType;
use CuyZ\Valinor\Type\IntegerType;
+use CuyZ\Valinor\Type\Parser\Exception\Iterable\InvalidArrayKey;
use CuyZ\Valinor\Type\StringType;
use CuyZ\Valinor\Type\Type;
/** @internal */
final class ArrayKeyType implements Type
{
+ private static self $default;
+
private static self $integer;
private static self $string;
- private static self $integerAndString;
-
- /** @var array<IntegerType|StringType> */
+ /** @var array<Type> */
private array $types;
private string $signature;
- /**
- * @codeCoverageIgnore
- * @infection-ignore-all
- */
- private function __construct(IntegerType|StringType ...$types)
+ private function __construct(Type $type)
{
- $this->types = $types;
- $this->signature = count($this->types) === 1
- ? $this->types[0]->toString()
- : 'array-key';
+ $this->signature = $type->toString();
+ $this->types = $type instanceof CombiningType
+ ? [...$type->types()]
+ : [$type];
+
+ foreach ($this->types as $subType) {
+ if (! $subType instanceof IntegerType && ! $subType instanceof StringType) {
+ throw new InvalidArrayKey($subType);
+ }
+ }
}
public static function default(): self
{
- return self::$integerAndString ??= new self(NativeIntegerType::get(), NativeStringType::get());
+ return self::$default ??= new self(new UnionType(NativeIntegerType::get(), NativeStringType::get()));
}
public static function integer(): self
return self::$string ??= new self(NativeStringType::get());
}
- public function accepts(mixed $value): bool
+ public static function from(Type $type): ?self
{
- // If an array key can be evaluated as an integer, it will always be
- // cast to an integer, even if the actual key is a string.
- if (is_int($value)) {
- return true;
- }
+ return match (true) {
+ $type instanceof self => $type,
+ $type instanceof NativeIntegerType => self::integer(),
+ $type instanceof NativeStringType => self::string(),
+ default => new self($type),
+ };
+ }
+ public function accepts(mixed $value): bool
+ {
foreach ($this->types as $type) {
- if ($type->accepts($value)) {
+ // If an array key can be evaluated as an integer, it will always be
+ // cast to an integer, even if the actual key is a string.
+ if (is_int($value) && $type instanceof NativeStringType) {
+ return true;
+ } elseif ($type->accepts($value)) {
return true;
}
}
--- /dev/null
+<?php
+
+namespace CuyZ\Valinor\Utility\Reflection;
+
+use CuyZ\Valinor\Type\Parser\Exception\Template\DuplicatedTemplateName;
+use ReflectionClass;
+use ReflectionFunctionAbstract;
+use ReflectionParameter;
+use ReflectionProperty;
+
+use function end;
+use function preg_match;
+use function preg_match_all;
+use function str_replace;
+use function str_split;
+use function strrpos;
+use function substr;
+
+/** @internal */
+final class DocParser
+{
+ public static function propertyType(ReflectionProperty $reflection): ?string
+ {
+ $doc = self::sanitizeDocComment($reflection->getDocComment());
+
+ if ($doc === null) {
+ return null;
+ }
+
+ return self::annotationType($doc, 'var');
+ }
+
+ public static function parameterType(ReflectionParameter $reflection): ?string
+ {
+ $doc = self::sanitizeDocComment($reflection->getDeclaringFunction()->getDocComment());
+
+ if ($doc === null) {
+ return null;
+ }
+
+ if (! preg_match("/(?<type>.*)\\$$reflection->name(\s|\z)/s", $doc, $matches)) {
+ return null;
+ }
+
+ return self::annotationType($matches['type'], 'param');
+ }
+
+ public static function functionReturnType(ReflectionFunctionAbstract $reflection): ?string
+ {
+ $doc = self::sanitizeDocComment($reflection->getDocComment());
+
+ if ($doc === null) {
+ return null;
+ }
+
+ return self::annotationType($doc, 'return');
+ }
+
+ /**
+ * @param ReflectionClass<object> $reflection
+ * @return array<string, string>
+ */
+ public static function localTypeAliases(ReflectionClass $reflection): array
+ {
+ $doc = self::sanitizeDocComment($reflection->getDocComment());
+
+ if ($doc === null) {
+ return [];
+ }
+
+ $types = [];
+
+ preg_match_all('/@(phpstan|psalm)-type\s+(?<name>[a-zA-Z]\w*)\s*=?\s*(?<type>.*)/', $doc, $matches);
+
+ foreach ($matches['name'] as $key => $name) {
+ /** @var string $name */
+ $types[$name] = self::findType($matches['type'][$key]);
+ }
+
+ return $types;
+ }
+
+ /**
+ * @param ReflectionClass<object> $reflection
+ * @return array<class-string, string[]>
+ */
+ public static function importedTypeAliases(ReflectionClass $reflection): array
+ {
+ $doc = self::sanitizeDocComment($reflection->getDocComment());
+
+ if ($doc === null) {
+ return [];
+ }
+
+ $types = [];
+
+ preg_match_all('/@(phpstan|psalm)-import-type\s+(?<name>[a-zA-Z]\w*)\s*from\s*(?<class>\w+)/', $doc, $matches);
+
+ foreach ($matches['name'] as $key => $name) {
+ /** @var class-string $class */
+ $class = $matches['class'][$key];
+
+ $types[$class][] = $name;
+ }
+
+ return $types;
+ }
+
+ /**
+ * @param ReflectionClass<object> $reflection
+ * @return array<string>
+ */
+ public static function classExtendsTypes(ReflectionClass $reflection): array
+ {
+ $doc = self::sanitizeDocComment($reflection->getDocComment());
+
+ if ($doc === null) {
+ return [];
+ }
+
+ preg_match_all('/@(phpstan-|psalm-)?extends\s+(?<type>.+)/', $doc, $matches);
+
+ return $matches['type'];
+ }
+
+ /**
+ * @param ReflectionClass<object> $reflection
+ * @return array<string, string>
+ */
+ public static function classTemplates(ReflectionClass $reflection): array
+ {
+ $doc = self::sanitizeDocComment($reflection->getDocComment());
+
+ if ($doc === null) {
+ return [];
+ }
+
+ $templates = [];
+
+ preg_match_all("/@(phpstan-|psalm-)?template\s+(?<name>\w+)(\s+of\s+(?<type>.+))?/", $doc, $matches);
+
+ foreach ($matches['name'] as $key => $name) {
+ /** @var string $name */
+ if (isset($templates[$name])) {
+ throw new DuplicatedTemplateName($reflection->name, $name);
+ }
+
+ $templates[$name] = self::findType($matches['type'][$key]);
+ }
+
+ return $templates;
+ }
+
+ private static function annotationType(string $string, string $annotation): ?string
+ {
+ foreach (["@phpstan-$annotation", "@psalm-$annotation", "@$annotation"] as $case) {
+ $pos = strrpos($string, $case);
+
+ if ($pos !== false) {
+ return self::findType(substr($string, $pos + strlen($case)));
+ }
+ }
+
+ return null;
+ }
+
+ private static function findType(string $string): string
+ {
+ $operatorsMatrix = [
+ '{' => '}',
+ '<' => '>',
+ '"' => '"',
+ "'" => "'",
+ ];
+
+ $type = '';
+ $operators = [];
+ $expectExpression = true;
+
+ $string = str_replace("\n", ' ', $string);
+ $chars = str_split($string);
+
+ foreach ($chars as $key => $char) {
+ if ($operators === []) {
+ if ($char === '|' || $char === '&') {
+ $expectExpression = true;
+ } elseif (! $expectExpression && $chars[$key - 1] === ' ') {
+ break;
+ } elseif ($char !== ' ') {
+ $expectExpression = false;
+ }
+ }
+
+ if (isset($operatorsMatrix[$char])) {
+ $operators[] = $operatorsMatrix[$char];
+ } elseif ($operators !== [] && $char === end($operators)) {
+ array_pop($operators);
+ }
+
+ $type .= $char;
+ }
+
+ return trim($type);
+ }
+
+ private static function sanitizeDocComment(string|false $doc): ?string
+ {
+ /** @infection-ignore-all mutating `$doc` to `true` makes no sense */
+ if ($doc === false) {
+ return null;
+ }
+
+ $doc = preg_replace('#^\s*/\*\*([^/]+)\*/\s*$#', '$1', $doc);
+
+ return preg_replace('/^\s*\*\s*(\S*)/m', '$1', $doc); // @phpstan-ignore-line
+ }
+}
use Closure;
use ReflectionClass;
use ReflectionFunction;
-use ReflectionFunctionAbstract;
use ReflectionIntersectionType;
use ReflectionMethod;
use ReflectionNamedType;
use Reflector;
use RuntimeException;
-use function assert;
use function class_exists;
-use function count;
use function implode;
use function interface_exists;
-use function is_array;
-use function preg_match_all;
-use function preg_replace;
use function spl_object_hash;
use function str_contains;
-use function trim;
/** @internal */
final class Reflection
{
- private const TOOL_NONE = '';
- private const TOOL_EXPRESSION = '((?<tool>psalm|phpstan)-)';
- private const TYPE_EXPRESSION = '(?<type>[\w\s?|&<>\'",-:\\\\\[\]{}*]+)';
-
/** @var array<class-string, ReflectionClass<object>> */
private static array $classReflection = [];
return $name;
}
-
- public static function docBlockType(\ReflectionProperty|\ReflectionParameter $reflection): ?string
- {
- if ($reflection instanceof ReflectionProperty) {
- return self::parseDocBlock(
- self::sanitizeDocComment($reflection),
- sprintf('@%s?var\s+%s', self::TOOL_EXPRESSION, self::TYPE_EXPRESSION)
- );
- }
-
- if ($reflection->isPromoted()) {
- $type = self::parseDocBlock(
- // @phpstan-ignore-next-line / parameter is promoted so class exists for sure
- self::sanitizeDocComment($reflection->getDeclaringClass()->getProperty($reflection->name)),
- sprintf('@%s?var\s+%s', self::TOOL_EXPRESSION, self::TYPE_EXPRESSION)
- );
-
- if ($type !== null) {
- return $type;
- }
- }
-
- return self::parseDocBlock(
- self::sanitizeDocComment($reflection->getDeclaringFunction()),
- sprintf('@%s?param\s+%s\s+\$\b%s\b', self::TOOL_EXPRESSION, self::TYPE_EXPRESSION, $reflection->name)
- );
- }
-
- private static function parseDocBlock(string $docComment, string $expression): ?string
- {
- if (! preg_match_all("/$expression/", $docComment, $matches)) {
- return null;
- }
-
- foreach ($matches['tool'] as $index => $tool) {
- if ($tool === self::TOOL_NONE) {
- continue;
- }
-
- return trim($matches['type'][$index]);
- }
-
- return trim($matches['type'][0]);
- }
-
- public static function docBlockReturnType(ReflectionFunctionAbstract $reflection): ?string
- {
- $docComment = self::sanitizeDocComment($reflection);
-
- $expression = sprintf('/@%s?return\s+%s/', self::TOOL_EXPRESSION, self::TYPE_EXPRESSION);
-
- if (! preg_match_all($expression, $docComment, $matches)) {
- return null;
- }
-
- foreach ($matches['tool'] as $index => $tool) {
- if ($tool === self::TOOL_NONE) {
- continue;
- }
-
- return trim($matches['type'][$index]);
- }
-
- return trim($matches['type'][0]);
- }
-
- /**
- * @param ReflectionClass<object> $reflection
- * @return array<string, string>
- */
- public static function localTypeAliases(ReflectionClass $reflection): array
- {
- $types = [];
- $docComment = self::sanitizeDocComment($reflection);
-
- $expression = sprintf('/@(phpstan|psalm)-type\s+([a-zA-Z]\w*)\s*=?\s*%s/', self::TYPE_EXPRESSION);
-
- preg_match_all($expression, $docComment, $matches);
-
- foreach ($matches[2] as $key => $name) {
- $types[(string)$name] = $matches[3][$key];
- }
-
- return $types;
- }
-
- /**
- * @param ReflectionClass<object> $reflection
- * @return array<class-string, string[]>
- */
- public static function importedTypeAliases(ReflectionClass $reflection): array
- {
- $types = [];
- $docComment = self::sanitizeDocComment($reflection);
-
- $expression = sprintf('/@(phpstan|psalm)-import-type\s+([a-zA-Z]\w*)\s*from\s*%s/', self::TYPE_EXPRESSION);
- preg_match_all($expression, $docComment, $matches);
-
- foreach ($matches[2] as $key => $name) {
- /** @var class-string $classString */
- $classString = $matches[3][$key];
-
- $types[$classString][] = $name;
- }
-
- return $types;
- }
-
- /**
- * @param ReflectionClass<object> $reflection
- * @return array<string>
- */
- public static function extendedClassAnnotation(ReflectionClass $reflection): array
- {
- $docComment = self::sanitizeDocComment($reflection);
-
- $expression = sprintf('/@%s?extends\s+%s/', self::TOOL_EXPRESSION, self::TYPE_EXPRESSION);
- preg_match_all($expression, $docComment, $matches);
-
- assert(is_array($matches['type']));
-
- if (count($matches['type']) === 0) {
- return [];
- }
-
- return $matches['type'];
- }
-
- /**
- * @param ReflectionClass<object>|ReflectionProperty|ReflectionFunctionAbstract $reflection
- */
- private static function sanitizeDocComment(\ReflectionClass|\ReflectionProperty|ReflectionFunctionAbstract $reflection): string
- {
- $docComment = preg_replace('#^\s*/\*\*([^/]+)\*/\s*$#', '$1', $reflection->getDocComment() ?: '');
-
- return trim(preg_replace('/^\s*\*\s*(\S*)/m', '$1', $docComment)); // @phpstan-ignore-line
- }
}
namespace CuyZ\Valinor\Utility\String;
use CuyZ\Valinor\Mapper\Tree\Message\HasParameters;
+use IntlException;
use MessageFormatter;
use function class_exists;
*/
private static function formatWithIntl(string $locale, string $body, array $parameters): string
{
- return MessageFormatter::formatMessage($locale, $body, $parameters)
- ?: throw new StringFormatterError($body, intl_get_error_message());
+ try {
+ $formatted = MessageFormatter::formatMessage($locale, $body, $parameters);
+
+ if ($formatted === false) {
+ throw new StringFormatterError($body, intl_get_error_message());
+ }
+
+ return $formatted;
+ } catch (IntlException $e) {
+ throw new StringFormatterError($body, $e->getMessage(), $e);
+ }
}
/**
/** @internal */
final class StringFormatterError extends RuntimeException
{
- public function __construct(string $body, string $message = '')
+ public function __construct(string $body, string $message = '', ?\Throwable $previous = null)
{
if ($message !== '') {
$message = ": $message";
}
- parent::__construct("Message formatter error using `$body`$message.", 1652901203);
+ parent::__construct("Message formatter error using `$body`$message.", 1652901203, $previous);
}
}