From 80a73afbdc4ee4b1d46cf9748f5302b482fbdd3c Mon Sep 17 00:00:00 2001 From: "J. N" Date: Tue, 27 Jul 2021 23:37:12 +0200 Subject: [PATCH] Use PHP8 Attributes instead of old annotations --- .gitignore | 2 +- .php-cs-fixer.cache | 2 +- Annotations/TSConstant.php | 13 - Attributes/Constant.php | 19 + Attributes/Enum.php | 19 + Commands/GenerateCommand.php | 203 +-------- Configuration/Config.php | 31 +- Contracts/SelfValidationInterface.php | 2 +- Dto/ConstantCollectResult.php | 29 ++ README.md | 50 ++- Services/ConstantAnnotationReader.php | 46 -- Services/Generator.php | 209 +++++++++ Services/TransformService.php | 76 ---- Tests/AnnotationTest.php | 34 -- Tests/Fixtures/AnnotationTestFixture.php | 14 +- Tests/GeneratorTest.php | 36 ++ Tests/Traits/MockTrait.php | 14 +- Tests/TransformServiceTest.php | 28 -- Tests/bootstrap.php | 4 +- Traits/Assert.php | 28 ++ Traits/SelfValidationTrait.php | 2 +- ...{php-const-to-ts.php => ts-const-enum.php} | 6 +- composer.json | 7 +- composer.lock | 404 +++++++++--------- psalm.xml | 23 + 25 files changed, 641 insertions(+), 660 deletions(-) delete mode 100644 Annotations/TSConstant.php create mode 100644 Attributes/Constant.php create mode 100644 Attributes/Enum.php create mode 100644 Dto/ConstantCollectResult.php delete mode 100644 Services/ConstantAnnotationReader.php create mode 100644 Services/Generator.php delete mode 100644 Services/TransformService.php delete mode 100644 Tests/AnnotationTest.php create mode 100644 Tests/GeneratorTest.php delete mode 100644 Tests/TransformServiceTest.php create mode 100644 Traits/Assert.php rename bin/{php-const-to-ts.php => ts-const-enum.php} (93%) create mode 100644 psalm.xml diff --git a/.gitignore b/.gitignore index cc68d5f..323599d 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,6 @@ fabric.properties # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser .generated/ -.php-const-to-ts-config.php +.ts-const-enum-config.php composer.phar ./TestPackage \ No newline at end of file diff --git a/.php-cs-fixer.cache b/.php-cs-fixer.cache index 89db4a9..1c159b5 100644 --- a/.php-cs-fixer.cache +++ b/.php-cs-fixer.cache @@ -1 +1 @@ -{"php":"8.0.1","version":"3.0.1-DEV:3.0.x-dev#82824602da2f4f2d75b1e4a272b24984790acbb5","indent":" ","lineEnding":"\n","rules":{"blank_line_after_namespace":true,"braces":{"allow_single_line_anonymous_class_with_empty_body":true,"allow_single_line_closure":true},"constant_case":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline"},"no_break_comment":true,"no_closing_tag":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":true,"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":true,"encoding":true,"full_opening_tag":true,"align_multiline_comment":true,"array_indentation":true,"combine_consecutive_issets":true,"combine_consecutive_unsets":true,"escape_implicit_backslashes":true,"explicit_indirect_variable":true,"explicit_string_variable":true,"heredoc_to_nowdoc":true,"method_chaining_indentation":true,"multiline_comment_opening_closing":true,"multiline_whitespace_before_semicolons":{"strategy":"new_line_for_chained_calls"},"no_extra_blank_lines":true,"no_null_property_initialization":true,"no_superfluous_elseif":true,"no_useless_else":true,"no_useless_return":true,"operator_linebreak":{"only_booleans":true},"ordered_class_elements":true,"phpdoc_order_by_value":true,"phpdoc_var_annotation_correct_order":true,"return_assignment":true,"simple_to_complex_string_variable":true,"single_line_comment_style":true,"array_syntax":{"syntax":"short"},"backtick_to_shell_exec":true,"binary_operator_spaces":true,"cast_spaces":{"space":"none"},"class_attributes_separation":{"elements":{"method":"one"}},"clean_namespace":true,"concat_space":{"spacing":"one"},"echo_tag_syntax":true,"fully_qualified_strict_types":true,"function_typehint_space":true,"general_phpdoc_tag_rename":{"replacements":{"inheritDocs":"inheritDoc"}},"include":true,"increment_style":true,"lambda_not_used_import":true,"linebreak_after_opening_tag":true,"magic_constant_casing":true,"magic_method_casing":true,"native_function_casing":true,"native_function_type_declaration_casing":true,"no_alias_language_construct_call":true,"no_alternative_syntax":true,"no_binary_string":true,"no_blank_lines_after_phpdoc":true,"no_empty_comment":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_leading_namespace_whitespace":true,"no_mixed_echo_print":true,"no_multiline_whitespace_around_double_arrow":true,"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_superfluous_phpdoc_tags":{"allow_mixed":true,"allow_unused_params":true},"no_trailing_comma_in_list_call":true,"no_trailing_comma_in_singleline_array":true,"no_unneeded_control_parentheses":{"statements":["break","clone","continue","echo_print","return","switch_case","yield","yield_from"]},"no_unneeded_curly_braces":{"namespaces":true},"no_unset_cast":true,"no_unused_imports":true,"no_whitespace_before_comma_in_array":true,"normalize_index_brace":true,"object_operator_without_whitespace":true,"ordered_imports":true,"php_unit_fqcn_annotation":true,"php_unit_method_casing":true,"phpdoc_align":{"align":"left"},"phpdoc_annotation_without_dot":true,"phpdoc_indent":true,"phpdoc_inline_tag_normalizer":true,"phpdoc_no_access":true,"phpdoc_no_alias_tag":true,"phpdoc_no_package":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_return_self_reference":true,"phpdoc_scalar":true,"phpdoc_single_line_var_spacing":true,"phpdoc_summary":true,"phpdoc_tag_type":{"tags":{"inheritDoc":"inline"}},"phpdoc_trim":true,"phpdoc_trim_consecutive_blank_line_separation":true,"phpdoc_types":true,"protected_to_private":true,"semicolon_after_instruction":true,"single_quote":true,"single_space_after_construct":true,"space_after_semicolon":{"remove_in_empty_for_expressions":true},"standardize_increment":true,"standardize_not_equals":true,"switch_continue_to_break":true,"trailing_comma_in_multiline":true,"trim_array_spaces":true,"unary_operator_spaces":true,"whitespace_after_comma_in_array":true,"blank_line_after_opening_tag":true,"compact_nullable_typehint":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_braces":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"return_type_declaration":true,"short_scalar_cast":true,"single_blank_line_before_namespace":true,"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"ternary_to_null_coalescing":true,"declare_strict_types":true},"hashes":{"Configuration\/Config.php":2550026830,"bin\/php-const-to-ts.php":1559571241,"Traits\/SelfValidationTrait.php":3506909554,"Contracts\/SelfValidationInterface.php":3970208274,"Tests\/AnnotationTest.php":3059284050,"Tests\/Fixtures\/AnnotationTestFixture.php":2377654812,"Annotations\/TSConstant.php":5720201,"Commands\/GenerateCommand.php":3413540455,"Services\/TransformService.php":1348946438}} \ No newline at end of file +{"php":"8.0.1","version":"3.0.1-DEV:3.0.x-dev#82824602da2f4f2d75b1e4a272b24984790acbb5","indent":" ","lineEnding":"\n","rules":{"blank_line_after_namespace":true,"braces":{"allow_single_line_anonymous_class_with_empty_body":true,"allow_single_line_closure":true},"constant_case":true,"elseif":true,"function_declaration":true,"indentation_type":true,"line_ending":true,"lowercase_keywords":true,"method_argument_space":{"on_multiline":"ensure_fully_multiline"},"no_break_comment":true,"no_closing_tag":true,"no_spaces_after_function_name":true,"no_spaces_inside_parenthesis":true,"no_trailing_whitespace":true,"no_trailing_whitespace_in_comment":true,"single_blank_line_at_eof":true,"single_class_element_per_statement":true,"single_import_per_statement":true,"single_line_after_imports":true,"switch_case_semicolon_to_colon":true,"switch_case_space":true,"visibility_required":true,"encoding":true,"full_opening_tag":true,"align_multiline_comment":true,"array_indentation":true,"combine_consecutive_issets":true,"combine_consecutive_unsets":true,"escape_implicit_backslashes":true,"explicit_indirect_variable":true,"explicit_string_variable":true,"heredoc_to_nowdoc":true,"method_chaining_indentation":true,"multiline_comment_opening_closing":true,"multiline_whitespace_before_semicolons":{"strategy":"new_line_for_chained_calls"},"no_extra_blank_lines":true,"no_null_property_initialization":true,"no_superfluous_elseif":true,"no_useless_else":true,"no_useless_return":true,"operator_linebreak":{"only_booleans":true},"ordered_class_elements":true,"phpdoc_order_by_value":true,"phpdoc_var_annotation_correct_order":true,"return_assignment":true,"simple_to_complex_string_variable":true,"single_line_comment_style":true,"array_syntax":{"syntax":"short"},"backtick_to_shell_exec":true,"binary_operator_spaces":true,"cast_spaces":{"space":"none"},"class_attributes_separation":{"elements":{"method":"one"}},"clean_namespace":true,"concat_space":{"spacing":"one"},"echo_tag_syntax":true,"fully_qualified_strict_types":true,"function_typehint_space":true,"general_phpdoc_tag_rename":{"replacements":{"inheritDocs":"inheritDoc"}},"include":true,"increment_style":true,"lambda_not_used_import":true,"linebreak_after_opening_tag":true,"magic_constant_casing":true,"magic_method_casing":true,"native_function_casing":true,"native_function_type_declaration_casing":true,"no_alias_language_construct_call":true,"no_alternative_syntax":true,"no_binary_string":true,"no_blank_lines_after_phpdoc":true,"no_empty_comment":true,"no_empty_phpdoc":true,"no_empty_statement":true,"no_leading_namespace_whitespace":true,"no_mixed_echo_print":true,"no_multiline_whitespace_around_double_arrow":true,"no_short_bool_cast":true,"no_singleline_whitespace_before_semicolons":true,"no_spaces_around_offset":true,"no_superfluous_phpdoc_tags":{"allow_mixed":true,"allow_unused_params":true},"no_trailing_comma_in_list_call":true,"no_trailing_comma_in_singleline_array":true,"no_unneeded_control_parentheses":{"statements":["break","clone","continue","echo_print","return","switch_case","yield","yield_from"]},"no_unneeded_curly_braces":{"namespaces":true},"no_unset_cast":true,"no_unused_imports":true,"no_whitespace_before_comma_in_array":true,"normalize_index_brace":true,"object_operator_without_whitespace":true,"ordered_imports":true,"php_unit_fqcn_annotation":true,"php_unit_method_casing":true,"phpdoc_align":{"align":"left"},"phpdoc_annotation_without_dot":true,"phpdoc_indent":true,"phpdoc_inline_tag_normalizer":true,"phpdoc_no_access":true,"phpdoc_no_alias_tag":true,"phpdoc_no_package":true,"phpdoc_no_useless_inheritdoc":true,"phpdoc_return_self_reference":true,"phpdoc_scalar":true,"phpdoc_single_line_var_spacing":true,"phpdoc_summary":true,"phpdoc_tag_type":{"tags":{"inheritDoc":"inline"}},"phpdoc_trim":true,"phpdoc_trim_consecutive_blank_line_separation":true,"phpdoc_types":true,"protected_to_private":true,"semicolon_after_instruction":true,"single_quote":true,"single_space_after_construct":true,"space_after_semicolon":{"remove_in_empty_for_expressions":true},"standardize_increment":true,"standardize_not_equals":true,"switch_continue_to_break":true,"trailing_comma_in_multiline":true,"trim_array_spaces":true,"unary_operator_spaces":true,"whitespace_after_comma_in_array":true,"blank_line_after_opening_tag":true,"compact_nullable_typehint":true,"declare_equal_normalize":true,"lowercase_cast":true,"lowercase_static_reference":true,"new_with_braces":true,"no_blank_lines_after_class_opening":true,"no_leading_import_slash":true,"no_whitespace_in_blank_line":true,"return_type_declaration":true,"short_scalar_cast":true,"single_blank_line_before_namespace":true,"single_trait_insert_per_statement":true,"ternary_operator_spaces":true,"ternary_to_null_coalescing":true,"declare_strict_types":true},"hashes":{"Configuration\/Config.php":2205079017,"bin\/php-const-to-ts.php":870726729,"Traits\/SelfValidationTrait.php":880251879,"Contracts\/SelfValidationInterface.php":1948507973,"Tests\/AnnotationTest.php":2470161135,"Tests\/Fixtures\/AnnotationTestFixture.php":3804916670,"Annotations\/TSConstant.php":5720201,"Commands\/GenerateCommand.php":3158194640,"Services\/TransformService.php":987319512,"Tests\/TransformServiceTest.php":4287418710,"Tests\/Traits\/MockTrait.php":2829530143,"Tests\/bootstrap.php":2089242976,"Annotations\/Constant.php":408747419,"Annotations\/Enum.php":2396974697,"Services\/ConstantAnnotationReader.php":3163157864,"Dto\/ConstantCollectResult.php":3912647602,"Traits\/Assert.php":3430989811,"Tests\/GeneratorTest.php":3802969765,"Services\/Generator.php":1983967189,"Attributes\/Constant.php":3724557448,"Attributes\/Enum.php":2052572478,"bin\/ts-const-enum.php":1259499759}} \ No newline at end of file diff --git a/Annotations/TSConstant.php b/Annotations/TSConstant.php deleted file mode 100644 index 766fa17..0000000 --- a/Annotations/TSConstant.php +++ /dev/null @@ -1,13 +0,0 @@ -alias; + } +} diff --git a/Attributes/Enum.php b/Attributes/Enum.php new file mode 100644 index 0000000..b43317f --- /dev/null +++ b/Attributes/Enum.php @@ -0,0 +1,19 @@ +alias; + } +} diff --git a/Commands/GenerateCommand.php b/Commands/GenerateCommand.php index a9795e9..8f2aa36 100644 --- a/Commands/GenerateCommand.php +++ b/Commands/GenerateCommand.php @@ -2,11 +2,11 @@ declare(strict_types=1); -namespace PhpConstToTsConst\Commands; +namespace Bolzer\TsConstEnum\Commands; +use Bolzer\TsConstEnum\Configuration\Config; +use Bolzer\TsConstEnum\Services\Generator; use Composer\Autoload\ClassLoader; -use PhpConstToTsConst\Configuration\Config; -use PhpConstToTsConst\Services\TransformService; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -24,163 +24,20 @@ protected function configure(): void { $this ->setName('generate') - ->setDescription('Transforms PHP constants to Typescript') + ->setDescription('Transforms annotated PHP constants to Typescript') ->setHelp('Transforms PHP constants to typescript and generates a file containing the typescript constants') ; } protected function execute(InputInterface $input, OutputInterface $output): int { - //$this->generateTypescriptCodeFromConstantsAndWriteToFiles($output); + $result = (new Generator($this->config, $this->classLoader))->generate(); - $service = new TransformService($this->config, $this->classLoader); - $result = $service->transformAnnotatedConstantsToTypescriptCode(); + $this->writeContentToConstantFile($result); - return 1; - } - - private function generateTypescriptCodeFromConstantsAndWriteToFiles(OutputInterface $output): void - { - $tempBuffer = []; - - foreach ($this->readRegularExpressionFromConfigFileToMatchRelevantClasses() as $className => $_classPath) { - try { - if (!\class_exists($className)) { - continue; - } - } catch (\Throwable $throwable) { - continue; - } - - $reflectionClass = new \ReflectionClass($className); - - /** @psalm-suppress TooManyArguments */ - $publicConstants = $reflectionClass->getReflectionConstants(\ReflectionClassConstant::IS_PUBLIC); - - if (!$publicConstants) { - continue; - } - - $scalarClassConstants = \array_filter($publicConstants, static fn (\ReflectionClassConstant $reflectionClassConstant) => \is_scalar($reflectionClassConstant->getValue())); - - foreach ($scalarClassConstants as $constantReflection) { - $tempBuffer = $this->generateConstantAndAddToBuffer($tempBuffer, $constantReflection); - } - - if (!$this->config->isShouldGenerateEnums()) { - continue; - } - - $scalarConstantMap = $this->createScalarConstantMap($scalarClassConstants); - - foreach (\array_filter($publicConstants, static fn (\ReflectionClassConstant $reflectionClassConstant) => \is_array($reflectionClassConstant->getValue())) as $constantReflection) { - $tempBuffer = $this->generateEnumAndAddToBuffer($tempBuffer, $constantReflection, $scalarConstantMap); - } - } - - $this->writeContentToConstantFile($tempBuffer); - $output->writeln(\sprintf('Dumbed %s constants to file.', \count($tempBuffer))); - } - - private function generateConstantAndAddToBuffer(array $buffer, \ReflectionClassConstant $constantReflection): array - { - $scalarConstantName = $this->createConstantNameFromNamespaceAndConstantName($constantReflection); - $buffer[$scalarConstantName] = $this->createTypescriptCodeForScalarConstant($scalarConstantName, $constantReflection->getValue()); - return $buffer; - } - - /** @param Array $scalarConstantMap */ - private function generateEnumAndAddToBuffer(array $overallBuffer, \ReflectionClassConstant $constantReflection, array $scalarConstantMap): array - { - $arrayConstantValue = $constantReflection->getValue(); - - if (!\is_array($arrayConstantValue)) { - return $overallBuffer; - } - - if ($this->isMultiDimensionalArray($arrayConstantValue)) { - return $overallBuffer; - } - - $valueInMapResult = \array_map(static fn (mixed $value) => \in_array($value, $scalarConstantMap, true), $arrayConstantValue); - - if (\in_array(false, $valueInMapResult, true)) { - return $overallBuffer; - } - - $buffer = []; - - foreach ($arrayConstantValue as $value) { - $key = \array_search($value, $scalarConstantMap, true); - - if ($key === false && \is_string($value)) { - $buffer[] = \sprintf("'%s' = %s", $value, \var_export($value, true)); - } elseif ($key === false) { - continue; - } else { - $buffer[] = \sprintf("'%s' = %s", $key, \var_export($value, true)); - } - } - - $enumConstantName = $this->createConstantNameFromNamespaceAndConstantName($constantReflection); - $overallBuffer[$enumConstantName] = \sprintf('export enum %s { %s }', $enumConstantName, \implode(', ', $buffer)); - return $overallBuffer; - } - - private function createTypescriptCodeForScalarConstant(string $typeScriptConstantName, mixed $constantValue): string - { - $typeScriptCode = \sprintf('export const %s: %s = %s;', $typeScriptConstantName, 'placeholder_constant_type', "'placeholder_constant_value'"); - $strReplaceArguments = ['placeholder_constant_type', "'placeholder_constant_value'"]; - - if (\is_string($constantValue)) { - return \str_replace($strReplaceArguments, ['string', ($this->resolveScalarTypeValue($constantValue))], $typeScriptCode); - } - - if (\is_numeric($constantValue)) { - return \str_replace($strReplaceArguments, ['number', $this->resolveScalarTypeValue($constantValue)], $typeScriptCode); - } + $output->writeln(\sprintf('Generated %s constants', \count($result))); - if (\is_null($constantValue)) { - return \str_replace($strReplaceArguments, 'null', $typeScriptCode); - } - - if (\is_bool($constantValue)) { - return \str_replace($strReplaceArguments, ['boolean', $this->resolveScalarTypeValue($constantValue)], $typeScriptCode); - } - - throw new \InvalidArgumentException(\sprintf('Could not match constant %s', $typeScriptConstantName)); - } - - private function resolveScalarTypeValue(mixed $scalarValue): string - { - if (\is_string($scalarValue)) { - return \var_export(addslashes(str_replace(["\n", "\t", "\r"], '', $scalarValue)), true); - } - - if (\is_numeric($scalarValue)) { - return var_export($scalarValue, true); - } - - if (\is_null($scalarValue)) { - return 'null'; - } - - if (\is_bool($scalarValue)) { - return var_export($scalarValue, true); - } - - throw new \InvalidArgumentException('Value did not meet the checks'); - } - - private function createConstantNameFromNamespaceAndConstantName(\ReflectionClassConstant $reflectionClassConstant): string - { - $declaringClassNamespace = $reflectionClassConstant->getDeclaringClass()->getNamespaceName(); - - if ($declaringClassNamespace === '') { - return $reflectionClassConstant->getName(); - } - - return \sprintf('%s_%s', \str_replace('\\', '__', $declaringClassNamespace), $reflectionClassConstant->getName()); + return 1; } private function writeContentToConstantFile(array $fileContent): void @@ -204,26 +61,6 @@ private function writeContentToConstantFile(array $fileContent): void file_put_contents($filePath, implode(\PHP_EOL, $fileContent)); } - /** - * @param \ReflectionClassConstant[] $reflectionClassConstants - * @return Array - */ - private function createScalarConstantMap(array $reflectionClassConstants): array - { - $buffer = []; - - foreach ($reflectionClassConstants as $reflectionClassConstant) { - $buffer[$reflectionClassConstant->getName()] = $reflectionClassConstant->getValue(); - } - - return $buffer; - } - - private function isMultiDimensionalArray(array $array): bool - { - return count(array_filter($array, static fn (mixed $value) => \is_array($value))) > 0; - } - private function getHeaderOfGeneratedFile(): array { return [ @@ -234,28 +71,4 @@ private function getHeaderOfGeneratedFile(): array '*/', ]; } - - /** - * @return Array - * @psalm-suppress MixedReturnStatement, MixedInferredReturnType - */ - private function readRegularExpressionFromConfigFileToMatchRelevantClasses(): array - { - if (!$this->config->getExcludeClassRegex()) { - return $this->classLoader->getClassMap(); - } - - $buffer = []; - foreach ($this->classLoader->getClassMap() as $class => $classPath) { - $regexResults = \array_map(static fn (string $regex) => (bool)preg_match($regex, $class), $this->config->getExcludeClassRegex()); - - if (\in_array(true, $regexResults, true)) { - continue; - } - - $buffer[$class] = $classPath; - } - - return $buffer; - } } diff --git a/Configuration/Config.php b/Configuration/Config.php index ab17edc..0cc73d0 100644 --- a/Configuration/Config.php +++ b/Configuration/Config.php @@ -2,10 +2,10 @@ declare(strict_types=1); -namespace PhpConstToTsConst\Configuration; +namespace Bolzer\TsConstEnum\Configuration; -use PhpConstToTsConst\Contracts\SelfValidationInterface; -use PhpConstToTsConst\Traits\SelfValidationTrait; +use Bolzer\TsConstEnum\Contracts\SelfValidationInterface; +use Bolzer\TsConstEnum\Traits\SelfValidationTrait; use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Mapping\ClassMetadata; @@ -13,12 +13,11 @@ class Config implements SelfValidationInterface { use SelfValidationTrait; - public const FILENAME = '.php-const-to-ts-config.php'; + public const FILENAME = '.ts-const-enum-config.php'; /** @Assert\NotBlank() */ private string $outputPath = ''; - private bool $shouldGenerateEnums = true; private bool $shouldAddGenerateHint = true; /** @var string[] */ @@ -42,17 +41,6 @@ public function setOutputPath(string $outputPath): self return $this; } - public function isShouldGenerateEnums(): bool - { - return $this->shouldGenerateEnums; - } - - public function setShouldGeneratePatchworkEnums(bool $generateEnums): self - { - $this->shouldGenerateEnums = $generateEnums; - return $this; - } - public function isShouldAddGenerateHint(): bool { return $this->shouldAddGenerateHint; @@ -63,15 +51,4 @@ public function setShouldAddGenerateHint(bool $shouldAddGenerateHint): Config $this->shouldAddGenerateHint = $shouldAddGenerateHint; return $this; } - - public function getExcludeClassRegex(): array - { - return $this->excludeClassRegex; - } - - public function setExcludeForFullyQualifiedNamespaceRegexes(array $excludeClassRegex): Config - { - $this->excludeClassRegex = $excludeClassRegex; - return $this; - } } diff --git a/Contracts/SelfValidationInterface.php b/Contracts/SelfValidationInterface.php index 0fd865c..7b34a2f 100644 --- a/Contracts/SelfValidationInterface.php +++ b/Contracts/SelfValidationInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpConstToTsConst\Contracts; +namespace Bolzer\TsConstEnum\Contracts; use Symfony\Component\Validator\ConstraintViolationListInterface; use Symfony\Component\Validator\Mapping\ClassMetadata; diff --git a/Dto/ConstantCollectResult.php b/Dto/ConstantCollectResult.php new file mode 100644 index 0000000..0dc783a --- /dev/null +++ b/Dto/ConstantCollectResult.php @@ -0,0 +1,29 @@ +reflectionClassConstant->getDeclaringClass()->getShortName() . '_' . $this->reflectionClassConstant->getName(); + } + + public function getReflectionClassConstant(): \ReflectionClassConstant + { + return $this->reflectionClassConstant; + } + + public function getAnnotationClass(): string + { + return $this->annotationClass; + } +} diff --git a/README.md b/README.md index 139a76f..cbe1e8a 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,17 @@ -# PHP-CONST-TO-TYPESCRIPT +# Ts-Const-Enum (PHP) ## Description -This tool will transform PHP Scalar-Constants - which are public - to Typescript Constants and generates Typescript Enums from PHP Makeshift-Enums. -This is useful if you want to reference a value in a condition. You can easily import the generated constants. This has +This tool will transform PHP Scalar-Constants - which are annotated with the provided attributes - to Typescript Constants and generates Typescript +Enums from annotated PHP one dimensional constant arrays. This may be useful if you want to reference a value in a condition within your Typescript Code. You can easily import the generated constants. This has many advantages over just stating the value. If the value of the constant changes, your code won't break easily. -If you change the name of a php constant the tyepscript code won't compile unless you changed all occurrences of the old +If you change the name of a php constant the typescript code won't compile unless you changed all occurrences of the old constant name. This is especially useful if you use alot of JS/TS-Frameworks and do conditionally rendering in regards to -values / properties. The Constants are prefixed with the namespace of where the constants resides to easily identify -then in your IDE and to ensure tree-shaking with webpack and co. +values / properties. The Constants are by default prefixed with the declaring class followed by __ and the +constant name. You may provide an alias on attribute-level. Keep in mind to have unique names / alias as the +constants will land in one file. ### Usecase @@ -35,31 +36,54 @@ if (reflection.type === TARGET_CLASS_CONSTANT) { 1. Require the dependency ```shell -composer require bolzer/php-const-to-typescript +composer require bolzer/ts-const-enum ``` 2. Create in your root (where the vendor folder is) dir a config file ```shell -touch .php-const-to-ts-config.php +touch .ts-const-enum-config.php ``` 3. Add the following content to the previously created file with the output path. ```php setOutputPath(__DIR__ . '/generated/constants.ts') ; ``` -4. Take a look at the config class for more config options -5. Run the binary of the tool +4. Take a look at the config class for more config option. +5. Annotate some constants and arrays in your php code with the provided attributes `Constant` and `Enum` + +**Example** + +```php + "value" + ]; +} +``` + +6. Run the binary of the tool ```shell -php ./vendor/bin/php-const-to-typescript.php generate +composer dump-autoload -o --quiet +php ./vendor/bin/ts-const-enum.php generate ``` -6. Start using the constants +7. Start using the constants ## What's a makeshift enum in PHP? Currently there're no enums in PHP. This will change with the release of PHP 8.2. diff --git a/Services/ConstantAnnotationReader.php b/Services/ConstantAnnotationReader.php deleted file mode 100644 index 927137f..0000000 --- a/Services/ConstantAnnotationReader.php +++ /dev/null @@ -1,46 +0,0 @@ -getDeclaringClass(); - $context = 'const ' . $class->getName() . '::' . $constant->getName(); - - $annotationReader = new AnnotationReader(); - - $annotationParserClosure = \Closure::bind(static function(AnnotationReader $annotationReader) { - return $annotationReader->parser; - }, null, $annotationReader); - - - $annotationParserImportClosure = \Closure::bind(static function(AnnotationReader $annotationReader, \ReflectionClass $reflectionClass) { - return $annotationReader->getImports($reflectionClass); - }, null, $annotationReader); - - $parser = $annotationParserClosure($annotationReader); - - $parser->setTarget(Target::TARGET_PROPERTY); - $parser->setImports($annotationParserImportClosure($annotationReader, $constant->getDeclaringClass())); - - return $parser->parse($constant->getDocComment(), $context); - } - - public function getConstantAnnotation(\ReflectionClassConstant $constant, string $annotationName): mixed - { - $annotations = $this->getConstantAnnotations($constant); - - foreach ($annotations as $annotation) { - if ($annotation instanceof $annotationName) { - return $annotation; - } - } - - return null; - } -} \ No newline at end of file diff --git a/Services/Generator.php b/Services/Generator.php new file mode 100644 index 0000000..7101297 --- /dev/null +++ b/Services/Generator.php @@ -0,0 +1,209 @@ +collectAnnotatedConstantsFromClassLoader(); + + $result = []; + + foreach ($constants as $constant) { + if ($constant->getAnnotationClass() === Constant::class) { + [$name, $code] = $this->generateCodeAndNameForAnnotatedConstant($constant->getReflectionClassConstant()); + + if (isset($result[$name])) { + throw new \InvalidArgumentException(\sprintf('Constant with the name: %s already exists. Occurred for constant %s', $name, $constant->getReflectionClassConstant()->getName())); + } + + $result[$name] = $code; + continue; + } + + if ($constant->getAnnotationClass() === Enum::class) { + [$name, $code] = $this->generateCodeAndNameForAnnotatedEnum($constant->getReflectionClassConstant()); + + if (isset($result[$name])) { + throw new \InvalidArgumentException(\sprintf('Constant with the name: %s already exists. Occurred for constant %s', $name, $constant->getReflectionClassConstant()->getName())); + } + + $result[$name] = $code; + continue; + } + + throw new \InvalidArgumentException( + \printf( + 'Result with a different annotation class encountered. Type: %s not supported for %s', + $constant->getAnnotationClass(), + $constant->getReflectionClassConstant()->getName() + ) + ); + } + + return $result; + } + + /** @return string[] */ + private function generateCodeAndNameForAnnotatedConstant(\ReflectionClassConstant $reflectionClassConstant): array + { + $this->assertScalarReflectionConstant($reflectionClassConstant); + + return [ + $constantName = $this->createConstantNameFromReflection($reflectionClassConstant), + $this->createTypescriptCodeForScalarConstant($constantName, $reflectionClassConstant->getValue()), + ]; + } + + /** @return string[] */ + private function generateCodeAndNameForAnnotatedEnum(\ReflectionClassConstant $reflectionClassConstant): array + { + $this->assertEnumReflectionConstant($reflectionClassConstant); + + $buffer = []; + + foreach ($reflectionClassConstant->getValue() as $key => $value) { + $key = \is_string($key) ? $key : $value; + $buffer[] = \sprintf("'%s' = %s", (string)$key, \var_export($value, true)); + } + + return [ + $constantName = $this->createConstantNameFromReflection($reflectionClassConstant), + \sprintf('export enum %s { %s }', $constantName, \implode(', ', $buffer)), + ]; + } + + private function createConstantNameFromReflection(\ReflectionClassConstant $reflectionClassConstant): string + { + $result = \array_merge( + $reflectionClassConstant->getAttributes(Constant::class), + $reflectionClassConstant->getAttributes(Enum::class), + ); + + if (\count($result) !== 1) { + throw new \InvalidArgumentException(\sprintf('Annotation of %s is invalid.', $reflectionClassConstant->getName())); + } + + /** @var \ReflectionAttribute $attribute */ + $attribute = $result[0]; + + $name = $attribute->getArguments()['alias'] ?? ''; + + if ($name !== '') { + return $name; + } + + return \sprintf( + '%s__%s', + $reflectionClassConstant->getDeclaringClass()->getShortName(), + $reflectionClassConstant->getName() + ); + } + + private function createTypescriptCodeForScalarConstant(string $typeScriptConstantName, mixed $constantValue): string + { + $typeScriptCode = \sprintf('export const %s: %s = %s;', $typeScriptConstantName, 'placeholder_constant_type', "'placeholder_constant_value'"); + $strReplaceArguments = ['placeholder_constant_type', "'placeholder_constant_value'"]; + + if (\is_string($constantValue)) { + return \str_replace($strReplaceArguments, ['string', ($this->resolveScalarTypeValue($constantValue))], $typeScriptCode); + } + + if (\is_numeric($constantValue)) { + return \str_replace($strReplaceArguments, ['number', $this->resolveScalarTypeValue($constantValue)], $typeScriptCode); + } + + if (\is_null($constantValue)) { + return \str_replace($strReplaceArguments, 'null', $typeScriptCode); + } + + if (\is_bool($constantValue)) { + return \str_replace($strReplaceArguments, ['boolean', $this->resolveScalarTypeValue($constantValue)], $typeScriptCode); + } + + throw new \InvalidArgumentException(\sprintf('Could not match constant %s', $typeScriptConstantName)); + } + + private function resolveScalarTypeValue(mixed $scalarValue): string + { + if (\is_string($scalarValue)) { + return \var_export(addslashes(str_replace(["\n", "\t", "\r"], '', $scalarValue)), true); + } + + if (\is_numeric($scalarValue)) { + return var_export($scalarValue, true); + } + + if (\is_null($scalarValue)) { + return 'null'; + } + + if (\is_bool($scalarValue)) { + return var_export($scalarValue, true); + } + + throw new \InvalidArgumentException('Value did not meet the checks'); + } + + /** @return ConstantCollectResult[] */ + private function collectAnnotatedConstantsFromClassLoader(): array + { + $buffer = []; + + foreach ($this->classLoader->getClassMap() as $className => $_classPath) { + try { + $reflectionClass = new \ReflectionClass($className); + } catch (\Throwable $throwable) { + continue; + } + + if ($reflectionClass->isAnonymous()) { + continue; + } + + $annotatedClassConstantReflections = array_filter(\array_map(static function (\ReflectionClassConstant $reflectionClassConstant) { + if ($reflectionClassConstant->getAttributes(Constant::class)) { + return new ConstantCollectResult( + $reflectionClassConstant, + Constant::class + ); + } + + if ($reflectionClassConstant->getAttributes(Enum::class)) { + return new ConstantCollectResult( + $reflectionClassConstant, + Enum::class + ); + } + + return null; + }, $reflectionClass->getReflectionConstants())); + + if (!$annotatedClassConstantReflections) { + continue; + } + + $buffer = \array_merge($buffer, $annotatedClassConstantReflections); + } + + return \array_unique($buffer); + } +} diff --git a/Services/TransformService.php b/Services/TransformService.php deleted file mode 100644 index 1f53be5..0000000 --- a/Services/TransformService.php +++ /dev/null @@ -1,76 +0,0 @@ -collectAnnotatedConstantsFromClassLoader(); - - } - - /** @return \ReflectionClassConstant[] */ - private function collectAnnotatedConstantsFromClassLoader(): array - { - $buffer = []; - - $annotationReader = new ConstantAnnotationReader(); - - foreach ($this->readRegularExpressionFromConfigFileToMatchRelevantClasses() as $className => $_classPath) { - $reflectionClass = new \ReflectionClass($className); - - $constantWithTsConstantAnnotation = array_filter(\array_map(static function (\ReflectionClassConstant $reflectionClassConstant) use ($annotationReader) { - if($annotationReader->getConstantAnnotation($reflectionClassConstant, TSConstant::class)){ - return $reflectionClassConstant; - } - - return null; - }, $reflectionClass->getReflectionConstants())); - - if(!$constantWithTsConstantAnnotation){ - continue; - } - - $buffer = \array_merge($buffer, $constantWithTsConstantAnnotation); - } - - return $buffer; - } - - /** - * @return Array - * @psalm-suppress MixedReturnStatement, MixedInferredReturnType - */ - private function readRegularExpressionFromConfigFileToMatchRelevantClasses(): array - { - if (!$this->config->getExcludeClassRegex()) { - return $this->classLoader->getClassMap(); - } - - $buffer = []; - foreach ($this->classLoader->getClassMap() as $class => $classPath) { - $regexResults = \array_map(static fn (string $regex) => (bool)preg_match($regex, $class), $this->config->getExcludeClassRegex()); - - if (\in_array(true, $regexResults, true)) { - continue; - } - - $buffer[$class] = $classPath; - } - - return $buffer; - } -} diff --git a/Tests/AnnotationTest.php b/Tests/AnnotationTest.php deleted file mode 100644 index 155f4ca..0000000 --- a/Tests/AnnotationTest.php +++ /dev/null @@ -1,34 +0,0 @@ -getReflectionConstants(); - - static::assertCount(3, $fixtureConstantsReflection); - - $annotationReader = new ConstantAnnotationReader(); - - $constantWithTsConstantAnnotation = array_filter(\array_map(static function (\ReflectionClassConstant $reflectionClassConstant) use ($annotationReader) { - if($annotationReader->getConstantAnnotation($reflectionClassConstant, TSConstant::class)){ - return $reflectionClassConstant; - } - - return null; - }, $fixtureConstantsReflection)); - - static::assertCount(2, $constantWithTsConstantAnnotation); - } -} diff --git a/Tests/Fixtures/AnnotationTestFixture.php b/Tests/Fixtures/AnnotationTestFixture.php index 4a61fb5..a5ccd35 100644 --- a/Tests/Fixtures/AnnotationTestFixture.php +++ b/Tests/Fixtures/AnnotationTestFixture.php @@ -2,20 +2,26 @@ declare(strict_types=1); -namespace PhpConstToTsConst\Tests\Fixtures; +namespace Bolzer\TsConstEnum\Tests\Fixtures; -use PhpConstToTsConst\Annotations\TSConstant; +use Bolzer\TsConstEnum\Attributes\Constant; +use Bolzer\TsConstEnum\Attributes\Enum; class AnnotationTestFixture { - /** @TSConstant() */ + #[Constant] public const TEST_VALUE = 'mixed'; - /** @TSConstant() */ + #[Enum] public const TEST_ARRAY = [ self::TEST_VALUE => 'another_value', ]; + #[Enum(alias: 'TestName')] + public const TEST_SINGULAR_ARRAY = [ + 'another_value', + ]; + public const TEST_ARRAY_WITHOUT_ANNOTATION = [ self::TEST_VALUE => 'another_value', ]; diff --git a/Tests/GeneratorTest.php b/Tests/GeneratorTest.php new file mode 100644 index 0000000..e3716f5 --- /dev/null +++ b/Tests/GeneratorTest.php @@ -0,0 +1,36 @@ +getMockedClassLoader()); + + $method = new \ReflectionMethod(Generator::class, 'collectAnnotatedConstantsFromClassLoader'); + $method->setAccessible(true); + + $result = $method->invoke($service); + + static::assertCount(3, $result); + } + + public function testGenerate(): void + { + $service = new Generator(new Config(), $this->getMockedClassLoader()); + $result = $service->generate(); + static::assertCount(3, $result); + + $test = 0; + } +} diff --git a/Tests/Traits/MockTrait.php b/Tests/Traits/MockTrait.php index c2e4297..6b4c157 100644 --- a/Tests/Traits/MockTrait.php +++ b/Tests/Traits/MockTrait.php @@ -1,15 +1,11 @@ prophesize(ClassLoader::class); $classLoaderMock->getClassMap()->willReturn([ - $fixtureReflection->getName() => $fixtureReflection->getFileName() + $fixtureReflection->getName() => $fixtureReflection->getFileName(), ]); return $classLoaderMock->reveal(); } -} \ No newline at end of file +} diff --git a/Tests/TransformServiceTest.php b/Tests/TransformServiceTest.php deleted file mode 100644 index 67a0aac..0000000 --- a/Tests/TransformServiceTest.php +++ /dev/null @@ -1,28 +0,0 @@ -getMockedClassLoader()); - - $method = new \ReflectionMethod(TransformService::class, "collectAnnotatedConstantsFromClassLoader"); - $method->setAccessible(true); - - $result = $method->invoke($service); - - static::assertCount(2, $result); - } -} \ No newline at end of file diff --git a/Tests/bootstrap.php b/Tests/bootstrap.php index de14676..06fdcb4 100644 --- a/Tests/bootstrap.php +++ b/Tests/bootstrap.php @@ -1,3 +1,5 @@ getValue())) { + throw new \InvalidArgumentException(\sprintf('Constant %s is not scalar. The @Constant annotation is not supported', $reflectionClassConstant->getName())); + } + } + + public function assertEnumReflectionConstant(\ReflectionClassConstant $reflectionClassConstant): void + { + $value = $reflectionClassConstant->getValue(); + + if (!\is_array($value)) { + throw new \InvalidArgumentException(\sprintf('Constant %s is not an array. The @Enum annotation is not supported', $reflectionClassConstant->getName())); + } + + if (count(array_filter($value, static fn (mixed $value) => \is_array($value))) > 0) { + throw new \InvalidArgumentException(\sprintf('Constant %s is multi dimensional. The @Enum annotation is not supported', $reflectionClassConstant->getName())); + } + } +} diff --git a/Traits/SelfValidationTrait.php b/Traits/SelfValidationTrait.php index 342a29a..a0cde8e 100644 --- a/Traits/SelfValidationTrait.php +++ b/Traits/SelfValidationTrait.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace PhpConstToTsConst\Traits; +namespace Bolzer\TsConstEnum\Traits; use Symfony\Component\Validator\ConstraintViolationListInterface; use Symfony\Component\Validator\Validation; diff --git a/bin/php-const-to-ts.php b/bin/ts-const-enum.php similarity index 93% rename from bin/php-const-to-ts.php rename to bin/ts-const-enum.php index cfa79e6..d79ab40 100755 --- a/bin/php-const-to-ts.php +++ b/bin/ts-const-enum.php @@ -1,9 +1,9 @@ #!/usr/bin/env php setName('PHP-CONST-TO-TYPESCRIPT'); +$consoleApplication->setName('TsConstEnum'); $consoleApplication->add(new GenerateCommand($config, $classLoader)); $consoleApplication->run(); diff --git a/composer.json b/composer.json index 02ae324..052ab2b 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { - "name": "bolzer/php-const-to-typescript", - "bin": ["bin/php-const-to-ts.php"], + "name": "bolzer/ts-const-enum", + "bin": ["bin/ts-const-enum.php"], "description": "A command line tool to generate typescript constants from your php constants", "minimum-stability": "dev", "license": "MIT", @@ -12,12 +12,11 @@ ], "autoload": { "psr-4": { - "PhpConstToTsConst\\": "" + "Bolzer\\TsConstEnum\\": "" } }, "require": { "php": ">=8.0", - "doctrine/annotations": "1.13.x-dev", "symfony/console": "^5.3", "symfony/validator": "^5.3" } diff --git a/composer.lock b/composer.lock index 68f8fd1..86711a3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,209 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e25e8c5a6facffa24b6735c9f8f70696", + "content-hash": "995ee71335b2207a1beb0f1658731b1a", "packages": [ - { - "name": "doctrine/annotations", - "version": "1.13.x-dev", - "source": { - "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "f96567ba9e3848f59256b473d730e5ba39d81bfe" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/f96567ba9e3848f59256b473d730e5ba39d81bfe", - "reference": "f96567ba9e3848f59256b473d730e5ba39d81bfe", - "shasum": "" - }, - "require": { - "doctrine/lexer": "1.*", - "ext-tokenizer": "*", - "php": "^7.1 || ^8.0", - "psr/cache": "^1 || ^2 || ^3" - }, - "require-dev": { - "doctrine/cache": "^1.11 || ^2.0", - "doctrine/coding-standard": "^6.0 || ^8.1", - "phpstan/phpstan": "^0.12.20", - "phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5", - "symfony/cache": "^4.4 || ^5.2" - }, - "default-branch": true, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "https://www.doctrine-project.org/projects/annotations.html", - "keywords": [ - "annotations", - "docblock", - "parser" - ], - "support": { - "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/1.13.x" - }, - "time": "2021-07-17T08:59:21+00:00" - }, - { - "name": "doctrine/lexer", - "version": "1.3.x-dev", - "source": { - "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "59bfb3b9be04237be4cd1afea9bbb58794c25ce8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/59bfb3b9be04237be4cd1afea9bbb58794c25ce8", - "reference": "59bfb3b9be04237be4cd1afea9bbb58794c25ce8", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^8.0", - "phpstan/phpstan": "^0.12", - "phpunit/phpunit": "^8.2 || ^9.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "https://www.doctrine-project.org/projects/lexer.html", - "keywords": [ - "annotations", - "docblock", - "lexer", - "parser", - "php" - ], - "support": { - "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/1.3.x" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", - "type": "tidelift" - } - ], - "time": "2021-01-20T07:15:06+00:00" - }, - { - "name": "psr/cache", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "0a7c67d0d1c8167b342eb74339d6f961663826ce" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/0a7c67d0d1c8167b342eb74339d6f961663826ce", - "reference": "0a7c67d0d1c8167b342eb74339d6f961663826ce", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "suggest": { - "fig/cache-util": "Provides some useful PSR-6 utilities" - }, - "default-branch": true, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for caching libraries", - "keywords": [ - "cache", - "psr", - "psr-6" - ], - "support": { - "source": "https://github.com/php-fig/cache/tree/master" - }, - "time": "2021-02-24T03:25:37+00:00" - }, { "name": "psr/container", "version": "1.1.x-dev", @@ -1695,6 +1494,78 @@ }, "time": "2019-12-04T15:06:13+00:00" }, + { + "name": "doctrine/annotations", + "version": "1.14.x-dev", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "5e802cc8a05c8679b01ef60423642702bdef6e6b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/5e802cc8a05c8679b01ef60423642702bdef6e6b", + "reference": "5e802cc8a05c8679b01ef60423642702bdef6e6b", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "ext-tokenizer": "*", + "php": "^7.1 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" + }, + "require-dev": { + "doctrine/cache": "^1.11 || ^2.0", + "doctrine/coding-standard": "^6.0 || ^8.1", + "phpstan/phpstan": "^0.12.20", + "phpunit/phpunit": "^7.5 || ^8.0 || ^9.1.5", + "symfony/cache": "^4.4 || ^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "support": { + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/1.14.x" + }, + "time": "2021-05-16T18:15:24+00:00" + }, { "name": "doctrine/instantiator", "version": "1.5.x-dev", @@ -1764,6 +1635,81 @@ ], "time": "2020-11-10T19:05:51+00:00" }, + { + "name": "doctrine/lexer", + "version": "1.3.x-dev", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "59bfb3b9be04237be4cd1afea9bbb58794c25ce8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/59bfb3b9be04237be4cd1afea9bbb58794c25ce8", + "reference": "59bfb3b9be04237be4cd1afea9bbb58794c25ce8", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^8.0", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^8.2 || ^9.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/1.3.x" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2021-01-20T07:15:06+00:00" + }, { "name": "felixfbecker/advanced-json-rpc", "version": "v3.2.1", @@ -2986,6 +2932,59 @@ ], "time": "2021-07-20T11:51:25+00:00" }, + { + "name": "psr/cache", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "0a7c67d0d1c8167b342eb74339d6f961663826ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/0a7c67d0d1c8167b342eb74339d6f961663826ce", + "reference": "0a7c67d0d1c8167b342eb74339d6f961663826ce", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "suggest": { + "fig/cache-util": "Provides some useful PSR-6 utilities" + }, + "default-branch": true, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/master" + }, + "time": "2021-02-24T03:25:37+00:00" + }, { "name": "psr/event-dispatcher", "version": "dev-master", @@ -5259,7 +5258,6 @@ "aliases": [], "minimum-stability": "dev", "stability-flags": { - "doctrine/annotations": 20, "roave/security-advisories": 20 }, "prefer-stable": false, diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..064f77c --- /dev/null +++ b/psalm.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + +