Update composer dependencies
authorTim Düsterhus <duesterhus@woltlab.com>
Fri, 25 Aug 2023 10:35:34 +0000 (12:35 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Fri, 25 Aug 2023 10:35:52 +0000 (12:35 +0200)
50 files changed:
wcfsetup/install/files/lib/system/api/composer.json
wcfsetup/install/files/lib/system/api/composer.lock
wcfsetup/install/files/lib/system/api/composer/autoload_classmap.php
wcfsetup/install/files/lib/system/api/composer/autoload_static.php
wcfsetup/install/files/lib/system/api/composer/installed.json
wcfsetup/install/files/lib/system/api/composer/installed.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/composer.json
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/ChainCache.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/Compiled/CompiledPhpFileCache.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/FileSystemCache.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/FileWatchingCache.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/KeySanitizerCache.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/Warmup/RecursiveCacheWarmupService.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/WarmupCache.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/AttributesContainer.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/NativeAttributes.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/AttributesCompiler.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionClassDefinitionRepository.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionTypeResolver.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Library/Container.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Library/Settings.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/DateTimeFormatConstructor.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/ArrayNodeBuilder.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/MapperBuilder.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Constant/MissingClassConstantColon.php [deleted file]
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Enum/MissingEnumColon.php [deleted file]
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Iterable/InvalidArrayKey.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Template/DuplicatedTemplateName.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Template/InvalidClassTemplate.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Template/InvalidTemplate.php [deleted file]
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Template/InvalidTemplateType.php [deleted file]
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/LexingTypeParserFactory.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/LazyParser.php [deleted file]
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/AdvancedClassLexer.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/AliasLexer.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/NativeLexer.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/AdvancedClassNameToken.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/ArrayToken.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/ClassNameToken.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/DoubleColonToken.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/EnumNameToken.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/LexingParser.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/ParserSymbols.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Template/BasicTemplateParser.php [deleted file]
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Template/TemplateParser.php [deleted file]
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/ArrayKeyType.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Utility/Reflection/DocParser.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Utility/Reflection/Reflection.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Utility/String/StringFormatter.php
wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Utility/String/StringFormatterError.php

index da9210b11e9882b43029a7f67f56a6870e58cdfb..8717ec93f3f7ebd5e1babbe8559eb69536d5f446 100644 (file)
@@ -10,7 +10,7 @@
         }
     },
     "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",
index f17665620ce37fad2023faafea7d63bcb4fbac2d..007747f7d025b866c1f24ec5cde2c7889bf49256 100644 (file)
@@ -4,25 +4,25 @@
         "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": {
@@ -34,7 +34,7 @@
                 "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",
@@ -69,7 +69,7 @@
             ],
             "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": [
                 {
@@ -77,7 +77,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2023-08-07T18:29:08+00:00"
+            "time": "2023-08-25T10:26:38+00:00"
         },
         {
             "name": "dragonmantank/cron-expression",
index 20085d82af8d360531ade86794d1e0f3cb0aa989..a7bfbf9a4d07b3d7e4c96310cbb5ad24e227e0db 100644 (file)
@@ -32,6 +32,7 @@ return array(
     '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',
@@ -196,11 +197,9 @@ return array(
     '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',
@@ -243,8 +242,6 @@ return array(
     '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',
@@ -252,7 +249,6 @@ return array(
     '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',
@@ -268,6 +264,7 @@ return array(
     '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',
@@ -290,8 +287,7 @@ return array(
     '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',
@@ -336,6 +332,7 @@ return array(
     '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',
index 2dde04ac5a6558b392d01102c09ad0fb5097ba6b..c837b0ca6c28deba4739ef5e1ee90f1f2fd2971a 100644 (file)
@@ -220,6 +220,7 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d
         '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',
@@ -384,11 +385,9 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d
         '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',
@@ -431,8 +430,6 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d
         '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',
@@ -440,7 +437,6 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d
         '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',
@@ -456,6 +452,7 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d
         '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',
@@ -478,8 +475,7 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d
         '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',
@@ -524,6 +520,7 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d
         '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',
index 6089d8c0c6c0f223164af0140027d832eb20836d..0863aadc5e3d8a57526091d77414e403e12c5fe6 100644 (file)
@@ -2,22 +2,22 @@
     "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": {
@@ -66,7 +66,7 @@
             ],
             "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": [
                 {
index 8ff9b620a749002f5f8361c6727b9c2853f4e3b9..7b29adc6b1946f384d895c32593b1212d81ed582 100644 (file)
@@ -20,9 +20,9 @@
             '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(),
index 2e7a92015ca6f463b6b23dec8e51ba96f3187861..c699d5646576d187e55212c8312bd22459b44ae8 100644 (file)
@@ -15,7 +15,7 @@
         }
     ],
     "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"
     },
@@ -29,7 +29,7 @@
         "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": {
@@ -64,6 +64,7 @@
             "rector"
         ],
         "mutation": [
+            "@putenv XDEBUG_MODE=off",
             "infection --threads=max --git-diff-lines"
         ],
         "doc": [
index d97fdb4a36bdc669c2d2e3bf14789e346b2bd788..57e609845d9915f33f55e422e90aba6fd47b6918 100644 (file)
@@ -11,9 +11,9 @@ use Traversable;
  * @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;
@@ -29,6 +29,15 @@ final class ChainCache implements CacheInterface
         $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) {
index 88cbe1796382a8f5f12ae7417360cfa01c60f457..39ce5d57c0a184f33cff69d24e5063a68118ff87 100644 (file)
@@ -7,11 +7,11 @@ namespace CuyZ\Valinor\Cache\Compiled;
 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;
@@ -30,9 +30,9 @@ use function unlink;
  * @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;
 
@@ -46,6 +46,11 @@ final class CompiledPhpFileCache implements CacheInterface
         private CacheCompiler $compiler
     ) {}
 
+    public function warmup(): void
+    {
+        $this->createTemporaryDir();
+    }
+
     public function has($key): bool
     {
         $filename = $this->path($key);
@@ -74,11 +79,7 @@ final class CompiledPhpFileCache implements CacheInterface
 
         $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));
@@ -228,6 +229,17 @@ final class CompiledPhpFileCache implements CacheInterface
         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 */
index 0082308e367021c8517078e386b844311d0c702e..ded11aa1172ce5b63f95ae4161e1411135834746 100644 (file)
@@ -20,9 +20,9 @@ use function sys_get_temp_dir;
  * @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;
@@ -39,6 +39,15 @@ final class FileSystemCache implements CacheInterface
         ];
     }
 
+    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) {
index 770d38c68927104fb864c16197ad64db5bbfa6a6..417762bb088c459949e294c8a0ef50ee104cbe24 100644 (file)
@@ -29,9 +29,9 @@ use function is_string;
  *
  * @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 = [];
@@ -41,6 +41,13 @@ final class FileWatchingCache implements CacheInterface
         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) {
index bd83af13a79b610d790297d6d5e32b507b5dc1f0..6950e44627daf90e8ba007de393175be1a4929c6 100644 (file)
@@ -15,9 +15,9 @@ use function sha1;
  * @internal
  *
  * @template EntryType
- * @implements CacheInterface<EntryType>
+ * @implements WarmupCache<EntryType>
  */
-final class KeySanitizerCache implements CacheInterface
+final class KeySanitizerCache implements WarmupCache
 {
     private static string $version;
 
@@ -38,6 +38,13 @@ final class KeySanitizerCache implements CacheInterface
         $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);
index f22beca30505392e555d1baee2b7c57f1aa043cf..4648fc681db8584df7e28a0f98f70dfb7df05173 100644 (file)
@@ -5,15 +5,17 @@ declare(strict_types=1);
 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;
 
@@ -23,8 +25,12 @@ final class RecursiveCacheWarmupService
     /** @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
@@ -32,6 +38,14 @@ final class RecursiveCacheWarmupService
 
     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));
diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/WarmupCache.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/WarmupCache.php
new file mode 100644 (file)
index 0000000..97a4684
--- /dev/null
@@ -0,0 +1,16 @@
+<?php
+
+namespace CuyZ\Valinor\Cache;
+
+use Psr\SimpleCache\CacheInterface;
+
+/**
+ * @internal
+ *
+ * @template T
+ * @extends CacheInterface<T>
+ */
+interface WarmupCache extends CacheInterface
+{
+    public function warmup(): void;
+}
index ba6ae279f2c981de0bac19ce7b41127882836b8b..f60353c8540f3119f8eaf3790eefc12e5cbdba80 100644 (file)
@@ -7,18 +7,28 @@ namespace CuyZ\Valinor\Definition;
 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;
     }
@@ -31,7 +41,7 @@ final class AttributesContainer implements 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;
             }
         }
@@ -41,9 +51,15 @@ final class AttributesContainer implements Attributes
 
     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
         ));
     }
 
@@ -57,6 +73,8 @@ final class AttributesContainer implements Attributes
      */
     public function getIterator(): Traversable
     {
-        return yield from $this->attributes;
+        foreach ($this->attributes as $attribute) {
+            yield $attribute['callback']();
+        }
     }
 }
index f4a7b980226aba41bf93ec1262d7f288a3c6cab7..064702aeb93c10c275e6b9081cf92d98c632fb86 100644 (file)
@@ -32,7 +32,12 @@ final class NativeAttributes implements Attributes
             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
index a872f9dd4aa8c6f5d7ffa5838191c798d102e061..8870630787ca343b1059937b0f2e7f4944bef8c7 100644 (file)
@@ -10,6 +10,8 @@ use CuyZ\Valinor\Definition\NativeAttributes;
 
 use function count;
 use function implode;
+use function is_array;
+use function is_object;
 use function var_export;
 
 /** @internal */
@@ -32,24 +34,38 @@ final class AttributesCompiler
 
     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);
     }
 }
index 5140d86552b7c2ed7ecc14924f6cdd39721da67f..7824749bd1b7ebc56eeb78305f0aaed9a0500b09 100644 (file)
@@ -15,6 +15,7 @@ use CuyZ\Valinor\Definition\Properties;
 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;
@@ -23,11 +24,12 @@ use CuyZ\Valinor\Type\Parser\Factory\Specifications\TypeAliasAssignerSpecificati
 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;
@@ -76,7 +78,7 @@ final class ReflectionClassDefinitionRepository implements ClassDefinitionReposi
     {
         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);
             },
@@ -100,21 +102,26 @@ final class ReflectionClassDefinitionRepository implements ClassDefinitionReposi
         }
 
         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();
         }
 
@@ -156,7 +163,7 @@ final class ReflectionClassDefinitionRepository implements ClassDefinitionReposi
     private function localTypeAliases(ClassType $type): array
     {
         $reflection = Reflection::class($type->className());
-        $rawTypes = Reflection::localTypeAliases($reflection);
+        $rawTypes = DocParser::localTypeAliases($reflection);
 
         $typeParser = $this->typeParser($type);
 
@@ -181,7 +188,7 @@ final class ReflectionClassDefinitionRepository implements ClassDefinitionReposi
     private function importedTypeAliases(ClassType $type): array
     {
         $reflection = Reflection::class($type->className());
-        $importedTypesRaw = Reflection::importedTypeAliases($reflection);
+        $importedTypesRaw = DocParser::importedTypeAliases($reflection);
 
         $typeParser = $this->typeParser($type);
 
index ca6a4e1d1e4eabbde9bfd77465a9bdba6bf06508..b2e5391030cc51fc99770e00c8f89e79a9a8eb3c 100644 (file)
@@ -10,6 +10,7 @@ use CuyZ\Valinor\Type\Parser\TypeParser;
 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;
@@ -23,7 +24,7 @@ final class ReflectionTypeResolver
         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);
@@ -51,11 +52,24 @@ final class ReflectionTypeResolver
         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;
@@ -64,7 +78,7 @@ final class ReflectionTypeResolver
         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()
@@ -83,7 +97,7 @@ final class ReflectionTypeResolver
         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);
index 3fc9618f3ccfa6e1c07b05cd4c5a177732f80295..eb19d8bbd2a1f44afbea35db683e7e13fb6110d7 100644 (file)
@@ -50,8 +50,6 @@ use CuyZ\Valinor\Mapper\TypeTreeMapper;
 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;
@@ -196,16 +194,13 @@ final class Container
 
             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)
index 143eac2f3f55667806e3d145499bad9e25f51e03..54d2617d7dcf01a7a3e22713d35b64289effbd50 100644 (file)
@@ -14,7 +14,11 @@ use Throwable;
 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 = [];
index 2e2c53f82ce100c96555d391dddfcd1027c84b9b..98298f22adf22e2f3d1d8cfcca69a99300ec7af8 100644 (file)
@@ -14,7 +14,7 @@ use DateTimeInterface;
  * 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:
  *
index bcacaca5a6a07b2b360eb6f4e5828cbd7abf7e5c..2ab1340aa728d679333980ebd89e3e79ce40e978 100644 (file)
@@ -54,12 +54,13 @@ final class ArrayNodeBuilder implements NodeBuilder
         $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;
index 30c191fac5bc9264c3df963dfba1b0dfc6073dda..3cce949ef1dd038adbd768904b99436ea1196e3c 100644 (file)
@@ -219,7 +219,7 @@ final class MapperBuilder
     /**
      * 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
@@ -244,7 +244,7 @@ final class MapperBuilder
     /**
      * 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>
diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Constant/MissingClassConstantColon.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Constant/MissingClassConstantColon.php
deleted file mode 100644 (file)
index 6838823..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-<?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
-        );
-    }
-}
diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Enum/MissingEnumColon.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Enum/MissingEnumColon.php
deleted file mode 100644 (file)
index 5843f6b..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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
-        );
-    }
-}
index 243687c25645a68fe0668bf7040d249a802216c5..a94089aaecf1ff6ecbab4dee59c8d6796acf872d 100644 (file)
@@ -6,26 +6,15 @@ namespace CuyZ\Valinor\Type\Parser\Exception\Iterable;
 
 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
         );
     }
index 7f599ef7873ef5eb15c508ceddf407e86e4493cf..c15e06f7ec91054e966a2b9ed5f5bb8894a59de9 100644 (file)
@@ -7,12 +7,15 @@ namespace CuyZ\Valinor\Type\Parser\Exception\Template;
 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
         );
     }
index b70e07ff20544c513b237c4bd2b7d2f02be5d9cd..c09077328d52ae11345b1fc94b694133376e4a5c 100644 (file)
@@ -4,18 +4,19 @@ declare(strict_types=1);
 
 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
         );
diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Template/InvalidTemplate.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Template/InvalidTemplate.php
deleted file mode 100644 (file)
index 5e4594e..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Type\Parser\Exception\Template;
-
-use Throwable;
-
-/** @internal */
-interface InvalidTemplate extends Throwable {}
diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Template/InvalidTemplateType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Template/InvalidTemplateType.php
deleted file mode 100644 (file)
index eae59f1..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-<?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
-        );
-    }
-}
index 08d0eea3ae3878394536f34ebb153a16b5be29c8..facde9206e00290943b58443a7aebcea526a9e59 100644 (file)
@@ -9,7 +9,6 @@ use CuyZ\Valinor\Type\Parser\Factory\Specifications\TypeParserSpecification;
 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 */
@@ -17,8 +16,6 @@ final class LexingTypeParserFactory implements TypeParserFactory
 {
     private TypeParser $nativeParser;
 
-    public function __construct(private TemplateParser $templateParser) {}
-
     public function get(TypeParserSpecification ...$specifications): TypeParser
     {
         if (empty($specifications)) {
@@ -26,7 +23,7 @@ final class LexingTypeParserFactory implements TypeParserFactory
         }
 
         $lexer = new NativeLexer();
-        $lexer = new AdvancedClassLexer($lexer, $this, $this->templateParser);
+        $lexer = new AdvancedClassLexer($lexer, $this);
 
         foreach ($specifications as $specification) {
             $lexer = $specification->transform($lexer);
@@ -38,9 +35,9 @@ final class LexingTypeParserFactory implements TypeParserFactory
     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);
     }
 }
diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/LazyParser.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/LazyParser.php
deleted file mode 100644 (file)
index ade3126..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-<?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);
-    }
-}
index 0ac28d38eb0d7a621ee0659d85af9fa25e906452..6601ef896239f34693df4acb64a91661c8f64f1b 100644 (file)
@@ -8,7 +8,6 @@ use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
 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
@@ -16,7 +15,6 @@ final class AdvancedClassLexer implements TypeLexer
     public function __construct(
         private TypeLexer $delegate,
         private TypeParserFactory $typeParserFactory,
-        private TemplateParser $templateParser
     ) {}
 
     public function tokenize(string $symbol): Token
@@ -24,7 +22,7 @@ final class AdvancedClassLexer implements TypeLexer
         $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;
index 96c2ac469d440d5367816d24fd7a82a980b211ab..d5320a30ed7c635b8f4e6d27671e017b97239963 100644 (file)
@@ -31,7 +31,9 @@ final class AliasLexer implements TypeLexer
 
     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;
         }
 
index 9111ecda2409775e7a62754229958e9d3b61daf7..f319f35672a5bf6fb529a5c7b1dcf517b153750d 100644 (file)
@@ -12,6 +12,7 @@ use CuyZ\Valinor\Type\Parser\Lexer\Token\ClosingCurlyBracketToken;
 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;
@@ -53,6 +54,7 @@ final class NativeLexer implements TypeLexer
             ']' => ClosingSquareBracketToken::get(),
             '{' => OpeningCurlyBracketToken::get(),
             '}' => ClosingCurlyBracketToken::get(),
+            '::' => DoubleColonToken::get(),
             ':' => ColonToken::get(),
             '?' => NullableToken::get(),
             ',' => CommaToken::get(),
index e5bafa96a6e0129a71c878c922523ad878632abe..adb3dc2e2cd19017b85a7668b561c3d3b8a10412 100644 (file)
@@ -17,20 +17,20 @@ use CuyZ\Valinor\Type\Parser\Exception\Generic\ExtendTagTypeError;
 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;
 
@@ -45,7 +45,6 @@ final class AdvancedClassNameToken implements TraversingToken
     public function __construct(
         private ClassNameToken $delegate,
         private TypeParserFactory $typeParserFactory,
-        private TemplateParser $templateParser
     ) {}
 
     public function traverse(TokenStream $stream): Type
@@ -65,25 +64,14 @@ final class AdvancedClassNameToken implements TraversingToken
             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);
         }
 
@@ -95,6 +83,38 @@ final class AdvancedClassNameToken implements TraversingToken
         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
@@ -182,7 +202,7 @@ final class AdvancedClassNameToken implements TraversingToken
      */
     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);
index 6d37b433e376fc96395264de6c93e3bafb258bb4..8d1563f0693dcb2c452a38baec97c87f628f7c66 100644 (file)
@@ -5,17 +5,14 @@ declare(strict_types=1);
 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;
@@ -84,17 +81,10 @@ final class ArrayToken implements TraversingToken
             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);
@@ -120,6 +110,10 @@ final class ArrayToken implements TraversingToken
                 throw new ShapedArrayCommaMissing($elements);
             }
 
+            if ($stream->done()) {
+                throw new ShapedArrayClosingBracketMissing($elements);
+            }
+
             if ($stream->next() instanceof ClosingCurlyBracketToken) {
                 $stream->forward();
                 break;
@@ -134,7 +128,7 @@ final class ArrayToken implements TraversingToken
             }
 
             if ($stream->done()) {
-                $elements[] = new ShapedArrayElement(new StringValueType((string)$index), $type);
+                $elements[] = new ShapedArrayElement(new IntegerValueType($index), $type);
 
                 throw new ShapedArrayClosingBracketMissing($elements);
             }
index d95a5be34f1e62fcc311a46aa1b9a9094b029347..f3be92690a65f3f3a66c9d3cec2d63b968dd82e0 100644 (file)
@@ -6,7 +6,6 @@ namespace CuyZ\Valinor\Type\Parser\Lexer\Token;
 
 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;
@@ -58,37 +57,22 @@ final class ClassNameToken implements TraversingToken
 
     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)) {
diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/DoubleColonToken.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/DoubleColonToken.php
new file mode 100644 (file)
index 0000000..a38a2ea
--- /dev/null
@@ -0,0 +1,18 @@
+<?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 '::';
+    }
+}
index 513ba07bfbd63f6d0428b9aa4ff55da0ad6d52aa..69e11d8fe9d98aa3bc3e27bda8f08f941a65eb50 100644 (file)
@@ -5,7 +5,6 @@ declare(strict_types=1);
 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;
@@ -28,37 +27,22 @@ final class EnumNameToken implements TraversingToken
 
     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);
     }
 
index a1a8379c27668225a2d6d3e45b573a5f809c26d6..116de4ce0fe68dc29b8cd6d30297a286e99dc552 100644 (file)
@@ -1,67 +1,25 @@
 <?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;
-    }
 }
diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/ParserSymbols.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/ParserSymbols.php
new file mode 100644 (file)
index 0000000..15053ae
--- /dev/null
@@ -0,0 +1,82 @@
+<?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);
+        }
+    }
+}
diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Template/BasicTemplateParser.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Template/BasicTemplateParser.php
deleted file mode 100644 (file)
index be9f40f..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-<?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;
-    }
-}
diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Template/TemplateParser.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Template/TemplateParser.php
deleted file mode 100644 (file)
index 1155c90..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<?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;
-}
index 2655d3b44da429f093eeac531b37ed769d4c3143..dd0bd84777480f9500a44f23625cbc2a159f86e0 100644 (file)
@@ -4,7 +4,9 @@ declare(strict_types=1);
 
 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;
 
@@ -13,32 +15,34 @@ use function is_int;
 /** @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
@@ -51,16 +55,24 @@ final class ArrayKeyType implements Type
         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;
             }
         }
diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Utility/Reflection/DocParser.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Utility/Reflection/DocParser.php
new file mode 100644 (file)
index 0000000..4acc47d
--- /dev/null
@@ -0,0 +1,217 @@
+<?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
+    }
+}
index 4c1c82d4fc9b18d6651fd5b065c495ddea61f009..307f7f51eda644c24a7e78b07fd706136ec09c93 100644 (file)
@@ -7,7 +7,6 @@ namespace CuyZ\Valinor\Utility\Reflection;
 use Closure;
 use ReflectionClass;
 use ReflectionFunction;
-use ReflectionFunctionAbstract;
 use ReflectionIntersectionType;
 use ReflectionMethod;
 use ReflectionNamedType;
@@ -18,25 +17,15 @@ use ReflectionUnionType;
 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 = [];
 
@@ -133,141 +122,4 @@ final class Reflection
 
         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
-    }
 }
index 4fa5d286d033fce819c1f1758a36f268cf0798e4..7a70da5e077bc2d2a5c5e18f658fb03080d8659d 100644 (file)
@@ -5,6 +5,7 @@ declare(strict_types=1);
 namespace CuyZ\Valinor\Utility\String;
 
 use CuyZ\Valinor\Mapper\Tree\Message\HasParameters;
+use IntlException;
 use MessageFormatter;
 
 use function class_exists;
@@ -37,8 +38,17 @@ final class StringFormatter
      */
     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);
+        }
     }
 
     /**
index f99729377b75c8bdb78e97cc0168d6ce151d9099..191fe8516d10e079efdd846ace6e070b04f96ffd 100644 (file)
@@ -9,11 +9,11 @@ use RuntimeException;
 /** @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);
     }
 }