├── .github └── workflows │ └── check.yml ├── .gitignore ├── .php-cs-fixer.dist.php ├── .visitors.php-cs-fixer.dist.php ├── LICENSE ├── README.md ├── Taskfile.yml ├── composer-require-checker.json ├── composer.json ├── docs ├── reflection.md ├── reflection │ ├── caching.md │ ├── implementing_custom_types.md │ ├── native_adapters.md │ ├── php_doc_properties_and_methods.md │ └── types.md └── type.md ├── infection.json5.dist ├── phpstan.neon.dist ├── phpunit.xml.dist ├── psalm.xml.dist ├── src ├── ChangeDetector │ ├── CHANGELOG.md │ ├── ChangeDetector.php │ ├── ChangeDetectors.php │ ├── ComposerPackageChangeDetector.php │ ├── ConstantChangeDetector.php │ ├── DeterministicChangeDetector.php │ ├── ExtensionIsNotInstalled.php │ ├── FileChangeDetector.php │ ├── FileIsNotReadable.php │ ├── InMemoryChangeDetector.php │ ├── LICENSE │ ├── PackageIsNotInstalled.php │ ├── PhpExtensionVersionChangeDetector.php │ ├── PhpVersionChangeDetector.php │ ├── README.md │ └── composer.json ├── DeclarationId │ ├── AliasId.php │ ├── AnonymousClassId.php │ ├── AnonymousFunctionId.php │ ├── CHANGELOG.md │ ├── ClassConstantId.php │ ├── ConstantId.php │ ├── Id.php │ ├── Internal │ │ ├── IdIsNotDefined.php │ │ └── IdMap.php │ ├── LICENSE │ ├── MethodId.php │ ├── NamedClassId.php │ ├── NamedFunctionId.php │ ├── ParameterId.php │ ├── PropertyId.php │ ├── README.md │ ├── TemplateId.php │ └── composer.json ├── PhpStormReflectionStubs │ ├── CHANGELOG.md │ ├── Internal │ │ ├── ApplyLanguageLevelTypeAwareAttribute.php │ │ ├── ApplyTentativeTypeAttribute.php │ │ ├── RemovePhpStormMetaAttributes.php │ │ ├── RemoveThrowableToString.php │ │ └── RemoveTraversableExtendsIterable.php │ ├── LICENSE │ ├── PhpStormStubsLocator.php │ ├── README.md │ └── composer.json ├── Reflection │ ├── AliasReflection.php │ ├── Annotated │ │ ├── CustomTypeResolver.php │ │ ├── CustomTypeResolvers.php │ │ ├── NullCustomTypeResolver.php │ │ └── TypeContext.php │ ├── AttributeReflection.php │ ├── CHANGELOG.md │ ├── Cache │ │ └── FreshCache.php │ ├── ClassConstantReflection.php │ ├── ClassReflection.php │ ├── Collection.php │ ├── ConstantReflection.php │ ├── Deprecation.php │ ├── Exception │ │ ├── DeclarationNotFound.php │ │ ├── FileIsNotReadable.php │ │ └── ReflectionException.php │ ├── FunctionReflection.php │ ├── Internal │ │ ├── Annotated │ │ │ ├── AnnotatedDeclarations.php │ │ │ ├── AnnotatedDeclarationsDiscoverer.php │ │ │ └── NullAnnotatedDeclarationsDiscoverer.php │ │ ├── Cache │ │ │ ├── Cache.php │ │ │ ├── InMemoryPsr16Cache.php │ │ │ └── InvalidCacheKey.php │ │ ├── CompleteReflection │ │ │ ├── CleanUpInternallyDefined.php │ │ │ ├── CompleteEnum.php │ │ │ ├── CopyPromotedParameterToProperty.php │ │ │ ├── RemoveCode.php │ │ │ ├── RemoveContext.php │ │ │ ├── SetAttributeRepeated.php │ │ │ ├── SetClassCloneable.php │ │ │ ├── SetInterfaceMethodAbstract.php │ │ │ ├── SetParameterIndex.php │ │ │ ├── SetParameterOptional.php │ │ │ ├── SetReadonlyClassPropertyReadonly.php │ │ │ ├── SetStringableInterface.php │ │ │ └── SetTemplateIndex.php │ │ ├── ConstantExpression │ │ │ ├── ArrayElement.php │ │ │ ├── ArrayExpression.php │ │ │ ├── ArrayFetch.php │ │ │ ├── ArrayFetchCoalesce.php │ │ │ ├── BinaryOperation.php │ │ │ ├── ClassConstantFetch.php │ │ │ ├── CompilationContext.php │ │ │ ├── ConstantFetch.php │ │ │ ├── Expression.php │ │ │ ├── Instantiation.php │ │ │ ├── MagicClassInTrait.php │ │ │ ├── ParentClass.php │ │ │ ├── ParentClassInTrait.php │ │ │ ├── SelfClass.php │ │ │ ├── SelfClassInTrait.php │ │ │ ├── Ternary.php │ │ │ ├── UnaryOperation.php │ │ │ ├── Value.php │ │ │ └── Values.php │ │ ├── Context │ │ │ ├── Context.php │ │ │ ├── ContextProvider.php │ │ │ └── ContextVisitor.php │ │ ├── Data.php │ │ ├── Data │ │ │ ├── AliasTypeKey.php │ │ │ ├── ArgumentsExpressionKey.php │ │ │ ├── AttributeClassNameKey.php │ │ │ ├── AttributesKey.php │ │ │ ├── BackingTypeKey.php │ │ │ ├── BackingValueExpressionKey.php │ │ │ ├── BoolKeys.php │ │ │ ├── ChangeDetectorKey.php │ │ │ ├── ClassKind.php │ │ │ ├── ClassKindKey.php │ │ │ ├── CodeKey.php │ │ │ ├── ConstraintKey.php │ │ │ ├── ContextKey.php │ │ │ ├── DeclaringClassIdKey.php │ │ │ ├── DefaultValueExpressionKey.php │ │ │ ├── DeprecationKey.php │ │ │ ├── FileKey.php │ │ │ ├── InterfacesKey.php │ │ │ ├── LocationKey.php │ │ │ ├── NamedDataKeys.php │ │ │ ├── NamespaceKey.php │ │ │ ├── ParameterIndexKey.php │ │ │ ├── ParentsKey.php │ │ │ ├── PassedBy.php │ │ │ ├── PassedByKey.php │ │ │ ├── PhpDocKey.php │ │ │ ├── PhpExtensionKey.php │ │ │ ├── ThrowsTypeKey.php │ │ │ ├── TraitMethodAlias.php │ │ │ ├── TraitMethodAliasesKey.php │ │ │ ├── TraitMethodPrecedenceKey.php │ │ │ ├── TypeData.php │ │ │ ├── TypeDataKeys.php │ │ │ ├── UnresolvedInterfacesKey.php │ │ │ ├── UnresolvedParentKey.php │ │ │ ├── UnresolvedTraitsKey.php │ │ │ ├── UsePhpDocsKey.php │ │ │ ├── ValueExpressionKey.php │ │ │ ├── VarianceKey.php │ │ │ ├── Visibility.php │ │ │ └── VisibilityKey.php │ │ ├── Hook │ │ │ ├── ClassHook.php │ │ │ ├── ConstantHook.php │ │ │ ├── FunctionHook.php │ │ │ ├── HookPriorities.php │ │ │ └── Hooks.php │ │ ├── Inheritance │ │ │ ├── ClassInheritance.php │ │ │ ├── MethodInheritance.php │ │ │ ├── PropertyInheritance.php │ │ │ ├── ResolveClassInheritance.php │ │ │ ├── TypeInheritance.php │ │ │ └── TypeResolvers.php │ │ ├── Misc │ │ │ └── NonSerializable.php │ │ ├── NativeAdapter │ │ │ ├── AttributeAdapter.php │ │ │ ├── ClassAdapter.php │ │ │ ├── ClassConstantAdapter.php │ │ │ ├── EnumAdapter.php │ │ │ ├── EnumBackedCaseAdapter.php │ │ │ ├── EnumUnitCaseAdapter.php │ │ │ ├── FunctionAdapter.php │ │ │ ├── IntersectionTypeAdapter.php │ │ │ ├── MethodAdapter.php │ │ │ ├── NamedTypeAdapter.php │ │ │ ├── NativeTraitInfo.php │ │ │ ├── NativeTraitInfoKey.php │ │ │ ├── NonConvertableType.php │ │ │ ├── ParameterAdapter.php │ │ │ ├── PropertyAdapter.php │ │ │ ├── ToNativeTypeConverter.php │ │ │ └── UnionTypeAdapter.php │ │ ├── NativeReflector │ │ │ ├── DefinedConstantReflector.php │ │ │ └── NativeReflectionBasedReflector.php │ │ ├── PhpDoc │ │ │ ├── AlwaysTrimmingConstExprParser.php │ │ │ ├── InvalidPhpDocType.php │ │ │ ├── NamedObjectTypeDestructurizer.php │ │ │ ├── PhpDoc.php │ │ │ ├── PhpDocConstantExpressionCompiler.php │ │ │ ├── PhpDocParser.php │ │ │ ├── PhpDocReflector.php │ │ │ ├── PhpDocTagPrioritizer.php │ │ │ ├── PhpDocTypeReflector.php │ │ │ └── PrefixBasedPhpDocTagPrioritizer.php │ │ ├── PhpParser │ │ │ ├── CodeReflector.php │ │ │ ├── CollectIdReflectorsVisitor.php │ │ │ ├── ConstantExpressionCompiler.php │ │ │ ├── ConstantExpressionTypeReflector.php │ │ │ ├── FixNodeLocationVisitor.php │ │ │ ├── GeneratorVisitor.php │ │ │ ├── NameParser.php │ │ │ ├── NodeContextAttribute.php │ │ │ ├── NodeReflector.php │ │ │ ├── PhpParserChecker.php │ │ │ └── UnresolvedConstantType.php │ │ ├── Type │ │ │ └── IsNativeTypeNullable.php │ │ └── functions.php │ ├── KeyIsNotDefined.php │ ├── LICENSE │ ├── Location.php │ ├── Locator │ │ ├── AnonymousLocator.php │ │ ├── ComposerLocator.php │ │ ├── ConstantLocator.php │ │ ├── FileAnonymousLocator.php │ │ ├── Locators.php │ │ ├── NamedClassLocator.php │ │ ├── NamedFunctionLocator.php │ │ ├── NativeReflectionClassLocator.php │ │ ├── NativeReflectionFunctionLocator.php │ │ ├── NoSymfonyPolyfillLocator.php │ │ ├── OnlyLoadedClassLocator.php │ │ ├── Resource.php │ │ └── ScannedResourceLocator.php │ ├── MethodReflection.php │ ├── ModifierKind.php │ ├── ParameterReflection.php │ ├── PropertyReflection.php │ ├── README.md │ ├── ReflectionCollections.php │ ├── TemplateReflection.php │ ├── TypeKind.php │ ├── TyphoonReflector.php │ └── composer.json ├── Type │ ├── CHANGELOG.md │ ├── Internal │ │ ├── AliasType.php │ │ ├── ArgumentType.php │ │ ├── ArrayType.php │ │ ├── CallableType.php │ │ ├── ClassConstantMaskType.php │ │ ├── ClassConstantType.php │ │ ├── ClassStringType.php │ │ ├── ConditionalType.php │ │ ├── ConstantType.php │ │ ├── FloatType.php │ │ ├── FloatValueType.php │ │ ├── IntMaskType.php │ │ ├── IntType.php │ │ ├── IntValueType.php │ │ ├── IntersectionType.php │ │ ├── IterableType.php │ │ ├── KeyType.php │ │ ├── ListType.php │ │ ├── LiteralType.php │ │ ├── NamedObjectType.php │ │ ├── NonEmptyArrayType.php │ │ ├── NotType.php │ │ ├── ObjectType.php │ │ ├── OffsetType.php │ │ ├── ParentType.php │ │ ├── SelfType.php │ │ ├── StaticType.php │ │ ├── StringValueType.php │ │ ├── TemplateType.php │ │ ├── UnionType.php │ │ └── VarianceAwareType.php │ ├── LICENSE │ ├── Parameter.php │ ├── README.md │ ├── ShapeElement.php │ ├── Type.php │ ├── TypeVisitor.php │ ├── Variance.php │ ├── Visitor │ │ ├── DefaultTypeVisitor.php │ │ ├── RecursiveTypeReplacer.php │ │ ├── RelativeClassTypeResolver.php │ │ ├── TemplateTypeResolver.php │ │ └── TypeStringifier.php │ ├── composer.json │ ├── functions.php │ └── types.php └── TypedMap │ ├── CHANGELOG.md │ ├── Key.php │ ├── KeyIsNotDefined.php │ ├── LICENSE │ ├── OptionalKey.php │ ├── README.md │ ├── TypedMap.php │ └── composer.json ├── stubs ├── PHPStan │ └── PhpDocParser │ │ └── Ast │ │ ├── ConstExpr │ │ ├── ConstExprArrayNode.phpstub │ │ └── ConstFetchNode.phpstub │ │ ├── PhpDoc │ │ ├── MethodTagValueNode.phpstub │ │ ├── PhpDocTagNode.phpstub │ │ ├── TemplateTagValueNode.phpstub │ │ ├── TypeAliasImportTagValueNode.phpstub │ │ └── TypeAliasTagValueNode.phpstub │ │ └── Type │ │ ├── CallableTypeNode.phpstub │ │ ├── GenericTypeNode.phpstub │ │ ├── IdentifierTypeNode.phpstub │ │ ├── IntersectionTypeNode.phpstub │ │ └── UnionTypeNode.phpstub ├── PhpParser │ ├── Comment.phpstub │ └── Node │ │ ├── Expr │ │ └── Variable.phpstub │ │ ├── Identifier.phpstub │ │ ├── IntersectionType.phpstub │ │ ├── Name.phpstub │ │ ├── Stmt │ │ └── TraitUseAdaptation │ │ │ └── Alias.phpstub │ │ └── UnionType.phpstub └── Reflection.phpstub ├── tests ├── ChangeDetector │ ├── ChangeDetectorsTest.php │ ├── ComposerPackageChangeDetectorTest.php │ ├── ConstantChangeDetectorTest.php │ ├── DeterministicChangeDetectorTest.php │ ├── ExtensionIsNotInstalledTest.php │ ├── FileChangeDetectorTest.php │ ├── FileIsNotReadableTest.php │ ├── InMemoryChangeDetectorTest.php │ ├── PackageIsNotInstalledTest.php │ ├── PhpExtensionVersionChangeDetectorTest.php │ └── PhpVersionChangeDetectorTest.php ├── DeclarationId │ ├── DescribeTest.php │ ├── EncodeDecodeTest.php │ ├── Fixtures │ │ ├── anonymous_array_object_5_5.php │ │ └── anonymous_class_3_12.php │ └── IdClassFactoryTest.php ├── Reflection │ ├── Cache │ │ └── NullCache.php │ ├── FunctionalTest.php │ ├── Internal │ │ ├── GetNamespaceTest.php │ │ ├── GetShortNameTest.php │ │ ├── Inheritance │ │ │ └── TypeResolversTest.php │ │ ├── NativeAdapter │ │ │ ├── AdapterCompatibilityTest.php │ │ │ ├── AdapterCompletenessTest.php │ │ │ ├── ClassFixtures.php │ │ │ ├── Fixtures │ │ │ │ ├── classes.php │ │ │ │ ├── classes_php82.php │ │ │ │ ├── classes_php83.php │ │ │ │ ├── functions.php │ │ │ │ ├── functions_php82.php │ │ │ │ └── functions_php83.php │ │ │ └── FunctionFixtures.php │ │ ├── NativeReflector │ │ │ └── DefinedConstantReflectorTest.php │ │ ├── PhpDoc │ │ │ ├── NamedObjectTypeDestructurizerTest.php │ │ │ ├── PhpDocParserTest.php │ │ │ ├── PhpDocTypeReflectorTest.php │ │ │ └── PrefixBasedTagPrioritizerTest.php │ │ └── PhpParser │ │ │ ├── ConstantExpressionCompilerTest.php │ │ │ ├── ConstantExpressionTypeReflectorTest.php │ │ │ ├── FindAndCompileVisitor.php │ │ │ └── FindAndReflectVisitor.php │ ├── KeyIsNotDefinedTest.php │ ├── TyphoonReflectorMemoryTest.php │ ├── benchmark.php │ ├── functional_tests │ │ ├── class │ │ │ ├── alias │ │ │ │ └── lines.php │ │ │ ├── anonymous_class_id.php │ │ │ ├── constant │ │ │ │ ├── deprecation.php │ │ │ │ └── type.php │ │ │ ├── deprecation.php │ │ │ ├── enum_case │ │ │ │ ├── deprecation.php │ │ │ │ └── type.php │ │ │ ├── exception_for_anonymous_in_non_readable_file.php │ │ │ ├── exception_for_non_existing_class.php │ │ │ ├── infinite_recursion.php │ │ │ ├── iterator_types_with_single_generic.php │ │ │ ├── method │ │ │ │ └── parameter │ │ │ │ │ ├── deprecation.php │ │ │ │ │ └── nullable_types_in_namespace.php │ │ │ ├── multiple_anonymous_classes_on_line.php │ │ │ ├── phpdoc_method │ │ │ │ ├── native_reflection.php │ │ │ │ ├── parameter │ │ │ │ │ ├── defaults.php │ │ │ │ │ └── is_annotated.php │ │ │ │ ├── static.php │ │ │ │ └── templates.php │ │ │ ├── property │ │ │ │ ├── deprecation.php │ │ │ │ ├── location.php │ │ │ │ ├── phpdoc_properties.php │ │ │ │ ├── promoted_property_var_vs_param_phpdoc.php │ │ │ │ └── readonly.php │ │ │ ├── self_static_in_final_class.php │ │ │ ├── template │ │ │ │ ├── constraints.php │ │ │ │ ├── indexes.php │ │ │ │ ├── lines.php │ │ │ │ └── variance.php │ │ │ └── type_inheritance │ │ │ │ ├── class_implements_multiple_interfaces.php │ │ │ │ ├── interface_extends_multiple_interfaces.php │ │ │ │ ├── native_override.php │ │ │ │ ├── parameter_inheritance_is_resolved_by_position.php │ │ │ │ ├── parent.php │ │ │ │ ├── phpdoc_type_inheritance.php │ │ │ │ ├── self.php │ │ │ │ ├── static.php │ │ │ │ └── template_resolution.php │ │ ├── constant_const │ │ │ └── type.php │ │ ├── constant_define │ │ │ ├── evaluate_php_int_min.php │ │ │ └── type.php │ │ └── function │ │ │ ├── deprecation.php │ │ │ └── parameter │ │ │ └── deprecation.php │ └── functions.php ├── Type │ ├── ParameterTest.php │ ├── RoutingTester.php │ ├── ShapeElementTest.php │ ├── StringifyTest.php │ ├── TypeRoutingTest.php │ └── Visitor │ │ ├── DefaultTypeVisitorTest.php │ │ └── RecursiveTypeReplacerTest.php ├── TypedMap │ ├── KeyIsNotDefinedTest.php │ ├── Keys.php │ ├── OptionalKeys.php │ └── TypedMapTest.php └── isolated_package_testing │ ├── process_composer_json.php │ └── psalm.xml └── tools ├── composer-require-checker └── composer.json ├── phpunit └── composer.json └── psalm ├── composer.json └── src └── CheckVisibilityPlugin.php /.gitignore: -------------------------------------------------------------------------------- 1 | /.task/ 2 | /**/vendor/ 3 | /var/ 4 | /.php-cs-fixer.php 5 | /phpstan.neon 6 | /phpunit.xml 7 | /psalm.xml 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-present Valentin Udaltsov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /composer-require-checker.json: -------------------------------------------------------------------------------- 1 | { 2 | "symbol-whitelist" : [ 3 | "Composer\\Autoload\\ClassLoader", 4 | "Composer\\InstalledVersions", 5 | "PhpParser\\Node\\Scalar\\DNumber", 6 | "PhpParser\\Node\\Scalar\\LNumber", 7 | "PhpParser\\Node\\Stmt\\PropertyProperty", 8 | "Typhoon\\PhpStormReflectionStubs\\PhpStormStubsLocator" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /docs/reflection/caching.md: -------------------------------------------------------------------------------- 1 | # Caching 2 | 3 | By default, Typhoon Reflection uses in-memory LRU cache which should be enough for the majority of use cases. 4 | 5 | However, if you need persistent cache, you can use any [PSR-16](https://www.php-fig.org/psr/psr-16/) implementation. We 6 | highly recommend [Typhoon OPcache](https://github.com/typhoon-php/opcache). It stores values as opcacheable php files. 7 | 8 | ```php 9 | use Typhoon\Reflection\TyphoonReflector; 10 | use Typhoon\OPcache\TyphoonOPcache; 11 | 12 | $reflector = TyphoonReflector::build( 13 | cache: new TyphoonOPcache('path/to/cache/dir'), 14 | ); 15 | ``` 16 | 17 | To detect file changes during development, decorate your cache 18 | with [FreshCache](../../src/Reflection/Cache/FreshCache.php). 19 | 20 | ```php 21 | use Typhoon\Reflection\TyphoonReflector; 22 | use Typhoon\Reflection\Cache\FreshCache; 23 | use Typhoon\OPcache\TyphoonOPcache; 24 | 25 | $reflector = TyphoonReflector::build( 26 | cache: new FreshCache(new TyphoonOPcache('path/to/cache/dir')), 27 | ); 28 | ``` 29 | -------------------------------------------------------------------------------- /infection.json5.dist: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "vendor/infection/infection/resources/schema.json", 3 | "phpUnit": { 4 | "customPath": "tools/phpunit/vendor/bin/phpunit" 5 | }, 6 | "source": { 7 | "directories": [ 8 | "src/ChangeDetector", 9 | "src/Type", 10 | "src/TypedMap", 11 | ] 12 | }, 13 | "logs": { 14 | "text": "var/infection.log", 15 | "stryker": { 16 | "report": "/^\\d+\\.\\d+\\.x$/" 17 | }, 18 | }, 19 | "tmpDir": "var", 20 | "minCoveredMsi": 95, 21 | "mutators": { 22 | "@default": true, 23 | "CastInt": false, // Usually covered by Psalm 24 | }, 25 | "testFrameworkOptions": "--testsuite=infection" 26 | } 27 | -------------------------------------------------------------------------------- /phpstan.neon.dist: -------------------------------------------------------------------------------- 1 | includes: 2 | - phar://phpstan.phar/conf/bleedingEdge.neon 3 | 4 | parameters: 5 | level: 9 6 | paths: 7 | - src/Type 8 | tmpDir: var/phpstan 9 | ignoreErrors: 10 | - { identifier: missingType.generics } 11 | - { identifier: possiblyImpure.methodCall } 12 | -------------------------------------------------------------------------------- /src/ChangeDetector/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.4.4] 2024-08-18 9 | 10 | ### Fixed 11 | 12 | - Fix inverted logic in `PhpVersionChangeDetector::changed()`. 13 | - Fix inverted logic in `PhpExtensionVersionChangeDetector::changed()`. 14 | 15 | ## [0.4.3] 2024-08-06 16 | 17 | ### Added 18 | 19 | - Add `ConstantChangeDetector`. 20 | -------------------------------------------------------------------------------- /src/ChangeDetector/ChangeDetector.php: -------------------------------------------------------------------------------- 1 | 22 | */ 23 | public function deduplicate(): array; 24 | } 25 | -------------------------------------------------------------------------------- /src/ChangeDetector/ConstantChangeDetector.php: -------------------------------------------------------------------------------- 1 | exists) { 32 | return \defined($this->name); 33 | } 34 | 35 | if ($this->name === 'NAN') { 36 | return !(\is_float($this->value) && is_nan($this->value)); 37 | } 38 | 39 | try { 40 | return \constant($this->name) !== $this->value; 41 | } catch (\Error) { 42 | return true; 43 | } 44 | } 45 | 46 | public function deduplicate(): array 47 | { 48 | return [\sprintf('%s.%s.%d.%s', self::class, $this->name, (int) $this->exists, serialize($this->value)) => $this]; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/ChangeDetector/DeterministicChangeDetector.php: -------------------------------------------------------------------------------- 1 | changed; 23 | } 24 | 25 | public function deduplicate(): array 26 | { 27 | return [$this->hash => $this]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/ChangeDetector/ExtensionIsNotInstalled.php: -------------------------------------------------------------------------------- 1 | changed; 17 | } 18 | 19 | public function deduplicate(): array 20 | { 21 | return [\sprintf('%s.%d', self::class, (int) $this->changed) => $this]; 22 | } 23 | 24 | public function __serialize(): array 25 | { 26 | return []; 27 | } 28 | 29 | public function __unserialize(array $_data): void 30 | { 31 | $this->changed = true; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/ChangeDetector/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024-present Valentin Udaltsov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/ChangeDetector/PackageIsNotInstalled.php: -------------------------------------------------------------------------------- 1 | version !== \PHP_VERSION_ID; 19 | } 20 | 21 | public function deduplicate(): array 22 | { 23 | return [self::class . '.' . $this->version => $this]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/ChangeDetector/README.md: -------------------------------------------------------------------------------- 1 | # Typhoon Change Detector 2 | 3 | This repository is a readonly Typhoon monorepo subsplit. 4 | Please, open pull requests and issues in the [main repository](https://github.com/typhoon-php/typhoon). 5 | 6 | Read [documentation](https://github.com/typhoon-php/typhoon/tree/0.4.x). 7 | -------------------------------------------------------------------------------- /src/ChangeDetector/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typhoon/change-detector", 3 | "description": "Typhoon Change Detector", 4 | "license": "MIT", 5 | "type": "library", 6 | "authors": [ 7 | { 8 | "name": "Valentin Udaltsov", 9 | "email": "udaltsov.valentin@gmail.com" 10 | }, 11 | { 12 | "name": "Typhoon Team", 13 | "homepage": "https://github.com/orgs/typhoon-php/people" 14 | } 15 | ], 16 | "require": { 17 | "php": "^8.1" 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "Typhoon\\ChangeDetector\\": "" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/DeclarationId/AliasId.php: -------------------------------------------------------------------------------- 1 | name, $this->class->describe()); 23 | } 24 | 25 | public function equals(mixed $value): bool 26 | { 27 | return $value instanceof self 28 | && $value->class->equals($this->class) 29 | && $value->name === $this->name; 30 | } 31 | 32 | public function jsonSerialize(): array 33 | { 34 | return [self::CODE_ALIAS, $this->class, $this->name]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/DeclarationId/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | -------------------------------------------------------------------------------- /src/DeclarationId/ConstantId.php: -------------------------------------------------------------------------------- 1 | name; 22 | } 23 | 24 | public function equals(mixed $value): bool 25 | { 26 | return $value instanceof self 27 | && $value->name === $this->name; 28 | } 29 | 30 | public function jsonSerialize(): array 31 | { 32 | return [self::CODE_CONSTANT, $this->name]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/DeclarationId/Internal/IdIsNotDefined.php: -------------------------------------------------------------------------------- 1 | describe())); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/DeclarationId/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024-present Valentin Udaltsov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/DeclarationId/NamedClassId.php: -------------------------------------------------------------------------------- 1 | name; 23 | } 24 | 25 | public function equals(mixed $value): bool 26 | { 27 | return $value instanceof self 28 | && $value->name === $this->name; 29 | } 30 | 31 | public function reflect(): \ReflectionClass 32 | { 33 | return new \ReflectionClass($this->name); 34 | } 35 | 36 | public function jsonSerialize(): array 37 | { 38 | return [self::CODE_NAMED_CLASS, $this->name]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/DeclarationId/NamedFunctionId.php: -------------------------------------------------------------------------------- 1 | name); 23 | } 24 | 25 | public function describe(): string 26 | { 27 | return \sprintf('function %s()', $this->name); 28 | } 29 | 30 | public function equals(mixed $value): bool 31 | { 32 | return $value instanceof self 33 | && $value->name === $this->name; 34 | } 35 | 36 | public function reflect(): \ReflectionFunction 37 | { 38 | /** @psalm-suppress ArgumentTypeCoercion */ 39 | return new \ReflectionFunction($this->name); 40 | } 41 | 42 | public function jsonSerialize(): array 43 | { 44 | return [self::CODE_NAMED_FUNCTION, $this->name]; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/DeclarationId/README.md: -------------------------------------------------------------------------------- 1 | # Typhoon Declaration ID 2 | 3 | This repository is a readonly Typhoon monorepo subsplit. 4 | Please, open pull requests and issues in the [main repository](https://github.com/typhoon-php/typhoon). 5 | 6 | Read [documentation](https://github.com/typhoon-php/typhoon/tree/0.4.x). 7 | -------------------------------------------------------------------------------- /src/DeclarationId/TemplateId.php: -------------------------------------------------------------------------------- 1 | name, $this->declaration->describe()); 23 | } 24 | 25 | public function equals(mixed $value): bool 26 | { 27 | return $value instanceof self 28 | && $value->declaration->equals($this->declaration) 29 | && $value->name === $this->name; 30 | } 31 | 32 | public function jsonSerialize(): array 33 | { 34 | return [self::CODE_TEMPLATE, $this->declaration, $this->name]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/DeclarationId/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typhoon/declaration-id", 3 | "description": "Typhoon Declaration Id", 4 | "license": "MIT", 5 | "type": "library", 6 | "authors": [ 7 | { 8 | "name": "Valentin Udaltsov", 9 | "email": "udaltsov.valentin@gmail.com" 10 | }, 11 | { 12 | "name": "Typhoon Team", 13 | "homepage": "https://github.com/orgs/typhoon-php/people" 14 | } 15 | ], 16 | "require": { 17 | "php": "^8.1" 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "Typhoon\\DeclarationId\\": "" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/PhpStormReflectionStubs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.4.3] 2024-08-06 9 | 10 | ### Fixed 11 | 12 | - Do not locate defined constants via `PhpStormStubsLocator`. Reflecting defined constants from value is less 13 | error-prone. 14 | -------------------------------------------------------------------------------- /src/PhpStormReflectionStubs/Internal/RemoveThrowableToString.php: -------------------------------------------------------------------------------- 1 | name === \Throwable::class) { 31 | $methods = $data[Data::Methods]; 32 | unset($methods['__toString']); 33 | 34 | return $data->with(Data::Methods, $methods); 35 | } 36 | 37 | return $data; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/PhpStormReflectionStubs/Internal/RemoveTraversableExtendsIterable.php: -------------------------------------------------------------------------------- 1 | name === \Traversable::class) { 31 | return $data->without(Data::UnresolvedInterfaces); 32 | } 33 | 34 | return $data; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/PhpStormReflectionStubs/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024-present Valentin Udaltsov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/PhpStormReflectionStubs/README.md: -------------------------------------------------------------------------------- 1 | # Typhoon PhpStorm Reflection Stubs 2 | 3 | This repository is a readonly Typhoon monorepo subsplit. 4 | Please, open pull requests and issues in the [main repository](https://github.com/typhoon-php/typhoon). 5 | 6 | Read [documentation](https://github.com/typhoon-php/typhoon/tree/0.4.x). 7 | -------------------------------------------------------------------------------- /src/PhpStormReflectionStubs/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typhoon/phpstorm-reflection-stubs", 3 | "description": "Typhoon PhpStorm Reflection Stubs", 4 | "license": "MIT", 5 | "type": "library", 6 | "authors": [ 7 | { 8 | "name": "Valentin Udaltsov", 9 | "email": "udaltsov.valentin@gmail.com" 10 | }, 11 | { 12 | "name": "Typhoon Team", 13 | "homepage": "https://github.com/orgs/typhoon-php/people" 14 | } 15 | ], 16 | "require": { 17 | "php": "^8.1", 18 | "jetbrains/phpstorm-stubs": "^2024.1", 19 | "symfony/polyfill-php84": "^1.30", 20 | "typhoon/change-detector": "^0.4", 21 | "typhoon/declaration-id": "^0.4", 22 | "typhoon/reflection": "^0.4", 23 | "typhoon/type": "^0.4", 24 | "typhoon/typed-map": "^0.4" 25 | }, 26 | "autoload": { 27 | "psr-4": { 28 | "Typhoon\\PhpStormReflectionStubs\\": "" 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Reflection/AliasReflection.php: -------------------------------------------------------------------------------- 1 | id = $id; 38 | $this->data = $data; 39 | } 40 | 41 | public function location(): ?Location 42 | { 43 | return $this->data[Data::Location]; 44 | } 45 | 46 | public function type(): Type 47 | { 48 | return $this->data[Data::AliasType]; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Reflection/Annotated/CustomTypeResolver.php: -------------------------------------------------------------------------------- 1 | $typeArguments 17 | */ 18 | public function resolveCustomType(string $unresolvedName, array $typeArguments, TypeContext $context): ?Type; 19 | } 20 | -------------------------------------------------------------------------------- /src/Reflection/Annotated/CustomTypeResolvers.php: -------------------------------------------------------------------------------- 1 | $resolvers 16 | */ 17 | public function __construct( 18 | private readonly iterable $resolvers, 19 | ) {} 20 | 21 | public function resolveCustomType(string $unresolvedName, array $typeArguments, TypeContext $context): ?Type 22 | { 23 | foreach ($this->resolvers as $resolver) { 24 | $type = $resolver->resolveCustomType($unresolvedName, $typeArguments, $context); 25 | 26 | if ($type !== null) { 27 | return $type; 28 | } 29 | } 30 | 31 | return null; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Reflection/Annotated/NullCustomTypeResolver.php: -------------------------------------------------------------------------------- 1 | $arguments 29 | */ 30 | public function resolveNameAsType(string $unresolvedName, array $arguments = []): Type; 31 | } 32 | -------------------------------------------------------------------------------- /src/Reflection/Deprecation.php: -------------------------------------------------------------------------------- 1 | describe()))); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Reflection/Exception/FileIsNotReadable.php: -------------------------------------------------------------------------------- 1 | $templateNames 15 | * @param list $aliasNames 16 | */ 17 | public function __construct( 18 | public readonly array $templateNames = [], 19 | public readonly array $aliasNames = [], 20 | ) {} 21 | } 22 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Annotated/AnnotatedDeclarationsDiscoverer.php: -------------------------------------------------------------------------------- 1 | cache->get(self::key($id)); 31 | 32 | if ($value instanceof TypedMap) { 33 | return $value; 34 | } 35 | 36 | return null; 37 | } 38 | 39 | public function set(Id $id, TypedMap $data): void 40 | { 41 | $this->cache->set(self::key($id), $data); 42 | } 43 | 44 | private static function key(Id $id): string 45 | { 46 | return hash('xxh128', self::PREFIX . $id->encode() . self::VERSION); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Cache/InvalidCacheKey.php: -------------------------------------------------------------------------------- 1 | with(Data::Methods, array_map( 36 | static fn(TypedMap $method): TypedMap => $method->with(Data::Abstract, true), 37 | $data[Data::Methods], 38 | )); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Reflection/Internal/CompleteReflection/SetStringableInterface.php: -------------------------------------------------------------------------------- 1 | name === \Stringable::class || !isset($data[Data::Methods]['__toString'])) { 31 | return $data; 32 | } 33 | 34 | return $data->with(Data::UnresolvedInterfaces, [ 35 | ...$data[Data::UnresolvedInterfaces], 36 | \Stringable::class => [], 37 | ]); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Reflection/Internal/ConstantExpression/ArrayElement.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | final class ArrayFetch implements Expression 15 | { 16 | public function __construct( 17 | private readonly Expression $array, 18 | private readonly Expression $key, 19 | ) {} 20 | 21 | public function recompile(CompilationContext $context): Expression 22 | { 23 | return new self( 24 | array: $this->array->recompile($context), 25 | key: $this->key->recompile($context), 26 | ); 27 | } 28 | 29 | public function evaluate(?TyphoonReflector $reflector = null): mixed 30 | { 31 | /** @psalm-suppress MixedArrayAccess, MixedArrayOffset */ 32 | return $this->array->evaluate($reflector)[$this->key->evaluate($reflector)]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Reflection/Internal/ConstantExpression/ArrayFetchCoalesce.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | final class ArrayFetchCoalesce implements Expression 15 | { 16 | public function __construct( 17 | private readonly Expression $array, 18 | private readonly Expression $key, 19 | private readonly Expression $default, 20 | ) {} 21 | 22 | public function recompile(CompilationContext $context): Expression 23 | { 24 | return new self( 25 | array: $this->array->recompile($context), 26 | key: $this->key->recompile($context), 27 | default: $this->default->recompile($context), 28 | ); 29 | } 30 | 31 | public function evaluate(?TyphoonReflector $reflector = null): mixed 32 | { 33 | /** @psalm-suppress MixedArrayOffset */ 34 | return $this->array->evaluate($reflector)[$this->key->evaluate($reflector)] ?? $this->default->evaluate($reflector); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Reflection/Internal/ConstantExpression/Expression.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | public function recompile(CompilationContext $context): self; 20 | 21 | /** 22 | * @return T 23 | */ 24 | public function evaluate(?TyphoonReflector $reflector = null): mixed; 25 | } 26 | -------------------------------------------------------------------------------- /src/Reflection/Internal/ConstantExpression/Instantiation.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | final class Instantiation implements Expression 15 | { 16 | /** 17 | * @param array $arguments 18 | */ 19 | public function __construct( 20 | private readonly Expression $class, 21 | private readonly array $arguments, 22 | ) {} 23 | 24 | public function recompile(CompilationContext $context): Expression 25 | { 26 | return new self( 27 | class: $this->class->recompile($context), 28 | arguments: array_map( 29 | static fn(Expression $expression): Expression => $expression->recompile($context), 30 | $this->arguments, 31 | ), 32 | ); 33 | } 34 | 35 | public function evaluate(?TyphoonReflector $reflector = null): mixed 36 | { 37 | /** @psalm-suppress MixedMethodCall */ 38 | return new ($this->class->evaluate($reflector))(...array_map( 39 | static fn(Expression $expression): mixed => $expression->evaluate($reflector), 40 | $this->arguments, 41 | )); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Reflection/Internal/ConstantExpression/MagicClassInTrait.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | final class MagicClassInTrait implements Expression 15 | { 16 | public function __construct( 17 | private readonly string $trait, 18 | ) {} 19 | 20 | public function recompile(CompilationContext $context): Expression 21 | { 22 | $expression = $context->magicClass(); 23 | 24 | if ($expression instanceof self) { 25 | return $expression; 26 | } 27 | 28 | // even when trait is used in a class, __CONSTANT__ is not inlined 29 | return new self($expression->evaluate()); 30 | } 31 | 32 | public function evaluate(?TyphoonReflector $reflector = null): mixed 33 | { 34 | return $this->trait; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Reflection/Internal/ConstantExpression/ParentClass.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | final class ParentClass implements Expression 16 | { 17 | public function __construct( 18 | private readonly NamedClassId $resolvedClass, 19 | ) {} 20 | 21 | public function recompile(CompilationContext $context): Expression 22 | { 23 | return $context->parent(); 24 | } 25 | 26 | public function evaluate(?TyphoonReflector $reflector = null): mixed 27 | { 28 | return $this->resolvedClass->name; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Reflection/Internal/ConstantExpression/ParentClassInTrait.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | enum ParentClassInTrait implements Expression 15 | { 16 | case Instance; 17 | 18 | public function recompile(CompilationContext $context): Expression 19 | { 20 | return $context->parent(); 21 | } 22 | 23 | public function evaluate(?TyphoonReflector $reflector = null): mixed 24 | { 25 | throw new \LogicException('Parent in trait!'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Reflection/Internal/ConstantExpression/SelfClass.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | final class SelfClass implements Expression 17 | { 18 | public function __construct( 19 | private readonly NamedClassId|AnonymousClassId $class, 20 | ) {} 21 | 22 | public function recompile(CompilationContext $context): Expression 23 | { 24 | return $context->self(); 25 | } 26 | 27 | public function evaluate(?TyphoonReflector $reflector = null): mixed 28 | { 29 | return $this->class->name ?? throw new \LogicException('Anonymous!'); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Reflection/Internal/ConstantExpression/SelfClassInTrait.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | final class SelfClassInTrait implements Expression 16 | { 17 | public function __construct( 18 | private readonly NamedClassId $trait, 19 | ) {} 20 | 21 | public function recompile(CompilationContext $context): Expression 22 | { 23 | return $context->self(); 24 | } 25 | 26 | public function evaluate(?TyphoonReflector $reflector = null): mixed 27 | { 28 | return $this->trait->name; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Reflection/Internal/ConstantExpression/Ternary.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | final class Ternary implements Expression 15 | { 16 | public function __construct( 17 | private readonly Expression $condition, 18 | private readonly ?Expression $if, 19 | private readonly Expression $else, 20 | ) {} 21 | 22 | public function recompile(CompilationContext $context): Expression 23 | { 24 | return new self( 25 | condition: $this->condition->recompile($context), 26 | if: $this->if?->recompile($context), 27 | else: $this->else, 28 | ); 29 | } 30 | 31 | public function evaluate(?TyphoonReflector $reflector = null): mixed 32 | { 33 | if ($this->if === null) { 34 | return $this->condition->evaluate($reflector) ?: $this->else->evaluate($reflector); 35 | } 36 | 37 | return $this->condition->evaluate($reflector) ? $this->if->evaluate($reflector) : $this->else->evaluate($reflector); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Reflection/Internal/ConstantExpression/UnaryOperation.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | final class UnaryOperation implements Expression 15 | { 16 | /** 17 | * @param non-empty-string $operator 18 | */ 19 | public function __construct( 20 | private readonly Expression $expression, 21 | private readonly string $operator, 22 | ) {} 23 | 24 | public function recompile(CompilationContext $context): Expression 25 | { 26 | return new self( 27 | expression: $this->expression->recompile($context), 28 | operator: $this->operator, 29 | ); 30 | } 31 | 32 | public function evaluate(?TyphoonReflector $reflector = null): mixed 33 | { 34 | return match ($this->operator) { 35 | '+' => +$this->expression->evaluate($reflector), 36 | '-' => -$this->expression->evaluate($reflector), 37 | '!' => !$this->expression->evaluate($reflector), 38 | '~' => ~$this->expression->evaluate($reflector), 39 | }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Reflection/Internal/ConstantExpression/Values.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | enum Values implements Expression 15 | { 16 | case Null; 17 | case True; 18 | case False; 19 | case MinusOne; 20 | case Zero; 21 | case One; 22 | case EmptyString; 23 | case EmptyArray; 24 | 25 | public function recompile(CompilationContext $context): Expression 26 | { 27 | return $this; 28 | } 29 | 30 | public function evaluate(?TyphoonReflector $reflector = null): mixed 31 | { 32 | return match ($this) { 33 | self::Null => null, 34 | self::True => true, 35 | self::False => false, 36 | self::MinusOne => -1, 37 | self::Zero => 0, 38 | self::One => 1, 39 | self::EmptyString => '', 40 | self::EmptyArray => [], 41 | }; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Context/ContextProvider.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | enum AliasTypeKey implements Key 16 | { 17 | case Key; 18 | } 19 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/ArgumentsExpressionKey.php: -------------------------------------------------------------------------------- 1 | > 16 | */ 17 | enum ArgumentsExpressionKey implements OptionalKey 18 | { 19 | case Key; 20 | 21 | public function default(TypedMap $map): mixed 22 | { 23 | return Value::from([]); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/AttributeClassNameKey.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | enum AttributeClassNameKey implements Key 15 | { 16 | case Key; 17 | } 18 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/AttributesKey.php: -------------------------------------------------------------------------------- 1 | > 14 | */ 15 | enum AttributesKey implements OptionalKey 16 | { 17 | case Key; 18 | 19 | public function default(TypedMap $map): mixed 20 | { 21 | return []; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/BackingTypeKey.php: -------------------------------------------------------------------------------- 1 | > 15 | */ 16 | enum BackingTypeKey implements OptionalKey 17 | { 18 | case Key; 19 | 20 | public function default(TypedMap $map): mixed 21 | { 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/BackingValueExpressionKey.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | enum BackingValueExpressionKey implements OptionalKey 17 | { 18 | case Key; 19 | 20 | public function default(TypedMap $map): mixed 21 | { 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/BoolKeys.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | enum BoolKeys implements OptionalKey 16 | { 17 | case AttributeRepeated; 18 | case InternallyDefined; 19 | case IsAbstract; 20 | case AnnotatedFinal; 21 | case AnnotatedReadonly; 22 | case ReturnsReference; 23 | case EnumCase; 24 | case Generator; 25 | case NativeFinal; 26 | case NativeReadonly; 27 | case Promoted; 28 | case IsStatic; 29 | case Variadic; 30 | case Annotated; 31 | case Optional; 32 | case Cloneable; 33 | 34 | public function default(TypedMap $map): mixed 35 | { 36 | return false; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/ChangeDetectorKey.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | enum ChangeDetectorKey implements OptionalKey 18 | { 19 | case Key; 20 | 21 | public function default(TypedMap $map): mixed 22 | { 23 | return new InMemoryChangeDetector(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/ClassKind.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | enum ClassKindKey implements Key 15 | { 16 | case Key; 17 | } 18 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/CodeKey.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | enum CodeKey implements Key 15 | { 16 | case Key; 17 | } 18 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/ConstraintKey.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | enum ConstraintKey implements OptionalKey 18 | { 19 | case Key; 20 | 21 | public function default(TypedMap $map): mixed 22 | { 23 | return types::mixed; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/ContextKey.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | enum ContextKey implements Key 16 | { 17 | case Key; 18 | } 19 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/DeclaringClassIdKey.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | enum DeclaringClassIdKey implements Key 17 | { 18 | case Key; 19 | } 20 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/DefaultValueExpressionKey.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | enum DefaultValueExpressionKey implements OptionalKey 17 | { 18 | case Key; 19 | 20 | public function default(TypedMap $map): mixed 21 | { 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/DeprecationKey.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | enum DeprecationKey implements OptionalKey 17 | { 18 | case Key; 19 | 20 | public function default(TypedMap $map): mixed 21 | { 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/FileKey.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | enum FileKey implements OptionalKey 16 | { 17 | case Key; 18 | 19 | public function default(TypedMap $map): mixed 20 | { 21 | return null; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/InterfacesKey.php: -------------------------------------------------------------------------------- 1 | >> 15 | */ 16 | enum InterfacesKey implements OptionalKey 17 | { 18 | case Key; 19 | 20 | public function default(TypedMap $map): mixed 21 | { 22 | return []; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/LocationKey.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | enum LocationKey implements OptionalKey 17 | { 18 | case Key; 19 | 20 | public function default(TypedMap $map): mixed 21 | { 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/NamedDataKeys.php: -------------------------------------------------------------------------------- 1 | > 14 | */ 15 | enum NamedDataKeys implements OptionalKey 16 | { 17 | case Aliases; 18 | case Constants; 19 | case Methods; 20 | case Parameters; 21 | case Properties; 22 | case Templates; 23 | 24 | public function default(TypedMap $map): mixed 25 | { 26 | return []; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/NamespaceKey.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | enum NamespaceKey implements Key 15 | { 16 | case Key; 17 | } 18 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/ParameterIndexKey.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | enum ParameterIndexKey implements Key 15 | { 16 | case Key; 17 | } 18 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/ParentsKey.php: -------------------------------------------------------------------------------- 1 | >> 15 | */ 16 | enum ParentsKey implements OptionalKey 17 | { 18 | case Key; 19 | 20 | public function default(TypedMap $map): mixed 21 | { 22 | return []; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/PassedBy.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | enum PassedByKey implements OptionalKey 16 | { 17 | case Key; 18 | 19 | public function default(TypedMap $map): mixed 20 | { 21 | return PassedBy::Value; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/PhpDocKey.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | enum PhpDocKey implements OptionalKey 17 | { 18 | case Key; 19 | 20 | public function default(TypedMap $map): mixed 21 | { 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/PhpExtensionKey.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | enum PhpExtensionKey implements OptionalKey 16 | { 17 | case Key; 18 | 19 | public function default(TypedMap $map): mixed 20 | { 21 | return null; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/ThrowsTypeKey.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | enum ThrowsTypeKey implements OptionalKey 17 | { 18 | case Key; 19 | 20 | public function default(TypedMap $map): mixed 21 | { 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/TraitMethodAlias.php: -------------------------------------------------------------------------------- 1 | > 14 | */ 15 | enum TraitMethodAliasesKey implements OptionalKey 16 | { 17 | case Key; 18 | 19 | public function default(TypedMap $map): mixed 20 | { 21 | return []; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/TraitMethodPrecedenceKey.php: -------------------------------------------------------------------------------- 1 | > 16 | */ 17 | enum TraitMethodPrecedenceKey implements OptionalKey 18 | { 19 | case Key; 20 | 21 | public function default(TypedMap $map): mixed 22 | { 23 | return []; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/TypeDataKeys.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | enum TypeDataKeys implements OptionalKey 16 | { 17 | case Type; 18 | 19 | public function default(TypedMap $map): mixed 20 | { 21 | return new TypeData(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/UnresolvedInterfacesKey.php: -------------------------------------------------------------------------------- 1 | >> 15 | */ 16 | enum UnresolvedInterfacesKey implements OptionalKey 17 | { 18 | case Key; 19 | 20 | public function default(TypedMap $map): mixed 21 | { 22 | return []; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/UnresolvedParentKey.php: -------------------------------------------------------------------------------- 1 | }> 15 | */ 16 | enum UnresolvedParentKey implements OptionalKey 17 | { 18 | case Key; 19 | 20 | public function default(TypedMap $map): mixed 21 | { 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/UnresolvedTraitsKey.php: -------------------------------------------------------------------------------- 1 | >> 15 | */ 16 | enum UnresolvedTraitsKey implements OptionalKey 17 | { 18 | case Key; 19 | 20 | public function default(TypedMap $map): mixed 21 | { 22 | return []; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/UsePhpDocsKey.php: -------------------------------------------------------------------------------- 1 | > 15 | */ 16 | enum UsePhpDocsKey implements OptionalKey 17 | { 18 | case Key; 19 | 20 | public function default(TypedMap $map): mixed 21 | { 22 | return []; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/ValueExpressionKey.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | enum ValueExpressionKey implements Key 16 | { 17 | case Key; 18 | } 19 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/VarianceKey.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | enum VarianceKey implements OptionalKey 17 | { 18 | case Key; 19 | 20 | public function default(TypedMap $map): mixed 21 | { 22 | return Variance::Invariant; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Data/Visibility.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | enum VisibilityKey implements OptionalKey 16 | { 17 | case Key; 18 | 19 | public function default(TypedMap $map): mixed 20 | { 21 | return null; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Hook/ClassHook.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | final class TypeResolvers extends DefaultTypeVisitor 17 | { 18 | /** 19 | * @param iterable> $resolvers 20 | */ 21 | public function __construct( 22 | private readonly iterable $resolvers = [], 23 | ) {} 24 | 25 | protected function default(Type $type): mixed 26 | { 27 | foreach ($this->resolvers as $resolver) { 28 | $type = $type->accept($resolver); 29 | } 30 | 31 | return $type; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Misc/NonSerializable.php: -------------------------------------------------------------------------------- 1 | $types 15 | */ 16 | public function __construct( 17 | private readonly array $types, 18 | ) {} 19 | 20 | public function allowsNull(): bool 21 | { 22 | return false; 23 | } 24 | 25 | public function getTypes(): array 26 | { 27 | return $this->types; 28 | } 29 | 30 | public function __toString(): string 31 | { 32 | return implode('&', $this->types); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Reflection/Internal/NativeAdapter/NativeTraitInfo.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | public readonly array $aliases; 19 | 20 | /** 21 | * @param list $names 22 | * @param list $aliases 23 | */ 24 | public function __construct( 25 | public readonly array $names = [], 26 | array $aliases = [], 27 | ) { 28 | $resolvedAliases = []; 29 | 30 | foreach ($aliases as $alias) { 31 | if ($alias->newName !== null) { 32 | $resolvedAliases[$alias->newName] = $alias->trait . '::' . $alias->method; 33 | } 34 | } 35 | 36 | $this->aliases = $resolvedAliases; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Reflection/Internal/NativeAdapter/NativeTraitInfoKey.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | enum NativeTraitInfoKey implements OptionalKey 16 | { 17 | case Key; 18 | 19 | public function default(TypedMap $map): mixed 20 | { 21 | return new NativeTraitInfo(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Reflection/Internal/NativeAdapter/NonConvertableType.php: -------------------------------------------------------------------------------- 1 | $types 15 | */ 16 | public function __construct( 17 | private readonly array $types, 18 | ) {} 19 | 20 | public function allowsNull(): bool 21 | { 22 | foreach ($this->types as $type) { 23 | if ($type->allowsNull()) { 24 | return true; 25 | } 26 | } 27 | 28 | return false; 29 | } 30 | 31 | /** 32 | * @psalm-suppress InvalidReturnType, InvalidReturnStatement, UnusedPsalmSuppress 33 | */ 34 | public function getTypes(): array 35 | { 36 | return $this->types; 37 | } 38 | 39 | public function __toString(): string 40 | { 41 | return implode('|', $this->types); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Reflection/Internal/PhpDoc/AlwaysTrimmingConstExprParser.php: -------------------------------------------------------------------------------- 1 | }> 16 | */ 17 | final class NamedObjectTypeDestructurizer extends DefaultTypeVisitor 18 | { 19 | public function namedObject(Type $type, NamedClassId|AnonymousClassId $classId, array $typeArguments): mixed 20 | { 21 | return [$classId, $typeArguments]; 22 | } 23 | 24 | protected function default(Type $type): mixed 25 | { 26 | return null; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Reflection/Internal/PhpDoc/PhpDocTagPrioritizer.php: -------------------------------------------------------------------------------- 1 | 3, 15 | '@phpstan' => 2, 16 | '@phan' => 1, 17 | ]; 18 | 19 | /** 20 | * @param array $prefixPriorities 21 | */ 22 | public function __construct( 23 | private readonly array $prefixPriorities = self::DEFAULT_PREFIX_PRIORITIES, 24 | ) {} 25 | 26 | public function priorityFor(string $tagName): int 27 | { 28 | foreach ($this->prefixPriorities as $prefix => $priority) { 29 | if (str_starts_with($tagName, $prefix)) { 30 | return $priority; 31 | } 32 | } 33 | 34 | return 0; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Reflection/Internal/PhpParser/NameParser.php: -------------------------------------------------------------------------------- 1 | getAttribute(Context::class); 21 | \assert($context instanceof Context); 22 | 23 | return $context; 24 | } 25 | 26 | public static function set(ClassMethod $node, Context $context): void 27 | { 28 | $node->setAttribute(Context::class, $context); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Reflection/Internal/PhpParser/PhpParserChecker.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | final class UnresolvedConstantType implements Type 17 | { 18 | /** 19 | * @param non-empty-string $namespacedName 20 | * @param non-empty-string $globalName 21 | */ 22 | public function __construct( 23 | private readonly string $namespacedName, 24 | private readonly string $globalName, 25 | ) {} 26 | 27 | public function accept(TypeVisitor $visitor): mixed 28 | { 29 | if (\defined($this->namespacedName)) { 30 | return $visitor->constant($this, Id::constant($this->namespacedName)); 31 | } 32 | 33 | return $visitor->constant($this, Id::constant($this->globalName)); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Reflection/Internal/Type/IsNativeTypeNullable.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | final class IsNativeTypeNullable extends DefaultTypeVisitor 16 | { 17 | public function null(Type $type): mixed 18 | { 19 | return true; 20 | } 21 | 22 | public function union(Type $type, array $ofTypes): mixed 23 | { 24 | foreach ($ofTypes as $ofType) { 25 | if ($ofType->accept($this)) { 26 | return true; 27 | } 28 | } 29 | 30 | return false; 31 | } 32 | 33 | public function mixed(Type $type): mixed 34 | { 35 | return true; 36 | } 37 | 38 | protected function default(Type $type): mixed 39 | { 40 | return false; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Reflection/KeyIsNotDefined.php: -------------------------------------------------------------------------------- 1 | findFile($id->name); 24 | 25 | if ($file !== false) { 26 | \assert($file !== ''); 27 | 28 | return Resource::fromFile($file); 29 | } 30 | } 31 | 32 | return null; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Reflection/Locator/ConstantLocator.php: -------------------------------------------------------------------------------- 1 | file); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Reflection/Locator/NamedClassLocator.php: -------------------------------------------------------------------------------- 1 | name); 20 | } catch (\ReflectionException) { 21 | return null; 22 | } 23 | 24 | $file = $reflection->getFileName(); 25 | 26 | if ($file === false) { 27 | return null; 28 | } 29 | 30 | $data = new TypedMap(); 31 | $extension = $reflection->getExtensionName(); 32 | 33 | if ($extension !== false) { 34 | $data = $data->with(Data::PhpExtension, $extension); 35 | } 36 | 37 | return Resource::fromFile($file, $data); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Reflection/Locator/NativeReflectionFunctionLocator.php: -------------------------------------------------------------------------------- 1 | name); 21 | } catch (\ReflectionException) { 22 | return null; 23 | } 24 | 25 | $file = $reflection->getFileName(); 26 | 27 | if ($file === false) { 28 | return null; 29 | } 30 | 31 | $data = new TypedMap(); 32 | $extension = $reflection->getExtensionName(); 33 | 34 | if ($extension !== false) { 35 | $data = $data->with(Data::PhpExtension, $extension); 36 | } 37 | 38 | return Resource::fromFile($file, $data); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Reflection/Locator/NoSymfonyPolyfillLocator.php: -------------------------------------------------------------------------------- 1 | locator = new Locators([$locator]); 23 | } 24 | 25 | public function locate(NamedFunctionId|NamedClassId $id): ?Resource 26 | { 27 | $resource = $this->locator->locate($id); 28 | 29 | if ($resource === null) { 30 | return null; 31 | } 32 | 33 | $file = $resource->data[Data::File]; 34 | 35 | if ($file !== null && str_contains($file, self::PATTERN)) { 36 | return null; 37 | } 38 | 39 | return $resource; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Reflection/Locator/OnlyLoadedClassLocator.php: -------------------------------------------------------------------------------- 1 | name)) { 22 | return $this->namedClassLocator->locate($id); 23 | } 24 | 25 | return null; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Reflection/Locator/ScannedResourceLocator.php: -------------------------------------------------------------------------------- 1 | 21 | */ 22 | private readonly array $idsMap; 23 | 24 | /** 25 | * @param list $ids 26 | */ 27 | public function __construct( 28 | array $ids, 29 | private readonly Resource $resource, 30 | ) { 31 | $idsMap = []; 32 | 33 | foreach ($ids as $id) { 34 | $idsMap[$id->encode()] = true; 35 | } 36 | 37 | $this->idsMap = $idsMap; 38 | } 39 | 40 | public function locate(ConstantId|NamedFunctionId|AnonymousFunctionId|NamedClassId|AnonymousClassId $id): ?Resource 41 | { 42 | return isset($this->idsMap[$id->encode()]) ? $this->resource : null; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Reflection/ModifierKind.php: -------------------------------------------------------------------------------- 1 | 10 | * @psalm-type Aliases = Collection 11 | * @psalm-type Templates = Collection 12 | * @psalm-type ClassConstants = Collection 13 | * @psalm-type Methods = Collection 14 | * @psalm-type Properties = Collection 15 | * @psalm-type Parameters = Collection 16 | */ 17 | enum ReflectionCollections {} 18 | -------------------------------------------------------------------------------- /src/Reflection/TypeKind.php: -------------------------------------------------------------------------------- 1 | ` in `types::intMask()` due to possibly overflowing bitmasks. 35 | 36 | ## [0.4.1] 2024-08-05 37 | 38 | ### Fixed 39 | 40 | - Replace self, parent and static type arguments in RecursiveTypeReplacer. 41 | -------------------------------------------------------------------------------- /src/Type/Internal/AliasType.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | final class AliasType implements Type 17 | { 18 | /** 19 | * @param list $arguments 20 | */ 21 | public function __construct( 22 | private readonly AliasId $alias, 23 | private readonly array $arguments, 24 | ) {} 25 | 26 | public function accept(TypeVisitor $visitor): mixed 27 | { 28 | return $visitor->alias($this, $this->alias, $this->arguments); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Type/Internal/ArgumentType.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | final class ArgumentType implements Type 17 | { 18 | public function __construct( 19 | private readonly ParameterId $parameter, 20 | ) {} 21 | 22 | public function accept(TypeVisitor $visitor): mixed 23 | { 24 | return $visitor->argument($this, $this->parameter); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Type/Internal/ArrayType.php: -------------------------------------------------------------------------------- 1 | > 15 | */ 16 | final class ArrayType implements Type 17 | { 18 | /** 19 | * @param array $elements 20 | */ 21 | public function __construct( 22 | private readonly Type $key, 23 | private readonly Type $value, 24 | private readonly array $elements, 25 | ) {} 26 | 27 | public function accept(TypeVisitor $visitor): mixed 28 | { 29 | return $visitor->array($this, $this->key, $this->value, $this->elements); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Type/Internal/CallableType.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | final class CallableType implements Type 17 | { 18 | /** 19 | * @param list $parameters 20 | */ 21 | public function __construct( 22 | private readonly array $parameters, 23 | private readonly Type $return, 24 | ) {} 25 | 26 | public function accept(TypeVisitor $visitor): mixed 27 | { 28 | return $visitor->callable($this, $this->parameters, $this->return); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Type/Internal/ClassConstantMaskType.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | final class ClassConstantMaskType implements Type 16 | { 17 | public function __construct( 18 | private readonly Type $class, 19 | private readonly string $namePrefix, 20 | ) {} 21 | 22 | public function accept(TypeVisitor $visitor): mixed 23 | { 24 | return $visitor->classConstantMask($this, $this->class, $this->namePrefix); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Type/Internal/ClassConstantType.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | final class ClassConstantType implements Type 16 | { 17 | /** 18 | * @param non-empty-string $name 19 | */ 20 | public function __construct( 21 | private readonly Type $class, 22 | private readonly string $name, 23 | ) {} 24 | 25 | public function accept(TypeVisitor $visitor): mixed 26 | { 27 | return $visitor->classConstant($this, $this->class, $this->name); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Type/Internal/ClassStringType.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | final class ClassStringType implements Type 16 | { 17 | public function __construct( 18 | private readonly Type $class, 19 | ) {} 20 | 21 | public function accept(TypeVisitor $visitor): mixed 22 | { 23 | return $visitor->classString($this, $this->class); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Type/Internal/ConditionalType.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | final class ConditionalType implements Type 16 | { 17 | public function __construct( 18 | private readonly Type $subject, 19 | private readonly Type $if, 20 | private readonly Type $then, 21 | private readonly Type $else, 22 | ) {} 23 | 24 | public function accept(TypeVisitor $visitor): mixed 25 | { 26 | return $visitor->conditional($this, $this->subject, $this->if, $this->then, $this->else); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Type/Internal/ConstantType.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | final class ConstantType implements Type 17 | { 18 | public function __construct( 19 | private readonly ConstantId $constant, 20 | ) {} 21 | 22 | public function accept(TypeVisitor $visitor): mixed 23 | { 24 | return $visitor->constant($this, $this->constant); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Type/Internal/FloatType.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | final class FloatType implements Type 18 | { 19 | public function __construct( 20 | private readonly Type $minType, 21 | private readonly Type $maxType, 22 | ) {} 23 | 24 | public function accept(TypeVisitor $visitor): mixed 25 | { 26 | return $visitor->float($this, $this->minType, $this->maxType); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Type/Internal/FloatValueType.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | final class FloatValueType implements Type 17 | { 18 | /** 19 | * @param TValue $value 20 | */ 21 | public function __construct( 22 | private readonly float $value, 23 | ) {} 24 | 25 | public function accept(TypeVisitor $visitor): mixed 26 | { 27 | return $visitor->floatValue($this, $this->value); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Type/Internal/IntMaskType.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | final class IntMaskType implements Type 16 | { 17 | public function __construct( 18 | private readonly Type $type, 19 | ) {} 20 | 21 | public function accept(TypeVisitor $visitor): mixed 22 | { 23 | return $visitor->intMask($this, $this->type); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Type/Internal/IntType.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | final class IntType implements Type 18 | { 19 | public function __construct( 20 | private readonly Type $minType, 21 | private readonly Type $maxType, 22 | ) {} 23 | 24 | public function accept(TypeVisitor $visitor): mixed 25 | { 26 | return $visitor->int($this, $this->minType, $this->maxType); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Type/Internal/IntValueType.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | final class IntValueType implements Type 17 | { 18 | /** 19 | * @param TValue $value 20 | */ 21 | public function __construct( 22 | private readonly int $value, 23 | ) {} 24 | 25 | public function accept(TypeVisitor $visitor): mixed 26 | { 27 | return $visitor->intValue($this, $this->value); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Type/Internal/IntersectionType.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | final class IntersectionType implements Type 16 | { 17 | /** 18 | * @param non-empty-list $types 19 | */ 20 | public function __construct( 21 | private readonly array $types, 22 | ) {} 23 | 24 | public function accept(TypeVisitor $visitor): mixed 25 | { 26 | return $visitor->intersection($this, $this->types); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Type/Internal/IterableType.php: -------------------------------------------------------------------------------- 1 | > 16 | */ 17 | final class IterableType implements Type 18 | { 19 | /** 20 | * @param Type $key 21 | * @param Type $value 22 | */ 23 | public function __construct( 24 | private readonly Type $key, 25 | private readonly Type $value, 26 | ) {} 27 | 28 | public function accept(TypeVisitor $visitor): mixed 29 | { 30 | return $visitor->iterable($this, $this->key, $this->value); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Type/Internal/KeyType.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | final class KeyType implements Type 16 | { 17 | public function __construct( 18 | private readonly Type $type, 19 | ) {} 20 | 21 | public function accept(TypeVisitor $visitor): mixed 22 | { 23 | return $visitor->key($this, $this->type); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Type/Internal/ListType.php: -------------------------------------------------------------------------------- 1 | > 15 | */ 16 | final class ListType implements Type 17 | { 18 | /** 19 | * @param array $elements 20 | */ 21 | public function __construct( 22 | private readonly Type $value, 23 | private readonly array $elements, 24 | ) {} 25 | 26 | public function accept(TypeVisitor $visitor): mixed 27 | { 28 | return $visitor->list($this, $this->value, $this->elements); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Type/Internal/LiteralType.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | final class LiteralType implements Type 17 | { 18 | /** 19 | * @param Type $type 20 | */ 21 | public function __construct( 22 | private readonly Type $type, 23 | ) {} 24 | 25 | public function accept(TypeVisitor $visitor): mixed 26 | { 27 | return $visitor->literal($this, $this->type); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Type/Internal/NamedObjectType.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | final class NamedObjectType implements Type 18 | { 19 | /** 20 | * @param list $arguments 21 | */ 22 | public function __construct( 23 | private readonly NamedClassId|AnonymousClassId $class, 24 | private readonly array $arguments, 25 | ) {} 26 | 27 | public function accept(TypeVisitor $visitor): mixed 28 | { 29 | return $visitor->namedObject($this, $this->class, $this->arguments); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Type/Internal/NonEmptyArrayType.php: -------------------------------------------------------------------------------- 1 | > 15 | */ 16 | final class NonEmptyArrayType implements Type 17 | { 18 | public function __construct( 19 | private readonly Type $key, 20 | private readonly Type $value, 21 | ) {} 22 | 23 | public function accept(TypeVisitor $visitor): mixed 24 | { 25 | return $visitor->intersection($this, [ 26 | new NotType(new ArrayType(types::never, types::never, [])), 27 | new ArrayType($this->key, $this->value, []), 28 | ]); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Type/Internal/NotType.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | final class NotType implements Type 16 | { 17 | public function __construct( 18 | private readonly Type $type, 19 | ) {} 20 | 21 | public function accept(TypeVisitor $visitor): mixed 22 | { 23 | return $visitor->not($this, $this->type); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Type/Internal/ObjectType.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | final class ObjectType implements Type 17 | { 18 | /** 19 | * @param non-empty-array $properties 20 | */ 21 | public function __construct( 22 | private readonly array $properties, 23 | ) {} 24 | 25 | public function accept(TypeVisitor $visitor): mixed 26 | { 27 | return $visitor->object($this, $this->properties); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Type/Internal/OffsetType.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | final class OffsetType implements Type 16 | { 17 | public function __construct( 18 | private readonly Type $array, 19 | private readonly Type $key, 20 | ) {} 21 | 22 | public function accept(TypeVisitor $visitor): mixed 23 | { 24 | return $visitor->offset($this, $this->array, $this->key); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Type/Internal/ParentType.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | final class ParentType implements Type 17 | { 18 | /** 19 | * @param list $arguments 20 | */ 21 | public function __construct( 22 | private readonly array $arguments, 23 | private readonly ?NamedClassId $resolvedClass, 24 | ) {} 25 | 26 | public function accept(TypeVisitor $visitor): mixed 27 | { 28 | return $visitor->parent($this, $this->arguments, $this->resolvedClass); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Type/Internal/SelfType.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | final class SelfType implements Type 18 | { 19 | /** 20 | * @param list $arguments 21 | */ 22 | public function __construct( 23 | private readonly array $arguments, 24 | private readonly null|NamedClassId|AnonymousClassId $resolvedClass, 25 | ) {} 26 | 27 | public function accept(TypeVisitor $visitor): mixed 28 | { 29 | return $visitor->self($this, $this->arguments, $this->resolvedClass); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Type/Internal/StaticType.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | final class StaticType implements Type 18 | { 19 | /** 20 | * @param list $arguments 21 | */ 22 | public function __construct( 23 | private readonly array $arguments, 24 | private readonly null|NamedClassId|AnonymousClassId $resolvedClass, 25 | ) {} 26 | 27 | public function accept(TypeVisitor $visitor): mixed 28 | { 29 | return $visitor->static($this, $this->arguments, $this->resolvedClass); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Type/Internal/StringValueType.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | final class StringValueType implements Type 17 | { 18 | /** 19 | * @param TValue $value 20 | */ 21 | public function __construct( 22 | private readonly string $value, 23 | ) {} 24 | 25 | public function accept(TypeVisitor $visitor): mixed 26 | { 27 | return $visitor->stringValue($this, $this->value); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Type/Internal/TemplateType.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | final class TemplateType implements Type 17 | { 18 | public function __construct( 19 | private readonly TemplateId $template, 20 | ) {} 21 | 22 | public function accept(TypeVisitor $visitor): mixed 23 | { 24 | return $visitor->template($this, $this->template); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Type/Internal/UnionType.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | final class UnionType implements Type 17 | { 18 | /** 19 | * @param non-empty-list> $types 20 | */ 21 | public function __construct( 22 | private readonly array $types, 23 | ) {} 24 | 25 | public function accept(TypeVisitor $visitor): mixed 26 | { 27 | return $visitor->union($this, $this->types); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Type/Internal/VarianceAwareType.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | final class VarianceAwareType implements Type 18 | { 19 | /** 20 | * @param Type $type 21 | */ 22 | public function __construct( 23 | private readonly Type $type, 24 | private readonly Variance $variance, 25 | ) {} 26 | 27 | public function accept(TypeVisitor $visitor): mixed 28 | { 29 | return $visitor->varianceAware($this, $this->type, $this->variance); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Type/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-present Valentin Udaltsov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Type/Parameter.php: -------------------------------------------------------------------------------- 1 | $type 15 | */ 16 | public function __construct( 17 | public readonly Type $type = types::mixed, 18 | public readonly bool $hasDefault = false, 19 | public readonly bool $variadic = false, 20 | public readonly bool $byReference = false, 21 | ) {} 22 | 23 | public function with( 24 | ?Type $type = null, 25 | ?bool $hasDefault = null, 26 | ?bool $variadic = null, 27 | ?bool $byReference = null, 28 | ): self { 29 | return new self( 30 | type: $type ?? $this->type, 31 | hasDefault: $hasDefault ?? $this->hasDefault, 32 | variadic: $variadic ?? $this->variadic, 33 | byReference: $byReference ?? $this->byReference, 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Type/README.md: -------------------------------------------------------------------------------- 1 | # Typhoon Type 2 | 3 | This repository is a readonly Typhoon monorepo subsplit. 4 | Please, open pull requests and issues in the [main repository](https://github.com/typhoon-php/typhoon). 5 | 6 | Read [documentation](https://github.com/typhoon-php/typhoon/tree/0.4.x). 7 | -------------------------------------------------------------------------------- /src/Type/ShapeElement.php: -------------------------------------------------------------------------------- 1 | $type 15 | */ 16 | public function __construct( 17 | public readonly Type $type = types::mixed, 18 | public readonly bool $optional = false, 19 | ) {} 20 | 21 | public function with(?Type $type = null, ?bool $optional = null): self 22 | { 23 | return new self( 24 | type: $type ?? $this->type, 25 | optional: $optional ?? $this->optional, 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Type/Type.php: -------------------------------------------------------------------------------- 1 | $visitor 16 | * @return TReturn 17 | */ 18 | public function accept(TypeVisitor $visitor): mixed; 19 | } 20 | -------------------------------------------------------------------------------- /src/Type/Variance.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | private readonly array $typeArguments; 20 | 21 | /** 22 | * @param iterable $typeArguments 23 | */ 24 | public function __construct(iterable $typeArguments) 25 | { 26 | $map = []; 27 | 28 | foreach ($typeArguments as [$templateId, $type]) { 29 | $map[$templateId->encode()] = $type; 30 | } 31 | 32 | $this->typeArguments = $map; 33 | } 34 | 35 | public function template(Type $type, TemplateId $templateId): mixed 36 | { 37 | return $this->typeArguments[$templateId->encode()] ?? $type; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Type/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typhoon/type", 3 | "description": "Typhoon Type", 4 | "license": "MIT", 5 | "type": "library", 6 | "authors": [ 7 | { 8 | "name": "Valentin Udaltsov", 9 | "email": "udaltsov.valentin@gmail.com" 10 | }, 11 | { 12 | "name": "Typhoon Team", 13 | "homepage": "https://github.com/orgs/typhoon-php/people" 14 | } 15 | ], 16 | "require": { 17 | "php": "^8.1", 18 | "typhoon/declaration-id": "^0.4" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "Typhoon\\Type\\": "" 23 | }, 24 | "files": [ 25 | "functions.php" 26 | ] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Type/functions.php: -------------------------------------------------------------------------------- 1 | accept(TypeStringifier::Instance); 18 | } 19 | -------------------------------------------------------------------------------- /src/TypedMap/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [0.4.3] 2024-08-06 9 | 10 | ### Added 11 | 12 | - Make `TypedMap` implement `\Countable`. 13 | -------------------------------------------------------------------------------- /src/TypedMap/Key.php: -------------------------------------------------------------------------------- 1 | name)); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/TypedMap/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024-present Valentin Udaltsov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/TypedMap/OptionalKey.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | interface OptionalKey extends Key 14 | { 15 | /** 16 | * @return TValue 17 | */ 18 | public function default(TypedMap $map): mixed; 19 | } 20 | -------------------------------------------------------------------------------- /src/TypedMap/README.md: -------------------------------------------------------------------------------- 1 | # Typhoon Typed Map 2 | 3 | This repository is a readonly Typhoon monorepo subsplit. 4 | Please, open pull requests and issues in the [main repository](https://github.com/typhoon-php/typhoon). 5 | 6 | Read [documentation](https://github.com/typhoon-php/typhoon/tree/0.4.x). 7 | -------------------------------------------------------------------------------- /src/TypedMap/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typhoon/typed-map", 3 | "description": "Typhoon Typed Map", 4 | "license": "MIT", 5 | "type": "library", 6 | "authors": [ 7 | { 8 | "name": "Valentin Udaltsov", 9 | "email": "udaltsov.valentin@gmail.com" 10 | }, 11 | { 12 | "name": "Typhoon Team", 13 | "homepage": "https://github.com/orgs/typhoon-php/people" 14 | } 15 | ], 16 | "require": { 17 | "php": "^8.1" 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "Typhoon\\TypedMap\\": "" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /stubs/PHPStan/PhpDocParser/Ast/ConstExpr/ConstExprArrayNode.phpstub: -------------------------------------------------------------------------------- 1 | */ 8 | public $items; 9 | } 10 | -------------------------------------------------------------------------------- /stubs/PHPStan/PhpDocParser/Ast/ConstExpr/ConstFetchNode.phpstub: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | public $templateTypes; 18 | } 19 | -------------------------------------------------------------------------------- /stubs/PHPStan/PhpDocParser/Ast/PhpDoc/PhpDocTagNode.phpstub: -------------------------------------------------------------------------------- 1 | */ 11 | public $parameters; 12 | } 13 | -------------------------------------------------------------------------------- /stubs/PHPStan/PhpDocParser/Ast/Type/GenericTypeNode.phpstub: -------------------------------------------------------------------------------- 1 | */ 12 | public $genericTypes; 13 | } 14 | -------------------------------------------------------------------------------- /stubs/PHPStan/PhpDocParser/Ast/Type/IdentifierTypeNode.phpstub: -------------------------------------------------------------------------------- 1 | */ 12 | public $types; 13 | } 14 | -------------------------------------------------------------------------------- /stubs/PHPStan/PhpDocParser/Ast/Type/UnionTypeNode.phpstub: -------------------------------------------------------------------------------- 1 | */ 12 | public $types; 13 | } 14 | -------------------------------------------------------------------------------- /stubs/PhpParser/Comment.phpstub: -------------------------------------------------------------------------------- 1 | */ 8 | public $types; 9 | } 10 | -------------------------------------------------------------------------------- /stubs/PhpParser/Node/Name.phpstub: -------------------------------------------------------------------------------- 1 | toString() 21 | */ 22 | public function isFullyQualified() {} 23 | 24 | /** 25 | * @param string|string[]|self|null $name1 26 | * @param string|string[]|self|null $name2 27 | * @return ($name1 is null ? ($name2 is null ? null : static) : static) 28 | */ 29 | public static function concat($name1, $name2, array $attributes = []) {} 30 | 31 | /** 32 | * @return non-empty-list 33 | */ 34 | public function getParts() {} 35 | } 36 | -------------------------------------------------------------------------------- /stubs/PhpParser/Node/Stmt/TraitUseAdaptation/Alias.phpstub: -------------------------------------------------------------------------------- 1 | */ 8 | public $types; 9 | } 10 | -------------------------------------------------------------------------------- /stubs/Reflection.phpstub: -------------------------------------------------------------------------------- 1 | 6 | */ 7 | class ReflectionEnum {} 8 | 9 | /** 10 | * @template-covariant TClass as object 11 | */ 12 | class ReflectionAttribute {} 13 | 14 | class ReflectionFunction 15 | { 16 | /** 17 | * @var non-empty-string 18 | */ 19 | public string $name; 20 | } 21 | -------------------------------------------------------------------------------- /tests/ChangeDetector/ExtensionIsNotInstalledTest.php: -------------------------------------------------------------------------------- 1 | getMessage()); 18 | } 19 | 20 | public function testPreviousPreserved(): void 21 | { 22 | $previous = new \Exception(); 23 | 24 | $exception = new ExtensionIsNotInstalled('pcntl', $previous); 25 | 26 | self::assertSame($previous, $exception->getPrevious()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/ChangeDetector/FileIsNotReadableTest.php: -------------------------------------------------------------------------------- 1 | getMessage()); 18 | } 19 | 20 | public function testPreviousPreserved(): void 21 | { 22 | $previous = new \Exception(); 23 | 24 | $exception = new FileIsNotReadable('a.txt', $previous); 25 | 26 | self::assertSame($previous, $exception->getPrevious()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/ChangeDetector/PackageIsNotInstalledTest.php: -------------------------------------------------------------------------------- 1 | getMessage()); 18 | } 19 | 20 | public function testPreviousPreserved(): void 21 | { 22 | $previous = new \Exception(); 23 | 24 | $exception = new PackageIsNotInstalled('typhoon/type', $previous); 25 | 26 | self::assertSame($previous, $exception->getPrevious()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/DeclarationId/Fixtures/anonymous_array_object_5_5.php: -------------------------------------------------------------------------------- 1 | */ 5 | class 6 | extends ArrayObject {}; 7 | -------------------------------------------------------------------------------- /tests/DeclarationId/Fixtures/anonymous_class_3_12.php: -------------------------------------------------------------------------------- 1 | $default; 35 | } 36 | } 37 | 38 | public function setMultiple(iterable $values, null|\DateInterval|int $ttl = null): bool 39 | { 40 | return true; 41 | } 42 | 43 | public function deleteMultiple(iterable $keys): bool 44 | { 45 | return true; 46 | } 47 | 48 | public function has(string $key): bool 49 | { 50 | return false; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/Reflection/Internal/GetNamespaceTest.php: -------------------------------------------------------------------------------- 1 | accept(new NamedObjectTypeDestructurizer()); 18 | 19 | self::assertNull($destructurized); 20 | } 21 | 22 | public function testItDestructuresNamedObject(): void 23 | { 24 | $type = types::object(\ArrayAccess::class, [types::int, types::string]); 25 | 26 | $destructurized = $type->accept(new NamedObjectTypeDestructurizer()); 27 | 28 | self::assertEquals( 29 | [Id::namedClass(\ArrayAccess::class), [types::int, types::string]], 30 | $destructurized, 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Reflection/Internal/PhpDoc/PrefixBasedTagPrioritizerTest.php: -------------------------------------------------------------------------------- 1 | priorityFor('@psalm-var'); 18 | $phpStanPriority = $prioritizer->priorityFor('@phpstan-var'); 19 | 20 | self::assertGreaterThan($phpStanPriority, $psalmPriority); 21 | } 22 | 23 | public function testPHPStanTagHasHigherPriorityOverStandardTag(): void 24 | { 25 | $prioritizer = new PrefixBasedPhpDocTagPrioritizer(); 26 | 27 | $standardTagPriority = $prioritizer->priorityFor('@var'); 28 | $phpStanPriority = $prioritizer->priorityFor('@phpstan-var'); 29 | 30 | self::assertGreaterThan($standardTagPriority, $phpStanPriority); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tests/Reflection/Internal/PhpParser/FindAndCompileVisitor.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | public array $expressions = []; 19 | 20 | /** 21 | * @param \Closure(Node): \Generator $expressionFinder 22 | */ 23 | public function __construct( 24 | private readonly ContextProvider $contextProvider, 25 | private readonly \Closure $expressionFinder, 26 | ) {} 27 | 28 | public function leaveNode(Node $node): ?int 29 | { 30 | $compiler = new ConstantExpressionCompiler($this->contextProvider->get()); 31 | 32 | foreach (($this->expressionFinder)($node) as $key => $expr) { 33 | $this->expressions[$key] = $compiler->compile($expr); 34 | } 35 | 36 | return null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/Reflection/Internal/PhpParser/FindAndReflectVisitor.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | public array $types = []; 19 | 20 | /** 21 | * @param \Closure(Node): \Generator $expressionFinder 22 | */ 23 | public function __construct( 24 | private readonly ContextProvider $contextProvider, 25 | private readonly \Closure $expressionFinder, 26 | ) {} 27 | 28 | public function leaveNode(Node $node): ?int 29 | { 30 | $reflector = new ConstantExpressionTypeReflector($this->contextProvider->get()); 31 | 32 | foreach (($this->expressionFinder)($node) as $key => $expr) { 33 | $this->types[$key] = $reflector->reflect($expr); 34 | } 35 | 36 | return null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/Reflection/KeyIsNotDefinedTest.php: -------------------------------------------------------------------------------- 1 | getMessage(), 'Key "\"\'\"" is not defined in the Collection'); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/Reflection/TyphoonReflectorMemoryTest.php: -------------------------------------------------------------------------------- 1 | reflectClass(\AppendIterator::class); 20 | $weakReflector = \WeakReference::create($reflector); 21 | $weakReflection = \WeakReference::create($reflection); 22 | 23 | unset($reflection, $reflector); 24 | 25 | // assertTrue() is used instead of assertNull() to avoid huge reflector dump in the diff 26 | self::assertTrue($weakReflector->get() === null, 'Reflector is not garbage collected.'); 27 | self::assertTrue($weakReflection->get() === null, 'ClassReflection is not garbage collected.'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/alias/lines.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode(<<<'PHP' 13 | reflectClass('A') 21 | ->aliases(); 22 | 23 | assertSame(3, $aliases['First']->location()?->startLine); 24 | assertSame(3, $aliases['First']->location()?->endLine); 25 | assertSame(4, $aliases['Second']->location()?->startLine); 26 | assertSame(4, $aliases['Second']->location()?->endLine); 27 | }; 28 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/anonymous_class_id.php: -------------------------------------------------------------------------------- 1 | reflectClass($object::class)->id; 16 | 17 | assertInstanceOf(AnonymousClassId::class, $id); 18 | assertSame(__FILE__, $id->file); 19 | assertSame(13, $id->line); 20 | assertNull($id->column); 21 | assertSame($object::class, $id->name); 22 | }; 23 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/deprecation.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 15 | <<<'PHP' 16 | reflectClass('NotDeprecated')->isDeprecated()); 28 | assertNull($reflector->reflectClass('NotDeprecated')->deprecation()); 29 | 30 | assertTrue($reflector->reflectClass('Deprecated')->isDeprecated()); 31 | assertEquals(new Deprecation(), $reflector->reflectClass('Deprecated')->deprecation()); 32 | 33 | assertTrue($reflector->reflectClass('DeprecatedWithMessage')->isDeprecated()); 34 | assertEquals(new Deprecation('Message'), $reflector->reflectClass('DeprecatedWithMessage')->deprecation()); 35 | }; 36 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/enum_case/deprecation.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 16 | <<<'PHP' 17 | reflectClass('A') 29 | ->enumCases(); 30 | 31 | assertFalse($cases['NotDeprecated']->isDeprecated()); 32 | assertNull($cases['NotDeprecated']->deprecation()); 33 | assertTrue($cases['Deprecated']->isDeprecated()); 34 | assertEquals(new Deprecation(), $cases['Deprecated']->deprecation()); 35 | assertTrue($cases['DeprecatedWithMessage']->isDeprecated()); 36 | assertEquals(new Deprecation('Message'), $cases['DeprecatedWithMessage']->deprecation()); 37 | }; 38 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/enum_case/type.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode('reflectClass('A') 16 | ->enumCases()['X']; 17 | 18 | assertNull($reflection->type(TypeKind::Native)); 19 | assertEquals(types::classConstant('A', 'X'), $reflection->type()); 20 | assertNull($reflection->type(TypeKind::Native)); 21 | assertNull($reflection->type(TypeKind::Annotated)); 22 | assertEquals(types::classConstant('A', 'X'), $reflection->type(TypeKind::Inferred)); 23 | }; 24 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/exception_for_anonymous_in_non_readable_file.php: -------------------------------------------------------------------------------- 1 | expectExceptionObject(new FileIsNotReadable($file)); 16 | 17 | $reflector->reflect($id); 18 | }; 19 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/exception_for_non_existing_class.php: -------------------------------------------------------------------------------- 1 | expectExceptionObject(new DeclarationNotFound(Id::namedClass($class))); 15 | 16 | $reflector->reflectClass($class); 17 | }; 18 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/infinite_recursion.php: -------------------------------------------------------------------------------- 1 | expectExceptionObject(new \LogicException('Infinite recursive reflection of class A detected')); 12 | 13 | $reflector 14 | ->withResource(Resource::fromCode('reflectClass('A'); 16 | }; 17 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/multiple_anonymous_classes_on_line.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 14 | 'with(Data::File, 'some.php'), 16 | )); 17 | 18 | $test->expectExceptionMessage('because 2 anonymous classes are declared at columns 11, 25'); 19 | 20 | $reflector->reflectAnonymousClass('some.php', 1); 21 | }; 22 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/phpdoc_method/native_reflection.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 14 | <<<'PHP' 15 | reflectClass('A'); 23 | 24 | assertEmpty($class->toNativeReflection()->getMethods()); 25 | assertFalse($class->toNativeReflection()->hasMethod('m')); 26 | }; 27 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/phpdoc_method/parameter/defaults.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 15 | <<<'PHP' 16 | with(Data::File, 'dir/file.php'), 23 | )) 24 | ->reflectClass('A') 25 | ->methods()['m'] 26 | ->parameters(); 27 | 28 | assertSame($parameters['f']->evaluateDefault(), 'dir/file.php'); 29 | assertSame($parameters['d']->evaluateDefault(), 'dir'); 30 | assertSame($parameters['l']->evaluateDefault(), 3); 31 | assertSame($parameters['c']->evaluateDefault(), 'A'); 32 | assertSame($parameters['m']->evaluateDefault(), 'A::m'); 33 | assertSame($parameters['func']->evaluateDefault(), 'm'); 34 | assertSame($parameters['str']->evaluateDefault(), "\"\n"); 35 | }; 36 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/phpdoc_method/parameter/is_annotated.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 14 | <<<'PHP' 15 | reflectClass('A') 23 | ->methods()['m'] 24 | ->parameters(); 25 | 26 | assertTrue($parameters['param']->isAnnotated()); 27 | assertFalse($parameters['param']->isNative()); 28 | }; 29 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/phpdoc_method/templates.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 15 | <<<'PHP' 16 | (T $t) 19 | */ 20 | final class A {} 21 | PHP, 22 | )) 23 | ->reflectClass('A') 24 | ->methods()['m']; 25 | 26 | assertSame($method->templates()->keys(), ['T', 'T2']); 27 | assertEquals(types::methodTemplate('A', 'm', 'T2'), $method->returnType()); 28 | assertEquals(types::methodTemplate('A', 'm', 'T'), $method->parameters()['t']->type()); 29 | }; 30 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/self_static_in_final_class.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 13 | <<<'PHP' 14 | reflectClass('A'); 25 | 26 | assertEquals(types::self(resolvedClass: 'A'), $reflection->properties()['self']->type()); 27 | assertEquals(types::static(resolvedClass: 'A'), $reflection->properties()['static']->type()); 28 | }; 29 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/template/constraints.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode(<<<'PHP' 14 | 19 | */ 20 | class A {} 21 | PHP)) 22 | ->reflectClass('A') 23 | ->templates(); 24 | 25 | assertEquals(types::mixed, $templates['TMixed']->constraint()); 26 | assertEquals(types::string, $templates['TString']->constraint()); 27 | assertEquals( 28 | types::iterable( 29 | types::classTemplate('A', 'TMixed'), 30 | types::classTemplate('A', 'TString'), 31 | ), 32 | $templates['TComplex']->constraint(), 33 | ); 34 | }; 35 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/template/indexes.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode(<<<'PHP' 13 | reflectClass('A') 24 | ->templates(); 25 | 26 | assertSame(0, $templates['T0']->index()); 27 | assertSame(1, $templates['T1']->index()); 28 | assertSame(2, $templates['T2']->index()); 29 | }; 30 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/template/lines.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode(<<<'PHP' 13 | reflectClass('A') 22 | ->templates(); 23 | 24 | assertSame(3, $templates['TSingleLine']->location()?->startLine); 25 | assertSame(4, $templates['TSingleLine']->location()?->startColumn); 26 | assertSame(3, $templates['TSingleLine']->location()?->endLine); 27 | assertSame(25, $templates['TSingleLine']->location()?->endColumn); 28 | 29 | assertSame(4, $templates['TMultiLine']->location()?->startLine); 30 | assertSame(4, $templates['TMultiLine']->location()?->startColumn); 31 | assertSame(5, $templates['TMultiLine']->location()?->endLine); 32 | assertSame(43, $templates['TMultiLine']->location()?->endColumn); 33 | }; 34 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/template/variance.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode(<<<'PHP' 14 | reflectClass('A') 23 | ->templates(); 24 | 25 | assertSame(Variance::Invariant, $templates['TInvariant']->variance()); 26 | assertSame(Variance::Covariant, $templates['TCovariant']->variance()); 27 | assertSame(Variance::Contravariant, $templates['TContravariant']->variance()); 28 | }; 29 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/type_inheritance/class_implements_multiple_interfaces.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 14 | <<<'PHP' 15 | reflectClass('B') 32 | ->methods()['a']; 33 | 34 | assertEquals(types::string, $method->returnType(TypeKind::Native)); 35 | assertEquals(types::nonEmptyString, $method->returnType(TypeKind::Annotated)); 36 | assertEquals(types::nonEmptyString, $method->returnType()); 37 | }; 38 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/type_inheritance/interface_extends_multiple_interfaces.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 14 | <<<'PHP' 15 | reflectClass('I12') 31 | ->methods()['a']; 32 | 33 | assertEquals(types::string, $method->returnType(TypeKind::Native)); 34 | assertEquals(types::nonEmptyString, $method->returnType(TypeKind::Annotated)); 35 | assertEquals(types::nonEmptyString, $method->returnType()); 36 | }; 37 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/type_inheritance/native_override.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 15 | <<<'PHP' 16 | reflectClass('B') 29 | ->methods()['a']; 30 | 31 | assertEquals(types::string, $method->returnType(TypeKind::Native)); 32 | assertNull($method->returnType(TypeKind::Annotated)); 33 | assertEquals(types::string, $method->returnType()); 34 | }; 35 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/type_inheritance/parameter_inheritance_is_resolved_by_position.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 13 | <<<'PHP' 14 | reflectClass('B'); 30 | 31 | assertEquals(types::nonEmptyString, $reflection->methods()['a']->parameters()['differentName']->type()); 32 | assertEquals(types::int, $reflection->methods()['a']->parameters()['name']->type()); 33 | }; 34 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/type_inheritance/parent.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 13 | <<<'PHP' 14 | reflectClass('B')->methods()['a']->returnType()); 25 | assertEquals(types::parent(resolvedClass: 'A'), $reflector->reflectClass('C')->methods()['a']->returnType()); 26 | }; 27 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/type_inheritance/phpdoc_type_inheritance.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 14 | <<<'PHP' 15 | reflectClass('B') 28 | ->methods()['a']; 29 | 30 | assertEquals(types::string, $method->returnType(TypeKind::Native)); 31 | assertEquals(types::nonEmptyString, $method->returnType(TypeKind::Annotated)); 32 | assertEquals(types::nonEmptyString, $method->returnType()); 33 | }; 34 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/type_inheritance/self.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 13 | <<<'PHP' 14 | reflectClass('A')->methods()['a']->returnType()); 25 | assertEquals(types::self(resolvedClass: 'A'), $reflector->reflectClass('B')->methods()['a']->returnType()); 26 | assertEquals(types::self(resolvedClass: 'A'), $reflector->reflectClass('C')->methods()['a']->returnType()); 27 | }; 28 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/type_inheritance/static.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 13 | <<<'PHP' 14 | reflectClass('A')->methods()['a']->returnType()); 25 | assertEquals(types::static(resolvedClass: 'B'), $reflector->reflectClass('B')->methods()['a']->returnType()); 26 | assertEquals(types::static(resolvedClass: 'C'), $reflector->reflectClass('C')->methods()['a']->returnType()); 27 | }; 28 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/class/type_inheritance/template_resolution.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 13 | <<<'PHP' 14 | */ 27 | use TR; 28 | } 29 | 30 | /** @extends A */ 31 | final class B extends A {} 32 | PHP, 33 | ))->reflectClass('B'); 34 | 35 | assertEquals(types::string, $reflection->properties()['property']->type()); 36 | }; 37 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/constant_const/type.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 16 | <<<'PHP' 17 | reflectConstant('X\A'); 28 | 29 | assertSame(types::positiveInt, $constant->type()); 30 | assertEquals(types::int(123), $constant->type(TypeKind::Inferred)); 31 | assertNull($constant->type(TypeKind::Native)); 32 | assertNull($constant->type(TypeKind::Tentative)); 33 | assertSame(types::positiveInt, $constant->type(TypeKind::Annotated)); 34 | }; 35 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/constant_define/evaluate_php_int_min.php: -------------------------------------------------------------------------------- 1 | reflectConstant('PHP_INT_MIN')->evaluate(); 11 | 12 | assertSame(PHP_INT_MIN, $value); 13 | }; 14 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/constant_define/type.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 15 | <<<'PHP' 16 | reflectConstant('Y\A'); 24 | 25 | assertEquals(types::int(123), $constant->type()); 26 | assertEquals(types::int(123), $constant->type(TypeKind::Inferred)); 27 | assertNull($constant->type(TypeKind::Native)); 28 | assertNull($constant->type(TypeKind::Tentative)); 29 | assertNull($constant->type(TypeKind::Annotated)); 30 | }; 31 | -------------------------------------------------------------------------------- /tests/Reflection/functional_tests/function/deprecation.php: -------------------------------------------------------------------------------- 1 | withResource(Resource::fromCode( 15 | <<<'PHP' 16 | reflectFunction('notDeprecated')->isDeprecated()); 28 | assertNull($reflector->reflectFunction('notDeprecated')->deprecation()); 29 | 30 | assertTrue($reflector->reflectFunction('deprecated')->isDeprecated()); 31 | assertEquals(new Deprecation(), $reflector->reflectFunction('deprecated')->deprecation()); 32 | 33 | assertTrue($reflector->reflectFunction('deprecatedWithMessage')->isDeprecated()); 34 | assertEquals(new Deprecation('Message'), $reflector->reflectFunction('deprecatedWithMessage')->deprecation()); 35 | }; 36 | -------------------------------------------------------------------------------- /tests/Reflection/functions.php: -------------------------------------------------------------------------------- 1 | tokenize($type)); 22 | $typeNode = $typeParser->parse($tokens); 23 | 24 | return $phpDocTypeReflector->reflectType($typeNode); 25 | } 26 | -------------------------------------------------------------------------------- /tests/Type/ParameterTest.php: -------------------------------------------------------------------------------- 1 | type); 18 | self::assertFalse($parameter->hasDefault); 19 | self::assertFalse($parameter->variadic); 20 | self::assertFalse($parameter->byReference); 21 | } 22 | 23 | public function testWith(): void 24 | { 25 | $parameter = new Parameter(); 26 | 27 | $newParameter = $parameter->with( 28 | type: types::bool, 29 | hasDefault: true, 30 | variadic: true, 31 | byReference: true, 32 | ); 33 | 34 | self::assertSame(types::bool, $newParameter->type); 35 | self::assertTrue($newParameter->hasDefault); 36 | self::assertTrue($newParameter->variadic); 37 | self::assertTrue($newParameter->byReference); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/Type/RoutingTester.php: -------------------------------------------------------------------------------- 1 | 12 | */ 13 | abstract class RoutingTester extends DefaultTypeVisitor 14 | { 15 | protected function default(Type $type): never 16 | { 17 | Assert::fail('Type was routed to a different method'); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/Type/ShapeElementTest.php: -------------------------------------------------------------------------------- 1 | type); 18 | self::assertFalse($element->optional); 19 | } 20 | 21 | public function testWith(): void 22 | { 23 | $element = new ShapeElement(); 24 | 25 | $newElement = $element->with( 26 | type: types::bool, 27 | optional: true, 28 | ); 29 | 30 | self::assertSame(types::bool, $newElement->type); 31 | self::assertTrue($newElement->optional); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/Type/Visitor/DefaultTypeVisitorTest.php: -------------------------------------------------------------------------------- 1 | expectNotToPerformAssertions(); 17 | 18 | /** @phpstan-ignore expr.resultUnused */ 19 | new /** @extends DefaultTypeVisitor */ class extends DefaultTypeVisitor { 20 | protected function default(Type $type): mixed 21 | { 22 | return null; 23 | } 24 | }; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/TypedMap/KeyIsNotDefinedTest.php: -------------------------------------------------------------------------------- 1 | getMessage()); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/TypedMap/Keys.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | enum Keys implements Key 11 | { 12 | case A; 13 | case B; 14 | } 15 | -------------------------------------------------------------------------------- /tests/TypedMap/OptionalKeys.php: -------------------------------------------------------------------------------- 1 | 9 | */ 10 | enum OptionalKeys implements OptionalKey 11 | { 12 | public const DEFAULT = '129afde0-d3d2-4b36-b073-b06fecb6d775'; 13 | case A; 14 | case B; 15 | 16 | public function default(TypedMap $map): mixed 17 | { 18 | return self::DEFAULT; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tools/composer-require-checker/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require-dev": { 3 | "maglnet/composer-require-checker": "^4.7.1" 4 | }, 5 | "config": { 6 | "lock": false, 7 | "platform": { 8 | "php": "8.1" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tools/phpunit/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require-dev": { 3 | "phpunit/phpunit": "^10.5.30" 4 | }, 5 | "config": { 6 | "lock": false, 7 | "platform": { 8 | "php": "8.1" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tools/psalm/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require-dev": { 3 | "phpunit/phpunit": "^10.5.30", 4 | "psalm/plugin-phpunit": "^0.18.4", 5 | "vimeo/psalm": "^5.25.0" 6 | }, 7 | "conflict": { 8 | "netresearch/jsonmapper": "<4", 9 | "vimeo/psalm": "5.24" 10 | }, 11 | "autoload-dev": { 12 | "psr-4": { 13 | "Typhoon\\Tools\\Psalm\\": "src/" 14 | } 15 | }, 16 | "config": { 17 | "lock": false, 18 | "platform": { 19 | "php": "8.1" 20 | } 21 | } 22 | } 23 | --------------------------------------------------------------------------------