From e23041a6c35318f80ec27eba24acc06e09766359 Mon Sep 17 00:00:00 2001 From: Dustin Wilson Date: Mon, 11 Oct 2021 11:20:37 -0500 Subject: [PATCH] Moved MagicProperties and Exception to framework repository --- RoboFile.php | 117 +--------------------------- composer.json | 3 +- composer.lock | 38 ++++++++- lib/DOMException.php | 72 +++++------------ lib/Document.php | 1 + lib/DocumentFragment.php | 1 + lib/Element.php | 1 + lib/Exception.php | 59 +------------- lib/TokenList.php | 2 + lib/traits/MagicProperties.php | 79 ------------------- postcss.config.js | 17 ---- tests/cases/TestDocument.php | 1 - tests/cases/TestException.php | 49 ------------ tests/cases/TestMagicProperties.php | 79 ------------------- tests/phpunit.dist.xml | 2 - 15 files changed, 68 insertions(+), 453 deletions(-) delete mode 100644 lib/traits/MagicProperties.php delete mode 100644 postcss.config.js delete mode 100644 tests/cases/TestException.php delete mode 100644 tests/cases/TestMagicProperties.php diff --git a/RoboFile.php b/RoboFile.php index 8467a16..7fadb52 100644 --- a/RoboFile.php +++ b/RoboFile.php @@ -147,119 +147,4 @@ class RoboFile extends \Robo\Tasks { } return $this->taskExec($executor)->option("-d", "zend.assertions=1")->arg($execpath)->option("-c", $confpath)->args(array_merge($set, $args))->run(); } - - /** Runs the coding standards fixer */ - public function clean($opts = ['demo|d' => false]): Result { - $t = $this->taskExec(norm(BASE."vendor/bin/php-cs-fixer")); - $t->arg("fix"); - if ($opts['demo']) { - $t->args("--dry-run", "--diff")->option("--diff-format", "udiff"); - } - return $t->run(); - } - - /** Produces the CharacterReference class file */ - public function charref() { - $template = <<<'FILE' - $data) { - // strip the ampersand from the entity name - $entity = substr($entity, 1); - // add the entity name to an array of regular expression terms - // if the entry exists in unterminated form, compress it into one, skiping the unterminated version - if (substr($entity, -1) === ';') { - if (isset($input['&'.substr($entity, 0, strlen($entity) -1)])) { - $terms[] = "$entity?"; - } else { - $terms[] = $entity; - } - } - // add a PHP-code representation of the entity name and its characters to another array - $chars = $data['codepoints']; - for ($a = 0; $a < sizeof($chars); $a++) { - $chars[$a] = '\u{'.dechex($chars[$a]).'}'; - } - $chars = implode('', $chars); - $list[] = "'$entity'=>\"$chars\""; - } - // concatenate the list of entities and substitute them into the template - $list = implode(",", $list); - $template = str_replace('%NAMED_REFERENCES%', $list, $template); - // prepare the list of terms as a regular expression - // sort longest terms first - usort($terms, function($a, $b) { - return -1 * (strlen(preg_replace("/\W/", "", $a)) <=> strlen(preg_replace("/\W/", "", $b))); - }); - // note the longest term - $longest = strlen(preg_replace("/\W/", "", $terms[0])); - $template = str_replace('%LONGEST%', $longest, $template); - // concatenate the terms into a case-sensitive non-capturing prefix search - $regexp = '/^(?:'.implode('|', $terms).')/'; - $template = str_replace('%NAMED_PATTERN%', var_export($regexp, true), $template); - // Compile the C1 control substitution table - // See https://html.spec.whatwg.org/multipage/parsing.html#numeric-character-reference-end-state - $list = []; - $c1table = [ - 0x80 => 0x20AC, // EURO SIGN (€) - 0x82 => 0x201A, // SINGLE LOW-9 QUOTATION MARK (‚) - 0x83 => 0x0192, // LATIN SMALL LETTER F WITH HOOK (ƒ) - 0x84 => 0x201E, // DOUBLE LOW-9 QUOTATION MARK („) - 0x85 => 0x2026, // HORIZONTAL ELLIPSIS (…) - 0x86 => 0x2020, // DAGGER (†) - 0x87 => 0x2021, // DOUBLE DAGGER (‡) - 0x88 => 0x02C6, // MODIFIER LETTER CIRCUMFLEX ACCENT (ˆ) - 0x89 => 0x2030, // PER MILLE SIGN (‰) - 0x8A => 0x0160, // LATIN CAPITAL LETTER S WITH CARON (Š) - 0x8B => 0x2039, // SINGLE LEFT-POINTING ANGLE QUOTATION MARK (‹) - 0x8C => 0x0152, // LATIN CAPITAL LIGATURE OE (Œ) - 0x8E => 0x017D, // LATIN CAPITAL LETTER Z WITH CARON (Ž) - 0x91 => 0x2018, // LEFT SINGLE QUOTATION MARK (‘) - 0x92 => 0x2019, // RIGHT SINGLE QUOTATION MARK (’) - 0x93 => 0x201C, // LEFT DOUBLE QUOTATION MARK (“) - 0x94 => 0x201D, // RIGHT DOUBLE QUOTATION MARK (”) - 0x95 => 0x2022, // BULLET (•) - 0x96 => 0x2013, // EN DASH (–) - 0x97 => 0x2014, // EM DASH (—) - 0x98 => 0x02DC, // SMALL TILDE (˜) - 0x99 => 0x2122, // TRADE MARK SIGN (™) - 0x9A => 0x0161, // LATIN SMALL LETTER S WITH CARON (š) - 0x9B => 0x203A, // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK (›) - 0x9C => 0x0153, // LATIN SMALL LIGATURE OE (œ) - 0x9E => 0x017E, // LATIN SMALL LETTER Z WITH CARON (ž) - 0x9F => 0x0178, // LATIN CAPITAL LETTER Y WITH DIAERESIS (Ÿ) - ]; - foreach ($c1table as $c1 => $code) { - $list[] = "$c1=>$code"; - } - $list = implode(",", $list); - $template = str_replace('%C1_SUBSTITUTIONS%', $list, $template); - // output the file itself - file_put_contents(BASE."lib/CharacterReference.php", $template); - } -} +} \ No newline at end of file diff --git a/composer.json b/composer.json index cb1f0ca..ec2948b 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,8 @@ "require": { "php": ">=7.4", "ext-dom": "*", - "mensbeam/html-parser": "dev-master" + "mensbeam/html-parser": "dev-master", + "mensbeam/framework": "^1.0" }, "scripts": { "post-install-cmd": ["@composer bin all install"], diff --git a/composer.lock b/composer.lock index 79e761c..bb82bd3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,44 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3371a9b669d02688e062be96d83f1eff", + "content-hash": "fb8d8d217fab6f78a9bdf736c8da2316", "packages": [ + { + "name": "mensbeam/framework", + "version": "1.0", + "source": { + "type": "git", + "url": "https://code.mensbeam.com/MensBeam/Framework", + "reference": "503ade585f25a740d2f2f5b63b8c1a4ac6820cea" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "MensBeam\\Framework\\": [ + "lib/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dustin Wilson", + "email": "dustin@dustinwilson.com", + "homepage": "https://dustinwilson.com/" + } + ], + "description": "Common classes and traits used in many Mensbeam projects", + "time": "2021-10-11T14:53:50+00:00" + }, { "name": "mensbeam/html-parser", "version": "dev-master", diff --git a/lib/DOMException.php b/lib/DOMException.php index 7b989ce..3805106 100644 --- a/lib/DOMException.php +++ b/lib/DOMException.php @@ -7,8 +7,10 @@ declare(strict_types=1); namespace MensBeam\HTML\DOM; +use MensBeam\Framework\Exception; -class DOMException extends \Exception { + +class DOMException extends Exception { // From PHP's DOMException; keeping error codes consistent const HIERARCHY_REQUEST_ERROR = 3; const WRONG_DOCUMENT = 4; @@ -22,58 +24,26 @@ class DOMException extends \Exception { const INVALID_ACCESS_ERROR = 15; const VALIDATION_ERROR = 16; - const OUTER_HTML_FAILED_NOPARENT = 101; - - - protected static $messages = [ - 3 => 'Hierarchy request error; supplied node is not allowed here', - 4 => 'Supplied node does not belong to this document', - 5 => 'Invalid character', - 7 => 'Modification not allowed here', - 8 => 'Not found error', - 9 => 'Feature is not supported because %s', - 12 => 'Syntax error', - 13 => 'Invalid modification error', - 14 => 'Namespace error', - 15 => 'Invalid access error', - 16 => 'Validation error', + const OUTER_HTML_FAILED_NOPARENT = 301; - 101 => 'Failed to set the "outerHTML" property; the element does not have a parent node' - ]; public function __construct(int $code, ...$args) { - if (!isset(self::$messages[$code])) { - throw new Exception(Exception::INVALID_CODE); - } - - $message = self::$messages[$code]; - $previous = null; - - // @codeCoverageIgnoreStart - if ($args) { - // Grab a previous exception if there is one. - if ($args[0] instanceof \Throwable) { - $previous = array_shift($args); - } elseif (end($args) instanceof \Throwable) { - $previous = array_pop($args); - } - } - // @codeCoverageIgnoreEnd - - // Count the number of replacements needed in the message. - preg_match_all('/(\%(?:\d+\$)?s)/', $message, $matches); - $count = count($matches[1]); - - // If the number of replacements don't match the arguments then oops. - if (count($args) !== $count) { - throw new Exception(Exception::INCORRECT_PARAMETERS_FOR_MESSAGE, $count); - } - - if ($count > 0) { - // Go through each of the arguments and run sprintf on the strings. - $message = call_user_func_array('sprintf', array_merge([$message], $args)); - } - - parent::__construct($message, $code, $previous); + self::$messages = array_replace(parent::$messages, [ + 3 => 'Hierarchy request error; supplied node is not allowed here', + 4 => 'Supplied node does not belong to this document', + 5 => 'Invalid character', + 7 => 'Modification not allowed here', + 8 => 'Not found error', + 9 => 'Feature is not supported because %s', + 12 => 'Syntax error', + 13 => 'Invalid modification error', + 14 => 'Namespace error', + 15 => 'Invalid access error', + 16 => 'Validation error', + + 301 => 'Failed to set the "outerHTML" property; the element does not have a parent node' + ]); + + parent::__construct($code, ...$args); } } diff --git a/lib/Document.php b/lib/Document.php index d343a0e..24416b7 100644 --- a/lib/Document.php +++ b/lib/Document.php @@ -7,6 +7,7 @@ declare(strict_types=1); namespace MensBeam\HTML\DOM; +use MensBeam\Framework\MagicProperties; use MensBeam\HTML\Parser, MensBeam\HTML\Parser\Data; diff --git a/lib/DocumentFragment.php b/lib/DocumentFragment.php index 147c980..57a43aa 100644 --- a/lib/DocumentFragment.php +++ b/lib/DocumentFragment.php @@ -7,6 +7,7 @@ declare(strict_types=1); namespace MensBeam\HTML\DOM; +use MensBeam\Framework\MagicProperties; class DocumentFragment extends \DOMDocumentFragment { diff --git a/lib/Element.php b/lib/Element.php index 05fdc4d..d2a570a 100644 --- a/lib/Element.php +++ b/lib/Element.php @@ -7,6 +7,7 @@ declare(strict_types=1); namespace MensBeam\HTML\DOM; +use MensBeam\Framework\MagicProperties; use MensBeam\HTML\Parser; diff --git a/lib/Exception.php b/lib/Exception.php index e707383..87a02eb 100644 --- a/lib/Exception.php +++ b/lib/Exception.php @@ -7,62 +7,7 @@ declare(strict_types=1); namespace MensBeam\HTML\DOM; +use MensBeam\Framework\Exception as FrameworkException; -class Exception extends \Exception { - const INVALID_CODE = 100; - const UNKNOWN_ERROR = 101; - const INCORRECT_PARAMETERS_FOR_MESSAGE = 102; - const UNREACHABLE_CODE = 103; - const NONEXISTENT_PROPERTY = 201; - const READONLY_PROPERTY = 202; - const ARGUMENT_TYPE_ERROR = 203; - - - protected static $messages = [ - 100 => 'Invalid error code', - 101 => 'Unknown error; escaping', - 102 => 'Incorrect number of parameters for Exception message; %s expected', - 103 => 'Unreachable code', - - 201 => 'Property %s does not exist', - 202 => 'Cannot write readonly property %s', - 203 => 'Argument #%s ($%s) must be of type %s, %s given' - ]; - - public function __construct(int $code, ...$args) { - if (!isset(self::$messages[$code])) { - throw new Exception(Exception::INVALID_CODE); - } - - $message = self::$messages[$code]; - $previous = null; - - // @codeCoverageIgnoreStart - if ($args) { - // Grab a previous exception if there is one. - if ($args[0] instanceof \Throwable) { - $previous = array_shift($args); - } elseif (end($args) instanceof \Throwable) { - $previous = array_pop($args); - } - } - // @codeCoverageIgnoreEnd - - // Count the number of replacements needed in the message. - preg_match_all('/(\%(?:\d+\$)?s)/', $message, $matches); - $count = count($matches[1]); - - // If the number of replacements don't match the arguments then oops. - if (count($args) !== $count) { - throw new Exception(Exception::INCORRECT_PARAMETERS_FOR_MESSAGE, $count); - } - - if ($count > 0) { - // Go through each of the arguments and run sprintf on the strings. - $message = call_user_func_array('sprintf', array_merge([$message], $args)); - } - - parent::__construct($message, $code, $previous); - } -} +class Exception extends FrameworkException {} diff --git a/lib/TokenList.php b/lib/TokenList.php index d8af90e..e98a855 100644 --- a/lib/TokenList.php +++ b/lib/TokenList.php @@ -7,6 +7,8 @@ declare(strict_types=1); namespace MensBeam\HTML\DOM; +use MensBeam\Framework\MagicProperties; + class TokenList implements \ArrayAccess, \Countable, \Iterator { use MagicProperties; diff --git a/lib/traits/MagicProperties.php b/lib/traits/MagicProperties.php deleted file mode 100644 index 6102205..0000000 --- a/lib/traits/MagicProperties.php +++ /dev/null @@ -1,79 +0,0 @@ -getMagicPropertyMethodName($name); - if ($methodName === null) { - throw new Exception(Exception::NONEXISTENT_PROPERTY, $name); - } - return call_user_func([ $this, $methodName ]); - } - - public function __isset(string $name): bool { - return ($this->getMagicPropertyMethodName($name) !== null); - } - - public function __set(string $name, $value) { - $methodName = $this->getMagicPropertyMethodName($name, false); - if ($methodName !== null) { - call_user_func([ $this, $methodName ], $value); - return; - } - - if ($this->getMagicPropertyMethodName($name) !== null) { - throw new Exception(Exception::READONLY_PROPERTY, $name); - } else { - throw new Exception(Exception::NONEXISTENT_PROPERTY, $name); - } - } - - public function __unset(string $name) { - $methodName = $this->getMagicPropertyMethodName($name, false); - if ($methodName === null) { - throw new Exception(Exception::READONLY_PROPERTY, $name); - } - - call_user_func([ $this, $methodName ], null); - } - - - // Method_exists is case-insensitive because methods are case-insensitive in - // PHP. Properties in PHP 8 are sensitive, so let's use reflection to check - // against the actual name to get a case sensitive match like methods should be! - private function getMagicPropertyMethodName(string $name, bool $get = true): ?string { - static $protectedMethodsList = null; - - $methodName = "__" . (($get) ? 'get' : 'set') . "_{$name}"; - if (method_exists($this, $methodName)) { - if ($protectedMethodsList === null) { - $reflector = new \ReflectionClass($this); - // Magic property methods are protected - $protectedMethodsList = $reflector->getMethods(\ReflectionMethod::IS_PROTECTED); - } - - foreach ($protectedMethodsList as $method) { - if ($method->name === $methodName) { - return $methodName; - } - } - } - - return null; - } -} \ No newline at end of file diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index 4eecda3..0000000 --- a/postcss.config.js +++ /dev/null @@ -1,17 +0,0 @@ -module.exports = ctx => ({ - //map: ctx.options.map, - parser: 'postcss-scss', - //syntax: 'postcss-scss', - plugins: { - 'postcss-import': { root: ctx.file.dirname }, - 'postcss-discard-comments': {}, - 'postcss-sassy-mixins': {}, - 'postcss-custom-media': {preserve: false}, - 'postcss-media-minmax': {}, - 'postcss-custom-properties': {preserve: false}, - 'postcss-color-function': {}, - 'postcss-nested': {}, - 'autoprefixer': {}, - 'postcss-csso': {}, - } -}) diff --git a/tests/cases/TestDocument.php b/tests/cases/TestDocument.php index 6ca04cd..939fd2a 100644 --- a/tests/cases/TestDocument.php +++ b/tests/cases/TestDocument.php @@ -185,7 +185,6 @@ class TestDocument extends \PHPUnit\Framework\TestCase { /** * @dataProvider provideDocumentCreationFailures * @covers \MensBeam\HTML\DOM\Document::__construct - * @covers \MensBeam\HTML\DOM\Exception::__construct */ public function testDocumentCreationFailures($source): void { $this->expectException(Exception::class); diff --git a/tests/cases/TestException.php b/tests/cases/TestException.php deleted file mode 100644 index cd03ab5..0000000 --- a/tests/cases/TestException.php +++ /dev/null @@ -1,49 +0,0 @@ -expectException(Exception::class); - $this->expectExceptionCode($errorCode); - $closure(); - } -} \ No newline at end of file diff --git a/tests/cases/TestMagicProperties.php b/tests/cases/TestMagicProperties.php deleted file mode 100644 index 4748b15..0000000 --- a/tests/cases/TestMagicProperties.php +++ /dev/null @@ -1,79 +0,0 @@ -omgWTFBBQ; - }, Exception::NONEXISTENT_PROPERTY ], - [ function() { - $d = new Document(); - $d->omgWTFBBQ = 'ook'; - }, Exception::NONEXISTENT_PROPERTY ], - [ function() { - $d = new Document(); - $d->xpath = 'ook'; - }, Exception::READONLY_PROPERTY ], - [ function() { - $d = new Document(); - unset($d->xpath); - }, Exception::READONLY_PROPERTY ] - ]; - } - - /** - * @dataProvider provideFailures - * @covers \MensBeam\HTML\DOM\MagicProperties::__get - * @covers \MensBeam\HTML\DOM\MagicProperties::__set - * @covers \MensBeam\HTML\DOM\MagicProperties::__unset - */ - public function testFailures(\Closure $closure, int $errorCode): void { - $this->expectException(Exception::class); - $this->expectExceptionCode($errorCode); - $closure(); - } - - /** @covers \MensBeam\HTML\DOM\MagicProperties::__isset */ - public function testIsset(): void { - $d = new Document(); - $this->assertTrue(isset($d->body)); - } - - /** @covers \MensBeam\HTML\DOM\MagicProperties::__unset */ - public function testUnset(): void { - // Nothing allows setting values to null yet, so make one - $d = new class { - use MagicProperties; - protected ?string $_ook = 'ook'; - - - protected function __get_ook(): ?string { - return $this->_ook; - } - - protected function __set_ook(?string $value): void { - $this->_ook = $value; - } - }; - - unset($d->ook); - $this->assertNull($d->ook); - } -} \ No newline at end of file diff --git a/tests/phpunit.dist.xml b/tests/phpunit.dist.xml index 9b6cb67..f3c89ba 100644 --- a/tests/phpunit.dist.xml +++ b/tests/phpunit.dist.xml @@ -22,9 +22,7 @@ cases/TestDocumentOrElement.php cases/TestElement.php cases/TestElementMap.php - cases/TestException.php cases/TestLeafNode.php - cases/TestMagicProperties.php cases/TestMoonwalk.php cases/TestNode.php cases/TestParentNode.php