From 131e2c4a44185e502bde36957c92e40fa0804df3 Mon Sep 17 00:00:00 2001 From: Alain Schlesser Date: Fri, 21 May 2021 15:29:05 +0100 Subject: [PATCH] Refactor doc section --- bin/src/Validator/SpecGenerator.php | 10 +- bin/src/Validator/SpecGenerator/Dumper.php | 8 + .../Validator/SpecGenerator/FileManager.php | 9 +- .../SpecGenerator/Section/CssRulesets.php | 8 - .../Validator/SpecGenerator/Section/Doc.php | 12 -- .../SpecGenerator/Section/DocRulesets.php | 178 ++++++++++++++++++ .../SpecGenerator/Template/CssRuleset.php | 4 +- .../SpecGenerator/Template/DocRuleset.php | 88 +++++++++ .../SpecGenerator/Template/DocRulesets.php | 110 +++++++++++ .../Validator/SpecGenerator/Template/Tag.php | 2 - src/Exception/InvalidDocRulesetName.php | 28 +++ src/Validator/Spec.php | 26 +-- src/Validator/Spec/CssRuleset.php | 2 - src/Validator/Spec/DocRuleset.php | 90 +++++++++ src/Validator/Spec/DocRuleset/Amp4email.php | 35 ++++ src/Validator/Spec/Section/CssRulesets.php | 1 - src/Validator/Spec/Section/Doc.php | 30 --- src/Validator/Spec/Section/DocRulesets.php | 146 ++++++++++++++ src/Validator/Spec/Tag.php | 2 - tests/Validator/Spec/DocRulesetTest.php | 55 ++++++ .../Spec/Section/DocRulesetsTest.php | 79 ++++++++ tests/Validator/SpecTest.php | 6 +- .../src/ValidatorFixtures/DummyCssRuleset.php | 1 - .../src/ValidatorFixtures/DummyDocRuleset.php | 18 ++ 24 files changed, 866 insertions(+), 82 deletions(-) delete mode 100644 bin/src/Validator/SpecGenerator/Section/Doc.php create mode 100644 bin/src/Validator/SpecGenerator/Section/DocRulesets.php create mode 100644 bin/src/Validator/SpecGenerator/Template/DocRuleset.php create mode 100644 bin/src/Validator/SpecGenerator/Template/DocRulesets.php create mode 100644 src/Exception/InvalidDocRulesetName.php create mode 100644 src/Validator/Spec/DocRuleset.php create mode 100644 src/Validator/Spec/DocRuleset/Amp4email.php delete mode 100644 src/Validator/Spec/Section/Doc.php create mode 100644 src/Validator/Spec/Section/DocRulesets.php create mode 100644 tests/Validator/Spec/DocRulesetTest.php create mode 100644 tests/Validator/Spec/Section/DocRulesetsTest.php create mode 100644 tests/src/ValidatorFixtures/DummyDocRuleset.php diff --git a/bin/src/Validator/SpecGenerator.php b/bin/src/Validator/SpecGenerator.php index 24c9d5b05..854d41c70 100644 --- a/bin/src/Validator/SpecGenerator.php +++ b/bin/src/Validator/SpecGenerator.php @@ -51,6 +51,7 @@ public function generate($jsonSpec, $rootNamespace, $destination) $this->generateEntityClass('DeclarationList', $fileManager); $this->generateEntityClass('DescendantTagList', $fileManager); $this->generateEntityClass('CssRuleset', $fileManager); + $this->generateEntityClass('DocRuleset', $fileManager); $this->generateEntityClass('Tag', $fileManager); $this->generateEntityClass('TagWithExtensionSpec', $fileManager, 'interface'); $this->generateEntityClass('ExtensionSpec', $fileManager, 'trait'); @@ -218,8 +219,11 @@ private function generateErrorCodeInterface($jsonSpec, FileManager $fileManager) */ private function adaptJsonSpec($jsonSpec) { - $jsonSpec['cssRulesets'] = $jsonSpec['cssRulesets']; - unset($jsonSpec['cssRulesets']); + $jsonSpec['cssRulesets'] = $jsonSpec['css']; + unset($jsonSpec['css']); + + $jsonSpec['docRulesets'] = $jsonSpec['doc']; + unset($jsonSpec['doc']); $jsonSpec['attributeLists'] = $jsonSpec['attrLists']; unset($jsonSpec['attrLists']); @@ -272,7 +276,7 @@ private function collectSpecRuleKeys($jsonSpec) } break; case 'cssRulesets': - case 'doc': + case 'docRulesets': case 'errors': foreach ($sectionData as $ruleset) { foreach (array_keys($ruleset) as $specRuleKey) { diff --git a/bin/src/Validator/SpecGenerator/Dumper.php b/bin/src/Validator/SpecGenerator/Dumper.php index f686bfe19..b4ff69b7b 100644 --- a/bin/src/Validator/SpecGenerator/Dumper.php +++ b/bin/src/Validator/SpecGenerator/Dumper.php @@ -231,6 +231,14 @@ public function filterValueStrings($value) || strpos($value, 'AttributeList\\') === 0 || + strpos($value, 'CssRuleset::') === 0 + || + strpos($value, 'CssRuleset\\') === 0 + || + strpos($value, 'DocRuleset::') === 0 + || + strpos($value, 'DocRuleset\\') === 0 + || strpos($value, 'DeclarationList::') === 0 || strpos($value, 'DeclarationList\\') === 0 diff --git a/bin/src/Validator/SpecGenerator/FileManager.php b/bin/src/Validator/SpecGenerator/FileManager.php index 7e1e1e2fd..1716eda9f 100644 --- a/bin/src/Validator/SpecGenerator/FileManager.php +++ b/bin/src/Validator/SpecGenerator/FileManager.php @@ -62,6 +62,7 @@ public function ensureDirectoriesExist() "{$this->destination}/Spec", "{$this->destination}/Spec/AttributeList", "{$this->destination}/Spec/CssRuleset", + "{$this->destination}/Spec/DocRuleset", "{$this->destination}/Spec/DeclarationList", "{$this->destination}/Spec/DescendantTagList", "{$this->destination}/Spec/Section", @@ -300,6 +301,10 @@ private function getFullyQualifiedName($class) return "AmpProject\\Validator\\Spec\\CssSpecRule"; } + if (strpos($class, 'DocSpecRule\\') === 0) { + return "AmpProject\\Validator\\Spec\\DocSpecRule"; + } + if (strpos($class, 'AttributeList\\') === 0) { return "AmpProject\\Validator\\Spec\\AttributeList"; } @@ -315,7 +320,7 @@ private function getFullyQualifiedName($class) if ( in_array( $class, - ['AttributeList', 'CssRuleset', 'DeclarationList', 'DescendantTagList', 'SpecRule'], + ['AttributeList', 'CssRuleset', 'DocRuleset', 'DeclarationList', 'DescendantTagList', 'SpecRule'], true ) ) { @@ -325,7 +330,7 @@ private function getFullyQualifiedName($class) if ( in_array( $class, - ['AttributeLists', 'CssRulesets', 'DeclarationLists', 'DescendantTagLists', 'Tags'], + ['AttributeLists', 'CssRulesets', 'DocRulesets', 'DeclarationLists', 'DescendantTagLists', 'Tags'], true ) ) { diff --git a/bin/src/Validator/SpecGenerator/Section/CssRulesets.php b/bin/src/Validator/SpecGenerator/Section/CssRulesets.php index 8ea3b7cce..bdd0ed53e 100644 --- a/bin/src/Validator/SpecGenerator/Section/CssRulesets.php +++ b/bin/src/Validator/SpecGenerator/Section/CssRulesets.php @@ -18,13 +18,6 @@ final class CssRulesets implements Section use ClassNames; use ConstantNames; - /** - * Associative array of CSS rulesets and their attributes. - * - * @var array - */ - private $css = []; - /** * Dumper instance to use. * @@ -54,7 +47,6 @@ public function process(FileManager $fileManager, $spec, PhpNamespace $namespace $cssRulesets = []; $byFormat = []; - $namespace->addUse("LogicException"); $namespace->addUse("{$fileManager->getRootNamespace()}\\Spec\\IterableSection"); $namespace->addUse("{$fileManager->getRootNamespace()}\\Spec\\Iteration"); $namespace->addUse("{$fileManager->getRootNamespace()}\\Spec\\CssRuleset"); diff --git a/bin/src/Validator/SpecGenerator/Section/Doc.php b/bin/src/Validator/SpecGenerator/Section/Doc.php deleted file mode 100644 index 0798ad706..000000000 --- a/bin/src/Validator/SpecGenerator/Section/Doc.php +++ /dev/null @@ -1,12 +0,0 @@ -dumper = new Dumper(); + } + + /** + * Process a section. + * + * @param FileManager $fileManager FileManager instance to use. + * @param array $spec Associative array of spec data that was decoded from the JSON file. + * @param PhpNamespace $namespace Namespace object of the section. + * @param ClassType $class Class object of the section. + * @return void + */ + public function process(FileManager $fileManager, $spec, PhpNamespace $namespace, ClassType $class) + { + $docRulesets = []; + $byFormat = []; + + $namespace->addUse("{$fileManager->getRootNamespace()}\\Spec\\IterableSection"); + $namespace->addUse("{$fileManager->getRootNamespace()}\\Spec\\Iteration"); + $namespace->addUse("{$fileManager->getRootNamespace()}\\Spec\\DocRuleset"); + + $docRulesetsTemplateClass = ClassType::withBodiesFrom(Template\DocRulesets::class); + foreach ($docRulesetsTemplateClass->getMethods() as $method) { + $class->addMember($method); + } + + $class->addImplement("{$fileManager->getRootNamespace()}\\Spec\\IterableSection"); + $class->addTrait( + "{$fileManager->getRootNamespace()}\\Spec\\Iteration", + ['Iteration::current as parentCurrent'] + ); + + $class->addProperty('docRulesetsCache') + ->setPrivate() + ->addComment("Cache of instantiated DocRuleset objects.\n\n@var array") + ->setValue([]); + + $class->addProperty('iterationArray') + ->setPrivate() + ->addComment("Array used for storing the iteration index in.\n\n@var array|null"); + + foreach ($spec as $attributes) { + $docRulesetId = $this->getNameForRuleset($attributes); + $docRulesets[$docRulesetId] = $attributes; + } + + $docRulesetIds = array_keys($docRulesets); + natcasesort($docRulesetIds); + + $rulesets = []; + foreach ($docRulesetIds as $docRulesetId) { + $docRulesetIdString = "DocRuleset\\{$this->getClassNameFromId($docRulesetId)}::ID"; + + $className = $this->generateDocRulesetSpecificClass( + $docRulesetId, + $docRulesets[$docRulesetId], + $fileManager + ); + + $rulesets["DocRuleset\\{$className}::ID"] = "DocRuleset\\{$className}::class"; + + if (array_key_exists('htmlFormat', $docRulesets[$docRulesetId])) { + $formats = $docRulesets[$docRulesetId]['htmlFormat']; + foreach ($formats as $format) { + $format = $this->getFormatConstant($this->getConstantName($format)); + if (!array_key_exists($format, $byFormat)) { + $byFormat[$format] = []; + } + $byFormat[$format][] = $docRulesetIdString; + } + } + } + + $class->addConstant('DOC_RULESETS', $rulesets) + ->addComment("Mapping of document ruleset ID to document ruleset implementation.\n\n@var array"); + + $class->addConstant('BY_FORMAT', $byFormat) + ->addComment( + "Mapping of AMP format to array of document ruleset IDs.\n\n" + . "This is used to optimize querying by AMP format.\n\n" + . "@var array>" + ); + } + + /** + * Get the name for a given ruleset. + * + * @param array $ruleSet Rule set to get the name for. + * @return string Name to use for the rule set. + */ + private function getNameForRuleSet($ruleSet) + { + static $index = 1; + + if (!array_key_exists('htmlFormat', $ruleSet) || count($ruleSet['htmlFormat']) === 0) { + $name = "ruleset-{$index}"; + $index++; + + return $name; + } + + $name = $ruleSet['htmlFormat'][0]; + + if (array_key_exists('enabledBy', $ruleSet) && count($ruleSet['enabledBy']) > 0) { + $name .= " ({$ruleSet['enabledBy'][0]})"; + } + + if (array_key_exists('disabledBy', $ruleSet) && count($ruleSet['disabledBy']) > 0) { + $name .= " (no-{$ruleSet['disabledBy'][0]})"; + } + + return $name; + } + + /** + * Generate the document ruleset-specific class file. + * + * @param string $ruleset ID of the document ruleset to generate the class for. + * @param array $jsonSpec Array of spec data for the document ruleset. + * @param FileManager $fileManager File manager instance to use. + */ + private function generateDocRulesetSpecificClass($ruleset, $jsonSpec, FileManager $fileManager) + { + list($file, $namespace) = $fileManager->createNewNamespacedFile('Spec\\DocRuleset'); + + $className = $this->getClassNameFromId($ruleset); + + $namespace->addUse("{$fileManager->getRootNamespace()}\\Spec\\SpecRule"); + $namespace->addUse("{$fileManager->getRootNamespace()}\\Spec\\DocRuleset"); + + /** @var ClassType $class */ + $class = $namespace->addClass($className) + ->setFinal() + ->addExtend('AmpProject\Validator\Spec\DocRuleset'); + + $class->addConstant('ID', $ruleset) + ->addComment("ID of the ruleset.\n\n@var string"); + + $class->addConstant('SPEC', $jsonSpec) + ->addComment("Array of spec rules.\n\n@var array"); + + $fileManager->saveFile($file, "Spec/DocRuleset/{$className}.php"); + + return $className; + } +} diff --git a/bin/src/Validator/SpecGenerator/Template/CssRuleset.php b/bin/src/Validator/SpecGenerator/Template/CssRuleset.php index a76ed4026..26593777b 100644 --- a/bin/src/Validator/SpecGenerator/Template/CssRuleset.php +++ b/bin/src/Validator/SpecGenerator/Template/CssRuleset.php @@ -8,8 +8,6 @@ /** * Class CssRuleset. * - * @package AmpProject\Tooling\Validator\SpecGenerator\Template - * * @property-read bool $allowAllDeclarationInStyle * @property-read bool $allowImportant * @property-read bool $expandVendorPrefixes @@ -32,7 +30,7 @@ class CssRuleset * * @var string */ - const ID = '[cssRulesets ruleset base class]'; + const ID = '[css ruleset base class]'; /** * Spec data of the CSS ruleset. diff --git a/bin/src/Validator/SpecGenerator/Template/DocRuleset.php b/bin/src/Validator/SpecGenerator/Template/DocRuleset.php new file mode 100644 index 000000000..a2797e9bb --- /dev/null +++ b/bin/src/Validator/SpecGenerator/Template/DocRuleset.php @@ -0,0 +1,88 @@ + $htmlFormat + */ +class DocRuleset +{ + + /** + * ID of the document ruleset. + * + * This needs to be overridden in the extending class. + * + * @var string + */ + const ID = '[document ruleset base class]'; + + /** + * Spec data of the document ruleset. + * + * @var array + */ + const SPEC = []; + + /** + * Get the ID of the document ruleset. + * + * @return string ID of the document ruleset. + */ + public function getId() + { + return static::ID; + } + + + /** + * Check whether a given spec rule is present. + * + * @param string $docRulesetName Name of the spec rule to check for. + * @return bool Whether the given spec rule is contained in the spec. + */ + public function has($docRulesetName) + { + return array_key_exists($docRulesetName, static::SPEC); + } + + /** + * Get a specific spec rule. + * + * @param string $docRulesetName Name of the spec rule to get. + * @return array Spec rule data that was requested. + */ + public function get($docRulesetName) + { + if (!$this->has($docRulesetName)) { + throw InvalidSpecRuleName::forSpecRuleName($docRulesetName); + } + + return static::SPEC[$docRulesetName]; + } + + /** + * Magic getter to return the spec rules. + * + * @param string $docRulesetName Name of the spec rule to return. + * @return mixed Value of the spec rule. + */ + public function __get($docRulesetName) + { + switch ($docRulesetName) { + case SpecRule::HTML_FORMAT: + return array_key_exists($docRulesetName, static::SPEC) ? static::SPEC[$docRulesetName] : []; + default: + if (!array_key_exists($docRulesetName, static::SPEC)) { + throw InvalidSpecRuleName::forSpecRuleName($docRulesetName); + } + + return static::SPEC[$docRulesetName]; + } + } +} diff --git a/bin/src/Validator/SpecGenerator/Template/DocRulesets.php b/bin/src/Validator/SpecGenerator/Template/DocRulesets.php new file mode 100644 index 000000000..bd4601bf0 --- /dev/null +++ b/bin/src/Validator/SpecGenerator/Template/DocRulesets.php @@ -0,0 +1,110 @@ +docRulesetsCache)) { + return $this->docRulesetsCache[$docRulesetId]; + } + + $docRulesetClassName = self::DOC_RULESETS[$docRulesetId]; + + /** @var DocRuleset $docRuleset */ + $docRuleset = new $docRulesetClassName(); + + $this->docRulesetsCache[$docRulesetId] = $docRuleset; + + return $docRuleset; + } + + /** + * Get a collection of document rulesets for a given AMP HTML format name. + * + * @param string $format AMP HTML format to get the document rulesets for. + * @return array Array of document rulesets matching the requested AMP HTML format. + * @throws InvalidFormat If an invalid AMP HTML format is requested. + */ + public function byFormat($format) + { + if (!array_key_exists($format, self::BY_FORMAT)) { + throw InvalidFormat::forFormat($format); + } + + $docRulesetIds = self::BY_FORMAT[$format]; + if (!is_array($docRulesetIds)) { + $docRulesetIds = [$docRulesetIds]; + } + + $docRulesets = []; + foreach ($docRulesetIds as $docRulesetId) { + $docRulesets[] = $this->get($docRulesetId); + } + + return $docRulesets; + } + + /** + * Get the list of available keys. + * + * @return array Array of available keys. + */ + public function getAvailableKeys() + { + return array_keys(self::DOC_RULESETS); + } + + /** + * Find the instantiated object for the current key. + * + * This should use its own caching mechanism as needed. + * + * Ideally, current() should be overridden as well to provide the correct object type-hint. + * + * @param string $key Key to retrieve the instantiated object for. + * @return DocRuleset Instantiated object for the current key. + */ + public function findByKey($key) + { + return $this->get($key); + } + + /** + * Return the current iterable object. + * + * @return DocRuleset DocRuleset object. + */ + public function current() + { + return $this->parentCurrent(); + } +} diff --git a/bin/src/Validator/SpecGenerator/Template/Tag.php b/bin/src/Validator/SpecGenerator/Template/Tag.php index c211b63b5..e11100b4c 100644 --- a/bin/src/Validator/SpecGenerator/Template/Tag.php +++ b/bin/src/Validator/SpecGenerator/Template/Tag.php @@ -8,8 +8,6 @@ /** * Class Tag. * - * @package AmpProject\Tooling\Validator\SpecGenerator\Template - * * @property-read array $alsoRequiresTagWarning * @property-read array $ampLayout * @property-read array $attrLists diff --git a/src/Exception/InvalidDocRulesetName.php b/src/Exception/InvalidDocRulesetName.php new file mode 100644 index 000000000..3630aca6c --- /dev/null +++ b/src/Exception/InvalidDocRulesetName.php @@ -0,0 +1,28 @@ +doc === null) { - $this->doc = new Spec\Section\Doc(); + if ($this->cssRulesets === null) { + $this->cssRulesets = new Spec\Section\CssRulesets(); } - return $this->doc; + return $this->cssRulesets; } /** - * @return Spec\Section\CssRulesets + * @return Spec\Section\DocRulesets */ - public function cssRulesets() + public function docRulesets() { - if ($this->cssRulesets === null) { - $this->cssRulesets = new Spec\Section\CssRulesets(); + if ($this->docRulesets === null) { + $this->docRulesets = new Spec\Section\DocRulesets(); } - return $this->cssRulesets; + return $this->docRulesets; } /** diff --git a/src/Validator/Spec/CssRuleset.php b/src/Validator/Spec/CssRuleset.php index 34ec8c5bf..8d481fff8 100644 --- a/src/Validator/Spec/CssRuleset.php +++ b/src/Validator/Spec/CssRuleset.php @@ -12,8 +12,6 @@ /** * Class CssRuleset. * - * @package AmpProject\Tooling\Validator\SpecGenerator\Template - * * @property-read bool $allowAllDeclarationInStyle * @property-read bool $allowImportant * @property-read bool $expandVendorPrefixes diff --git a/src/Validator/Spec/DocRuleset.php b/src/Validator/Spec/DocRuleset.php new file mode 100644 index 000000000..c30733f39 --- /dev/null +++ b/src/Validator/Spec/DocRuleset.php @@ -0,0 +1,90 @@ + $htmlFormat + */ +class DocRuleset +{ + /** + * ID of the document ruleset. + * + * This needs to be overridden in the extending class. + * + * @var string + */ + const ID = '[document ruleset base class]'; + + /** + * Spec data of the document ruleset. + * + * @var array + */ + const SPEC = []; + + /** + * Get the ID of the document ruleset. + * + * @return string ID of the document ruleset. + */ + public function getId() + { + return static::ID; + } + + /** + * Check whether a given spec rule is present. + * + * @param string $docRulesetName Name of the spec rule to check for. + * @return bool Whether the given spec rule is contained in the spec. + */ + public function has($docRulesetName) + { + return array_key_exists($docRulesetName, static::SPEC); + } + + /** + * Get a specific spec rule. + * + * @param string $docRulesetName Name of the spec rule to get. + * @return array Spec rule data that was requested. + */ + public function get($docRulesetName) + { + if (!$this->has($docRulesetName)) { + throw InvalidSpecRuleName::forSpecRuleName($docRulesetName); + } + + return static::SPEC[$docRulesetName]; + } + + /** + * Magic getter to return the spec rules. + * + * @param string $docRulesetName Name of the spec rule to return. + * @return mixed Value of the spec rule. + */ + public function __get($docRulesetName) + { + switch ($docRulesetName) { + case SpecRule::HTML_FORMAT: + return array_key_exists($docRulesetName, static::SPEC) ? static::SPEC[$docRulesetName] : []; + default: + if (!array_key_exists($docRulesetName, static::SPEC)) { + throw InvalidSpecRuleName::forSpecRuleName($docRulesetName); + } + + return static::SPEC[$docRulesetName]; + } + } +} diff --git a/src/Validator/Spec/DocRuleset/Amp4email.php b/src/Validator/Spec/DocRuleset/Amp4email.php new file mode 100644 index 000000000..c7745db22 --- /dev/null +++ b/src/Validator/Spec/DocRuleset/Amp4email.php @@ -0,0 +1,35 @@ + [ + Format::AMP4EMAIL, + ], + SpecRule::MAX_BYTES => 200000, + SpecRule::MAX_BYTES_SPEC_URL => 'https://amp.dev/documentation/guides-and-tutorials/learn/email-spec/amp-email-format/?format=email', + ]; +} diff --git a/src/Validator/Spec/Section/CssRulesets.php b/src/Validator/Spec/Section/CssRulesets.php index b5c6be4b7..1f66cfd20 100644 --- a/src/Validator/Spec/Section/CssRulesets.php +++ b/src/Validator/Spec/Section/CssRulesets.php @@ -13,7 +13,6 @@ use AmpProject\Validator\Spec\CssRuleset; use AmpProject\Validator\Spec\IterableSection; use AmpProject\Validator\Spec\Iteration; -use LogicException; final class CssRulesets implements IterableSection { diff --git a/src/Validator/Spec/Section/Doc.php b/src/Validator/Spec/Section/Doc.php deleted file mode 100644 index 9529158d5..000000000 --- a/src/Validator/Spec/Section/Doc.php +++ /dev/null @@ -1,30 +0,0 @@ -doc = [ - [ - SpecRule::HTML_FORMAT => [ - Format::AMP4EMAIL, - ], - SpecRule::MAX_BYTES => 200000, - SpecRule::MAX_BYTES_SPEC_URL => 'https://amp.dev/documentation/guides-and-tutorials/learn/email-spec/amp-email-format/?format=email', - ], - ]; - } -} diff --git a/src/Validator/Spec/Section/DocRulesets.php b/src/Validator/Spec/Section/DocRulesets.php new file mode 100644 index 000000000..d1cd1e4b1 --- /dev/null +++ b/src/Validator/Spec/Section/DocRulesets.php @@ -0,0 +1,146 @@ + + */ + const DOC_RULESETS = [ + DocRuleset\Amp4email::ID => DocRuleset\Amp4email::class, + ]; + + /** + * Mapping of AMP format to array of document ruleset IDs. + * + * This is used to optimize querying by AMP format. + * + * @var array> + */ + const BY_FORMAT = [ + Format::AMP4EMAIL => [ + DocRuleset\Amp4email::ID, + ], + ]; + + /** + * Cache of instantiated DocRuleset objects. + * + * @var array + */ + private $docRulesetsCache = []; + + /** + * Array used for storing the iteration index in. + * + * @var array|null + */ + private $iterationArray; + + /** + * Get a document ruleset by its document ruleset ID. + * + * @param string $docRulesetId document ruleset ID to get the collection of document rulesets for. + * @return DocRuleset Requested document ruleset. + * @throws InvalidDocRulesetName If an invalid document ruleset name is requested. + */ + public function get($docRulesetId) + { + if (!array_key_exists($docRulesetId, self::DOC_RULESETS)) { + throw InvalidDocRulesetName::forDocRulesetName($docRulesetId); + } + + if (array_key_exists($docRulesetId, $this->docRulesetsCache)) { + return $this->docRulesetsCache[$docRulesetId]; + } + + $docRulesetClassName = self::DOC_RULESETS[$docRulesetId]; + + /** @var DocRuleset $docRuleset */ + $docRuleset = new $docRulesetClassName(); + + $this->docRulesetsCache[$docRulesetId] = $docRuleset; + + return $docRuleset; + } + + /** + * Get a collection of document rulesets for a given AMP HTML format name. + * + * @param string $format AMP HTML format to get the document rulesets for. + * @return array Array of document rulesets matching the requested AMP HTML format. + * @throws InvalidFormat If an invalid AMP HTML format is requested. + */ + public function byFormat($format) + { + if (!array_key_exists($format, self::BY_FORMAT)) { + throw InvalidFormat::forFormat($format); + } + + $docRulesetIds = self::BY_FORMAT[$format]; + if (!is_array($docRulesetIds)) { + $docRulesetIds = [$docRulesetIds]; + } + + $docRulesets = []; + foreach ($docRulesetIds as $docRulesetId) { + $docRulesets[] = $this->get($docRulesetId); + } + + return $docRulesets; + } + + /** + * Get the list of available keys. + * + * @return array Array of available keys. + */ + public function getAvailableKeys() + { + return array_keys(self::DOC_RULESETS); + } + + /** + * Find the instantiated object for the current key. + * + * This should use its own caching mechanism as needed. + * + * Ideally, current() should be overridden as well to provide the correct object type-hint. + * + * @param string $key Key to retrieve the instantiated object for. + * @return DocRuleset Instantiated object for the current key. + */ + public function findByKey($key) + { + return $this->get($key); + } + + /** + * Return the current iterable object. + * + * @return DocRuleset DocRuleset object. + */ + public function current() + { + return $this->parentCurrent(); + } +} diff --git a/src/Validator/Spec/Tag.php b/src/Validator/Spec/Tag.php index e41ca2edb..61c8c7b48 100644 --- a/src/Validator/Spec/Tag.php +++ b/src/Validator/Spec/Tag.php @@ -12,8 +12,6 @@ /** * Class Tag. * - * @package AmpProject\Tooling\Validator\SpecGenerator\Template - * * @property-read array $alsoRequiresTagWarning * @property-read array $ampLayout * @property-read array $attrLists diff --git a/tests/Validator/Spec/DocRulesetTest.php b/tests/Validator/Spec/DocRulesetTest.php new file mode 100644 index 000000000..bde0f95af --- /dev/null +++ b/tests/Validator/Spec/DocRulesetTest.php @@ -0,0 +1,55 @@ +assertIsArray($dummyDocRuleset->htmlFormat); + } + + /** + * @covers \AmpProject\Validator\Spec\DocRuleset::getId() + * @covers \AmpProject\Validator\Spec\DocRuleset::get() + * @covers \AmpProject\Validator\Spec\DocRuleset::has() + * @covers \AmpProject\Validator\Spec\Tag::__get() + */ + public function testGet() + { + $dummyDocRuleset = new DummyDocRuleset(); + + $this->assertEquals('DUMMY', $dummyDocRuleset->getId()); + + $this->assertEquals($dummyDocRuleset->maxBytes, $dummyDocRuleset->get('maxBytes')); + + $this->assertTrue($dummyDocRuleset->has('maxBytes')); + $this->assertFalse($dummyDocRuleset->has('utter nonsense')); + + $this->assertEquals(123, $dummyDocRuleset->get('maxBytes')); + } + + /** + * @covers \AmpProject\Validator\Spec\DocRuleset::get() + * @covers \AmpProject\Exception\InvalidSpecRuleName::forSpecRuleName() + */ + public function testThrowsExceptionForUnknownDocRuleset() + { + $dummyDocRuleset = new DummyDocRuleset(); + + $this->expectException(InvalidSpecRuleName::class); + $this->expectExceptionMessage("Invalid spec rule name 'utter nonsense' was requested from the validator spec."); + $dummyDocRuleset->get('utter nonsense'); + } +} diff --git a/tests/Validator/Spec/Section/DocRulesetsTest.php b/tests/Validator/Spec/Section/DocRulesetsTest.php new file mode 100644 index 000000000..f47ddb3be --- /dev/null +++ b/tests/Validator/Spec/Section/DocRulesetsTest.php @@ -0,0 +1,79 @@ +docRulesets = $spec->docRulesets(); + } + + /** + * @covers \AmpProject\Validator\Spec\Section\DocRulesets::get() + */ + public function testGet() + { + $ruleSet = $this->docRulesets->get('AMP4EMAIL'); + $this->assertInstanceOf(Spec\DocRuleset::class, $ruleSet); + } + + /** + * @covers \AmpProject\Validator\Spec\Section\DocRulesets::get() + * @covers \AmpProject\Exception\InvalidDocRulesetName::forDocRulesetName() + */ + public function testGetThrowsExceptionForUnknownName() + { + $this->expectException(InvalidDocRulesetName::class); + $this->docRulesets->get('utter nonsense'); + } + + /** + * @covers \AmpProject\Validator\Spec\Section\DocRulesets::byFormat() + */ + public function testByFormat() + { + $ruleSets = $this->docRulesets->byFormat(Format::AMP4EMAIL); + $this->assertIsArray($ruleSets); + $this->assertCount(1, $ruleSets); + foreach ($ruleSets as $ruleSet) { + $this->assertInstanceOf(Spec\DocRuleset::class, $ruleSet); + } + } + + /** + * @covers \AmpProject\Validator\Spec\Section\DocRulesets::byFormat() + * @covers \AmpProject\Exception\InvalidFormat::forFormat() + */ + public function testByFormatThrowsExceptionForUnknownFormat() + { + $this->expectException(InvalidFormat::class); + $this->docRulesets->byFormat('utter nonsense'); + } + + public function testIteration() + { + $this->assertGreaterThan(0, count($this->docRulesets)); + foreach ($this->docRulesets as $docRuleset) { + $this->assertInstanceOf(Spec\DocRuleset::class, $docRuleset); + $this->assertNotEmpty($docRuleset::SPEC); + } + } +} diff --git a/tests/Validator/SpecTest.php b/tests/Validator/SpecTest.php index ea4890dca..78d89bb82 100644 --- a/tests/Validator/SpecTest.php +++ b/tests/Validator/SpecTest.php @@ -48,11 +48,11 @@ public function testItCanProvideTheDescendantTagListsSection() $this->assertInstanceOf(Section\DescendantTagLists::class, $descendantTagList); } - public function testItCanProvideTheDocSection() + public function testItCanProvideTheDocRulesetsSection() { $spec = new Spec(); - $doc = $spec->doc(); - $this->assertInstanceOf(Section\Doc::class, $doc); + $doc = $spec->docRulesets(); + $this->assertInstanceOf(Section\DocRulesets::class, $doc); } public function testItCanProvideTheErrorsSection() diff --git a/tests/src/ValidatorFixtures/DummyCssRuleset.php b/tests/src/ValidatorFixtures/DummyCssRuleset.php index 59a4ba0b5..665918a2f 100644 --- a/tests/src/ValidatorFixtures/DummyCssRuleset.php +++ b/tests/src/ValidatorFixtures/DummyCssRuleset.php @@ -2,7 +2,6 @@ namespace AmpProject\Tests\ValidatorFixtures; -use AmpProject\Attribute; use AmpProject\Validator\Spec\CssRuleset; use AmpProject\Validator\Spec\SpecRule; diff --git a/tests/src/ValidatorFixtures/DummyDocRuleset.php b/tests/src/ValidatorFixtures/DummyDocRuleset.php new file mode 100644 index 000000000..9b3f7b844 --- /dev/null +++ b/tests/src/ValidatorFixtures/DummyDocRuleset.php @@ -0,0 +1,18 @@ + */ + const SPEC = [ + SpecRule::MAX_BYTES => 123, + ]; +}