diff --git a/.gitignore b/.gitignore
index 841798764..b0d9fc2ca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,12 +16,14 @@ bin
*/*/composer.json
*/*/composer.lock
# ignore README and UPGRADE
-*/*/README.md
-*/*/README.rst
-*/*/UPGRADE.md
-*/*/UPGRADE-*.md
-*/*/CONTRIBUTING.md
-*/*/SECURITY.md
+*/**/README.md
+*/**/README.rst
+*/**/UPGRADE.md
+*/**/UPGRADING.md
+*/**/UPGRADE-*.md
+*/**/CONTRIBUTING.md
+*/**/SECURITY.md
+*/**/RELEASE.md
# ignore codestyle tools config
*/*/psalm.xml
*/*/psalm.baseline.xml
@@ -71,10 +73,11 @@ giggsey/locale/CLDR-VERSION.txt
guzzlehttp/guzzle/build/
guzzlehttp/guzzle/docs/
guzzlehttp/guzzle/MAKEFILE
-guzzlehttp/guzzle/UPGRADING.md
justinrainbow/json-schema/demo
+masterminds/html5/CREDITS
+
mexitek/phpcolors/demo
nikic/php-parser/bin/
@@ -83,10 +86,6 @@ nikic/php-parser/test/
nikic/php-parser/test_old/
nikic/php-parser/grammar/analyze.php
nikic/php-parser/grammar/rebuildParser.php
-nikic/php-parser/UPGRADE-1.0.md
-nikic/php-parser/UPGRADE-2.0.md
-nikic/php-parser/UPGRADE-3.0.md
-nikic/php-parser/UPGRADE-4.0.md
nikic/php-parser/grammar/parser.template
nikic/php-parser/grammar/php5.y
nikic/php-parser/grammar/php7.y
diff --git a/composer.json b/composer.json
index c6317374c..e29153d24 100644
--- a/composer.json
+++ b/composer.json
@@ -24,7 +24,7 @@
"deepdiver1975/tarstreamer": "^2.1.0",
"doctrine/dbal": "^3.7.0",
"egulias/email-validator": "^3.2.6",
- "fusonic/opengraph": "^2.2",
+ "fusonic/opengraph": "^2.3.0",
"giggsey/libphonenumber-for-php-lite": "^8.13.12",
"guzzlehttp/guzzle": "^7.8.1",
"icewind/searchdav": "^3.1.0",
diff --git a/composer.lock b/composer.lock
index 7c32b323c..c620e58b9 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "603c28381b3cf9fdb8208123b2b64b60",
+ "content-hash": "369f52466ea42e09a8e1152d1f7e4e98",
"packages": [
{
"name": "aws/aws-crt-php",
@@ -1045,69 +1045,22 @@
"abandoned": true,
"time": "2021-04-24T19:01:55+00:00"
},
- {
- "name": "fusonic/linq",
- "version": "v1.1.0",
- "source": {
- "type": "git",
- "url": "https://github.com/fusonic/linq.git",
- "reference": "63520ef1470ca771acbd26871efb945dd4a7a5d8"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/fusonic/linq/zipball/63520ef1470ca771acbd26871efb945dd4a7a5d8",
- "reference": "63520ef1470ca771acbd26871efb945dd4a7a5d8",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.2"
- },
- "type": "library",
- "autoload": {
- "psr-0": {
- "Fusonic\\Linq": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Fusonic",
- "homepage": "http://www.fusonic.net"
- }
- ],
- "description": "LINQ 2 objects class for PHP",
- "homepage": "http://fusonic.github.io/fusonic-linq/",
- "keywords": [
- "linq",
- "linq2objects"
- ],
- "support": {
- "issues": "https://github.com/fusonic/linq/issues",
- "source": "https://github.com/fusonic/linq/tree/master"
- },
- "abandoned": true,
- "time": "2015-02-26T22:49:17+00:00"
- },
{
"name": "fusonic/opengraph",
- "version": "v2.2.0",
+ "version": "v2.3.0",
"source": {
"type": "git",
"url": "https://github.com/fusonic/opengraph.git",
- "reference": "a63b588fbe56c175ae06e158f1513642653ee3c1"
+ "reference": "09dca70b04b4221c9dc664e1689e876a3faa50c2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/fusonic/opengraph/zipball/a63b588fbe56c175ae06e158f1513642653ee3c1",
- "reference": "a63b588fbe56c175ae06e158f1513642653ee3c1",
+ "url": "https://api.github.com/repos/fusonic/opengraph/zipball/09dca70b04b4221c9dc664e1689e876a3faa50c2",
+ "reference": "09dca70b04b4221c9dc664e1689e876a3faa50c2",
"shasum": ""
},
"require": {
"ext-dom": "*",
- "fusonic/linq": "^1.0",
"php": "^7.4|^8.0",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.0",
@@ -1146,9 +1099,9 @@
],
"support": {
"issues": "https://github.com/fusonic/opengraph/issues",
- "source": "https://github.com/fusonic/opengraph/tree/v2.2.0"
+ "source": "https://github.com/fusonic/opengraph/tree/v2.3.0"
},
- "time": "2022-01-20T05:47:36+00:00"
+ "time": "2023-12-05T14:26:51+00:00"
},
{
"name": "giggsey/libphonenumber-for-php-lite",
@@ -2053,6 +2006,73 @@
],
"time": "2020-10-31T13:45:51+00:00"
},
+ {
+ "name": "masterminds/html5",
+ "version": "2.9.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Masterminds/html5-php.git",
+ "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
+ "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.7-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Masterminds\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Matt Butcher",
+ "email": "technosophos@gmail.com"
+ },
+ {
+ "name": "Matt Farina",
+ "email": "matt@mattfarina.com"
+ },
+ {
+ "name": "Asmir Mustafic",
+ "email": "goetas@gmail.com"
+ }
+ ],
+ "description": "An HTML5 parser and serializer.",
+ "homepage": "http://masterminds.github.io/html5-php",
+ "keywords": [
+ "HTML5",
+ "dom",
+ "html",
+ "parser",
+ "querypath",
+ "serializer",
+ "xml"
+ ],
+ "support": {
+ "issues": "https://github.com/Masterminds/html5-php/issues",
+ "source": "https://github.com/Masterminds/html5-php/tree/2.9.0"
+ },
+ "time": "2024-03-31T07:05:07+00:00"
+ },
{
"name": "mexitek/phpcolors",
"version": "v1.0.4",
@@ -4566,21 +4586,20 @@
},
{
"name": "symfony/css-selector",
- "version": "v5.4.11",
+ "version": "v6.4.3",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
- "reference": "c1681789f059ab756001052164726ae88512ae3d"
+ "reference": "ee0f7ed5cf298cc019431bb3b3977ebc52b86229"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/css-selector/zipball/c1681789f059ab756001052164726ae88512ae3d",
- "reference": "c1681789f059ab756001052164726ae88512ae3d",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/ee0f7ed5cf298cc019431bb3b3977ebc52b86229",
+ "reference": "ee0f7ed5cf298cc019431bb3b3977ebc52b86229",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
- "symfony/polyfill-php80": "^1.16"
+ "php": ">=8.1"
},
"type": "library",
"autoload": {
@@ -4612,7 +4631,7 @@
"description": "Converts CSS selectors to XPath expressions",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/css-selector/tree/v5.4.11"
+ "source": "https://github.com/symfony/css-selector/tree/v6.4.3"
},
"funding": [
{
@@ -4628,7 +4647,7 @@
"type": "tidelift"
}
],
- "time": "2022-06-27T16:58:25+00:00"
+ "time": "2024-01-23T14:51:35+00:00"
},
{
"name": "symfony/deprecation-contracts",
@@ -4699,34 +4718,26 @@
},
{
"name": "symfony/dom-crawler",
- "version": "v5.4.11",
+ "version": "v6.4.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/dom-crawler.git",
- "reference": "0b900ca5576ecd59e08c76127e616667cfe427a7"
+ "reference": "f0e7ec3fa17000e2d0cb4557b4b47c88a6a63531"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/0b900ca5576ecd59e08c76127e616667cfe427a7",
- "reference": "0b900ca5576ecd59e08c76127e616667cfe427a7",
+ "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/f0e7ec3fa17000e2d0cb4557b4b47c88a6a63531",
+ "reference": "f0e7ec3fa17000e2d0cb4557b4b47c88a6a63531",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
- "symfony/deprecation-contracts": "^2.1|^3",
+ "masterminds/html5": "^2.6",
+ "php": ">=8.1",
"symfony/polyfill-ctype": "~1.8",
- "symfony/polyfill-mbstring": "~1.0",
- "symfony/polyfill-php80": "^1.16"
- },
- "conflict": {
- "masterminds/html5": "<2.6"
+ "symfony/polyfill-mbstring": "~1.0"
},
"require-dev": {
- "masterminds/html5": "^2.6",
- "symfony/css-selector": "^4.4|^5.0|^6.0"
- },
- "suggest": {
- "symfony/css-selector": ""
+ "symfony/css-selector": "^5.4|^6.0|^7.0"
},
"type": "library",
"autoload": {
@@ -4754,7 +4765,7 @@
"description": "Eases DOM navigation for HTML and XML documents",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/dom-crawler/tree/v5.4.11"
+ "source": "https://github.com/symfony/dom-crawler/tree/v6.4.4"
},
"funding": [
{
@@ -4770,7 +4781,7 @@
"type": "tidelift"
}
],
- "time": "2022-06-27T16:58:25+00:00"
+ "time": "2024-02-07T09:17:57+00:00"
},
{
"name": "symfony/event-dispatcher",
@@ -5174,16 +5185,16 @@
},
{
"name": "symfony/polyfill-ctype",
- "version": "v1.28.0",
+ "version": "v1.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb"
+ "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
- "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4",
+ "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4",
"shasum": ""
},
"require": {
@@ -5197,9 +5208,6 @@
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.28-dev"
- },
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@@ -5236,7 +5244,7 @@
"portable"
],
"support": {
- "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0"
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0"
},
"funding": [
{
@@ -5252,7 +5260,7 @@
"type": "tidelift"
}
],
- "time": "2023-01-26T09:26:14+00:00"
+ "time": "2024-01-29T20:11:03+00:00"
},
{
"name": "symfony/polyfill-intl-grapheme",
@@ -5508,16 +5516,16 @@
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.28.0",
+ "version": "v1.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "42292d99c55abe617799667f454222c54c60e229"
+ "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229",
- "reference": "42292d99c55abe617799667f454222c54c60e229",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
+ "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
"shasum": ""
},
"require": {
@@ -5531,9 +5539,6 @@
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.28-dev"
- },
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@@ -5571,7 +5576,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0"
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0"
},
"funding": [
{
@@ -5587,7 +5592,7 @@
"type": "tidelift"
}
],
- "time": "2023-07-28T09:04:16+00:00"
+ "time": "2024-01-29T20:11:03+00:00"
},
{
"name": "symfony/polyfill-php72",
@@ -5746,16 +5751,16 @@
},
{
"name": "symfony/polyfill-php80",
- "version": "v1.28.0",
+ "version": "v1.29.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5"
+ "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
- "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
+ "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
"shasum": ""
},
"require": {
@@ -5763,9 +5768,6 @@
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.28-dev"
- },
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@@ -5809,7 +5811,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0"
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0"
},
"funding": [
{
@@ -5825,7 +5827,7 @@
"type": "tidelift"
}
],
- "time": "2023-01-26T09:26:14+00:00"
+ "time": "2024-01-29T20:11:03+00:00"
},
{
"name": "symfony/process",
diff --git a/composer/autoload_classmap.php b/composer/autoload_classmap.php
index 19834d353..86a2e4c92 100644
--- a/composer/autoload_classmap.php
+++ b/composer/autoload_classmap.php
@@ -1536,18 +1536,6 @@
'FG\\X509\\SAN\\DNSName' => $vendorDir . '/fgrosse/phpasn1/lib/X509/SAN/DNSName.php',
'FG\\X509\\SAN\\IPAddress' => $vendorDir . '/fgrosse/phpasn1/lib/X509/SAN/IPAddress.php',
'FG\\X509\\SAN\\SubjectAlternativeNames' => $vendorDir . '/fgrosse/phpasn1/lib/X509/SAN/SubjectAlternativeNames.php',
- 'Fusonic\\Linq\\GroupedLinq' => $vendorDir . '/fusonic/linq/src/Fusonic/Linq/GroupedLinq.php',
- 'Fusonic\\Linq\\Helper\\LinqHelper' => $vendorDir . '/fusonic/linq/src/Fusonic/Linq/Helper/LinqHelper.php',
- 'Fusonic\\Linq\\Iterator\\DistinctIterator' => $vendorDir . '/fusonic/linq/src/Fusonic/Linq/Iterator/DistinctIterator.php',
- 'Fusonic\\Linq\\Iterator\\ExceptIterator' => $vendorDir . '/fusonic/linq/src/Fusonic/Linq/Iterator/ExceptIterator.php',
- 'Fusonic\\Linq\\Iterator\\GroupIterator' => $vendorDir . '/fusonic/linq/src/Fusonic/Linq/Iterator/GroupIterator.php',
- 'Fusonic\\Linq\\Iterator\\IntersectIterator' => $vendorDir . '/fusonic/linq/src/Fusonic/Linq/Iterator/IntersectIterator.php',
- 'Fusonic\\Linq\\Iterator\\OfTypeIterator' => $vendorDir . '/fusonic/linq/src/Fusonic/Linq/Iterator/OfTypeIterator.php',
- 'Fusonic\\Linq\\Iterator\\OrderIterator' => $vendorDir . '/fusonic/linq/src/Fusonic/Linq/Iterator/OrderIterator.php',
- 'Fusonic\\Linq\\Iterator\\SelectIterator' => $vendorDir . '/fusonic/linq/src/Fusonic/Linq/Iterator/SelectIterator.php',
- 'Fusonic\\Linq\\Iterator\\SelectManyIterator' => $vendorDir . '/fusonic/linq/src/Fusonic/Linq/Iterator/SelectManyIterator.php',
- 'Fusonic\\Linq\\Iterator\\WhereIterator' => $vendorDir . '/fusonic/linq/src/Fusonic/Linq/Iterator/WhereIterator.php',
- 'Fusonic\\Linq\\Linq' => $vendorDir . '/fusonic/linq/src/Fusonic/Linq/Linq.php',
'Fusonic\\OpenGraph\\Consumer' => $vendorDir . '/fusonic/opengraph/src/Consumer.php',
'Fusonic\\OpenGraph\\Elements\\Audio' => $vendorDir . '/fusonic/opengraph/src/Elements/Audio.php',
'Fusonic\\OpenGraph\\Elements\\ElementBase' => $vendorDir . '/fusonic/opengraph/src/Elements/ElementBase.php',
@@ -1793,6 +1781,26 @@
'League\\Uri\\UriTemplate\\Template' => $vendorDir . '/league/uri/src/UriTemplate/Template.php',
'League\\Uri\\UriTemplate\\VarSpecifier' => $vendorDir . '/league/uri/src/UriTemplate/VarSpecifier.php',
'League\\Uri\\UriTemplate\\VariableBag' => $vendorDir . '/league/uri/src/UriTemplate/VariableBag.php',
+ 'Masterminds\\HTML5' => $vendorDir . '/masterminds/html5/src/HTML5.php',
+ 'Masterminds\\HTML5\\Elements' => $vendorDir . '/masterminds/html5/src/HTML5/Elements.php',
+ 'Masterminds\\HTML5\\Entities' => $vendorDir . '/masterminds/html5/src/HTML5/Entities.php',
+ 'Masterminds\\HTML5\\Exception' => $vendorDir . '/masterminds/html5/src/HTML5/Exception.php',
+ 'Masterminds\\HTML5\\InstructionProcessor' => $vendorDir . '/masterminds/html5/src/HTML5/InstructionProcessor.php',
+ 'Masterminds\\HTML5\\Parser\\CharacterReference' => $vendorDir . '/masterminds/html5/src/HTML5/Parser/CharacterReference.php',
+ 'Masterminds\\HTML5\\Parser\\DOMTreeBuilder' => $vendorDir . '/masterminds/html5/src/HTML5/Parser/DOMTreeBuilder.php',
+ 'Masterminds\\HTML5\\Parser\\EventHandler' => $vendorDir . '/masterminds/html5/src/HTML5/Parser/EventHandler.php',
+ 'Masterminds\\HTML5\\Parser\\FileInputStream' => $vendorDir . '/masterminds/html5/src/HTML5/Parser/FileInputStream.php',
+ 'Masterminds\\HTML5\\Parser\\InputStream' => $vendorDir . '/masterminds/html5/src/HTML5/Parser/InputStream.php',
+ 'Masterminds\\HTML5\\Parser\\ParseError' => $vendorDir . '/masterminds/html5/src/HTML5/Parser/ParseError.php',
+ 'Masterminds\\HTML5\\Parser\\Scanner' => $vendorDir . '/masterminds/html5/src/HTML5/Parser/Scanner.php',
+ 'Masterminds\\HTML5\\Parser\\StringInputStream' => $vendorDir . '/masterminds/html5/src/HTML5/Parser/StringInputStream.php',
+ 'Masterminds\\HTML5\\Parser\\Tokenizer' => $vendorDir . '/masterminds/html5/src/HTML5/Parser/Tokenizer.php',
+ 'Masterminds\\HTML5\\Parser\\TreeBuildingRules' => $vendorDir . '/masterminds/html5/src/HTML5/Parser/TreeBuildingRules.php',
+ 'Masterminds\\HTML5\\Parser\\UTF8Utils' => $vendorDir . '/masterminds/html5/src/HTML5/Parser/UTF8Utils.php',
+ 'Masterminds\\HTML5\\Serializer\\HTML5Entities' => $vendorDir . '/masterminds/html5/src/HTML5/Serializer/HTML5Entities.php',
+ 'Masterminds\\HTML5\\Serializer\\OutputRules' => $vendorDir . '/masterminds/html5/src/HTML5/Serializer/OutputRules.php',
+ 'Masterminds\\HTML5\\Serializer\\RulesInterface' => $vendorDir . '/masterminds/html5/src/HTML5/Serializer/RulesInterface.php',
+ 'Masterminds\\HTML5\\Serializer\\Traverser' => $vendorDir . '/masterminds/html5/src/HTML5/Serializer/Traverser.php',
'Mexitek\\PHPColors\\Color' => $vendorDir . '/mexitek/phpcolors/src/Mexitek/PHPColors/Color.php',
'MicrosoftAzure\\Storage\\Blob\\BlobRestProxy' => $vendorDir . '/microsoft/azure-storage-blob/src/Blob/BlobRestProxy.php',
'MicrosoftAzure\\Storage\\Blob\\BlobSharedAccessSignatureHelper' => $vendorDir . '/microsoft/azure-storage-blob/src/Blob/BlobSharedAccessSignatureHelper.php',
diff --git a/composer/autoload_files.php b/composer/autoload_files.php
index b20dade95..6843f465f 100644
--- a/composer/autoload_files.php
+++ b/composer/autoload_files.php
@@ -7,8 +7,8 @@
return array(
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
- 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
+ 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'383eaff206634a77a1be54e64e6459c7' => $vendorDir . '/sabre/uri/lib/functions.php',
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
diff --git a/composer/autoload_namespaces.php b/composer/autoload_namespaces.php
index c43ac4882..225f7eb6a 100644
--- a/composer/autoload_namespaces.php
+++ b/composer/autoload_namespaces.php
@@ -7,7 +7,6 @@
return array(
'Pimple' => array($vendorDir . '/pimple/pimple/src'),
- 'Fusonic\\Linq' => array($vendorDir . '/fusonic/linq/src'),
'Console' => array($vendorDir . '/pear/console_getopt'),
'Archive_Tar' => array($vendorDir . '/pear/archive_tar'),
'' => array($vendorDir . '/pear/pear-core-minimal/src'),
diff --git a/composer/autoload_psr4.php b/composer/autoload_psr4.php
index c5fa437f9..9f64e2531 100644
--- a/composer/autoload_psr4.php
+++ b/composer/autoload_psr4.php
@@ -62,6 +62,7 @@
'Nextcloud\\LogNormalizer\\' => array($vendorDir . '/nextcloud/lognormalizer/src'),
'MicrosoftAzure\\Storage\\Common\\' => array($vendorDir . '/microsoft/azure-storage-common/src/Common'),
'MicrosoftAzure\\Storage\\Blob\\' => array($vendorDir . '/microsoft/azure-storage-blob/src/Blob'),
+ 'Masterminds\\' => array($vendorDir . '/masterminds/html5/src'),
'League\\Uri\\' => array($vendorDir . '/league/uri/src', $vendorDir . '/league/uri-interfaces/src'),
'Laravel\\SerializableClosure\\' => array($vendorDir . '/laravel/serializable-closure/src'),
'JsonSchema\\' => array($vendorDir . '/justinrainbow/json-schema/src/JsonSchema'),
diff --git a/composer/autoload_static.php b/composer/autoload_static.php
index 1dc699d53..f69f0f288 100644
--- a/composer/autoload_static.php
+++ b/composer/autoload_static.php
@@ -8,8 +8,8 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652
{
public static $files = array (
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
- 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
+ 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
'383eaff206634a77a1be54e64e6459c7' => __DIR__ . '/..' . '/sabre/uri/lib/functions.php',
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
@@ -225,6 +225,7 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652
array (
'MicrosoftAzure\\Storage\\Common\\' => 30,
'MicrosoftAzure\\Storage\\Blob\\' => 28,
+ 'Masterminds\\' => 12,
),
'L' =>
array (
@@ -516,6 +517,10 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652
array (
0 => __DIR__ . '/..' . '/microsoft/azure-storage-blob/src/Blob',
),
+ 'Masterminds\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/masterminds/html5/src',
+ ),
'League\\Uri\\' =>
array (
0 => __DIR__ . '/..' . '/league/uri/src',
@@ -635,13 +640,6 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652
0 => __DIR__ . '/..' . '/pimple/pimple/src',
),
),
- 'F' =>
- array (
- 'Fusonic\\Linq' =>
- array (
- 0 => __DIR__ . '/..' . '/fusonic/linq/src',
- ),
- ),
'C' =>
array (
'Console' =>
@@ -2193,18 +2191,6 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652
'FG\\X509\\SAN\\DNSName' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/SAN/DNSName.php',
'FG\\X509\\SAN\\IPAddress' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/SAN/IPAddress.php',
'FG\\X509\\SAN\\SubjectAlternativeNames' => __DIR__ . '/..' . '/fgrosse/phpasn1/lib/X509/SAN/SubjectAlternativeNames.php',
- 'Fusonic\\Linq\\GroupedLinq' => __DIR__ . '/..' . '/fusonic/linq/src/Fusonic/Linq/GroupedLinq.php',
- 'Fusonic\\Linq\\Helper\\LinqHelper' => __DIR__ . '/..' . '/fusonic/linq/src/Fusonic/Linq/Helper/LinqHelper.php',
- 'Fusonic\\Linq\\Iterator\\DistinctIterator' => __DIR__ . '/..' . '/fusonic/linq/src/Fusonic/Linq/Iterator/DistinctIterator.php',
- 'Fusonic\\Linq\\Iterator\\ExceptIterator' => __DIR__ . '/..' . '/fusonic/linq/src/Fusonic/Linq/Iterator/ExceptIterator.php',
- 'Fusonic\\Linq\\Iterator\\GroupIterator' => __DIR__ . '/..' . '/fusonic/linq/src/Fusonic/Linq/Iterator/GroupIterator.php',
- 'Fusonic\\Linq\\Iterator\\IntersectIterator' => __DIR__ . '/..' . '/fusonic/linq/src/Fusonic/Linq/Iterator/IntersectIterator.php',
- 'Fusonic\\Linq\\Iterator\\OfTypeIterator' => __DIR__ . '/..' . '/fusonic/linq/src/Fusonic/Linq/Iterator/OfTypeIterator.php',
- 'Fusonic\\Linq\\Iterator\\OrderIterator' => __DIR__ . '/..' . '/fusonic/linq/src/Fusonic/Linq/Iterator/OrderIterator.php',
- 'Fusonic\\Linq\\Iterator\\SelectIterator' => __DIR__ . '/..' . '/fusonic/linq/src/Fusonic/Linq/Iterator/SelectIterator.php',
- 'Fusonic\\Linq\\Iterator\\SelectManyIterator' => __DIR__ . '/..' . '/fusonic/linq/src/Fusonic/Linq/Iterator/SelectManyIterator.php',
- 'Fusonic\\Linq\\Iterator\\WhereIterator' => __DIR__ . '/..' . '/fusonic/linq/src/Fusonic/Linq/Iterator/WhereIterator.php',
- 'Fusonic\\Linq\\Linq' => __DIR__ . '/..' . '/fusonic/linq/src/Fusonic/Linq/Linq.php',
'Fusonic\\OpenGraph\\Consumer' => __DIR__ . '/..' . '/fusonic/opengraph/src/Consumer.php',
'Fusonic\\OpenGraph\\Elements\\Audio' => __DIR__ . '/..' . '/fusonic/opengraph/src/Elements/Audio.php',
'Fusonic\\OpenGraph\\Elements\\ElementBase' => __DIR__ . '/..' . '/fusonic/opengraph/src/Elements/ElementBase.php',
@@ -2450,6 +2436,26 @@ class ComposerStaticInit2f23f73bc0cc116b4b1eee1521aa8652
'League\\Uri\\UriTemplate\\Template' => __DIR__ . '/..' . '/league/uri/src/UriTemplate/Template.php',
'League\\Uri\\UriTemplate\\VarSpecifier' => __DIR__ . '/..' . '/league/uri/src/UriTemplate/VarSpecifier.php',
'League\\Uri\\UriTemplate\\VariableBag' => __DIR__ . '/..' . '/league/uri/src/UriTemplate/VariableBag.php',
+ 'Masterminds\\HTML5' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5.php',
+ 'Masterminds\\HTML5\\Elements' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/Elements.php',
+ 'Masterminds\\HTML5\\Entities' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/Entities.php',
+ 'Masterminds\\HTML5\\Exception' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/Exception.php',
+ 'Masterminds\\HTML5\\InstructionProcessor' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/InstructionProcessor.php',
+ 'Masterminds\\HTML5\\Parser\\CharacterReference' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/Parser/CharacterReference.php',
+ 'Masterminds\\HTML5\\Parser\\DOMTreeBuilder' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/Parser/DOMTreeBuilder.php',
+ 'Masterminds\\HTML5\\Parser\\EventHandler' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/Parser/EventHandler.php',
+ 'Masterminds\\HTML5\\Parser\\FileInputStream' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/Parser/FileInputStream.php',
+ 'Masterminds\\HTML5\\Parser\\InputStream' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/Parser/InputStream.php',
+ 'Masterminds\\HTML5\\Parser\\ParseError' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/Parser/ParseError.php',
+ 'Masterminds\\HTML5\\Parser\\Scanner' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/Parser/Scanner.php',
+ 'Masterminds\\HTML5\\Parser\\StringInputStream' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/Parser/StringInputStream.php',
+ 'Masterminds\\HTML5\\Parser\\Tokenizer' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/Parser/Tokenizer.php',
+ 'Masterminds\\HTML5\\Parser\\TreeBuildingRules' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/Parser/TreeBuildingRules.php',
+ 'Masterminds\\HTML5\\Parser\\UTF8Utils' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/Parser/UTF8Utils.php',
+ 'Masterminds\\HTML5\\Serializer\\HTML5Entities' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/Serializer/HTML5Entities.php',
+ 'Masterminds\\HTML5\\Serializer\\OutputRules' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/Serializer/OutputRules.php',
+ 'Masterminds\\HTML5\\Serializer\\RulesInterface' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/Serializer/RulesInterface.php',
+ 'Masterminds\\HTML5\\Serializer\\Traverser' => __DIR__ . '/..' . '/masterminds/html5/src/HTML5/Serializer/Traverser.php',
'Mexitek\\PHPColors\\Color' => __DIR__ . '/..' . '/mexitek/phpcolors/src/Mexitek/PHPColors/Color.php',
'MicrosoftAzure\\Storage\\Blob\\BlobRestProxy' => __DIR__ . '/..' . '/microsoft/azure-storage-blob/src/Blob/BlobRestProxy.php',
'MicrosoftAzure\\Storage\\Blob\\BlobSharedAccessSignatureHelper' => __DIR__ . '/..' . '/microsoft/azure-storage-blob/src/Blob/BlobSharedAccessSignatureHelper.php',
diff --git a/composer/installed.json b/composer/installed.json
index 34231e2a5..483266cda 100644
--- a/composer/installed.json
+++ b/composer/installed.json
@@ -181,6 +181,10 @@
"MIT"
],
"description": "Convenience wrapper around ini_get()",
+ "support": {
+ "issues": "https://github.com/bantuXorg/php-ini-get-wrapper/issues",
+ "source": "https://github.com/bantuXorg/php-ini-get-wrapper/tree/v1.0.1"
+ },
"install-path": "../bantu/ini-get-wrapper"
},
{
@@ -218,12 +222,12 @@
"type": "library",
"installation-source": "dist",
"autoload": {
- "psr-4": {
- "Assert\\": "lib/Assert"
- },
"files": [
"lib/Assert/functions.php"
- ]
+ ],
+ "psr-4": {
+ "Assert\\": "lib/Assert"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -1077,74 +1081,26 @@
"issues": "https://github.com/fgrosse/PHPASN1/issues",
"source": "https://github.com/fgrosse/PHPASN1/tree/v2.3.0"
},
+ "abandoned": true,
"install-path": "../fgrosse/phpasn1"
},
- {
- "name": "fusonic/linq",
- "version": "v1.1.0",
- "version_normalized": "1.1.0.0",
- "source": {
- "type": "git",
- "url": "https://github.com/fusonic/linq.git",
- "reference": "63520ef1470ca771acbd26871efb945dd4a7a5d8"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/fusonic/linq/zipball/63520ef1470ca771acbd26871efb945dd4a7a5d8",
- "reference": "63520ef1470ca771acbd26871efb945dd4a7a5d8",
- "shasum": ""
- },
- "require": {
- "php": ">=5.3.2"
- },
- "time": "2015-02-26T22:49:17+00:00",
- "type": "library",
- "installation-source": "dist",
- "autoload": {
- "psr-0": {
- "Fusonic\\Linq": "src/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Fusonic",
- "homepage": "http://www.fusonic.net"
- }
- ],
- "description": "LINQ 2 objects class for PHP",
- "homepage": "http://fusonic.github.io/fusonic-linq/",
- "keywords": [
- "linq",
- "linq2objects"
- ],
- "support": {
- "issues": "https://github.com/fusonic/linq/issues",
- "source": "https://github.com/fusonic/linq/tree/master"
- },
- "install-path": "../fusonic/linq"
- },
{
"name": "fusonic/opengraph",
- "version": "v2.2.0",
- "version_normalized": "2.2.0.0",
+ "version": "v2.3.0",
+ "version_normalized": "2.3.0.0",
"source": {
"type": "git",
"url": "https://github.com/fusonic/opengraph.git",
- "reference": "a63b588fbe56c175ae06e158f1513642653ee3c1"
+ "reference": "09dca70b04b4221c9dc664e1689e876a3faa50c2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/fusonic/opengraph/zipball/a63b588fbe56c175ae06e158f1513642653ee3c1",
- "reference": "a63b588fbe56c175ae06e158f1513642653ee3c1",
+ "url": "https://api.github.com/repos/fusonic/opengraph/zipball/09dca70b04b4221c9dc664e1689e876a3faa50c2",
+ "reference": "09dca70b04b4221c9dc664e1689e876a3faa50c2",
"shasum": ""
},
"require": {
"ext-dom": "*",
- "fusonic/linq": "^1.0",
"php": "^7.4|^8.0",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.0",
@@ -1160,7 +1116,7 @@
"nyholm/psr7": "^1.2",
"symfony/http-client": "^5.0"
},
- "time": "2022-01-20T05:47:36+00:00",
+ "time": "2023-12-05T14:26:51+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -1185,7 +1141,7 @@
],
"support": {
"issues": "https://github.com/fusonic/opengraph/issues",
- "source": "https://github.com/fusonic/opengraph/tree/v2.2.0"
+ "source": "https://github.com/fusonic/opengraph/tree/v2.3.0"
},
"install-path": "../fusonic/opengraph"
},
@@ -2128,6 +2084,76 @@
],
"install-path": "../league/uri-interfaces"
},
+ {
+ "name": "masterminds/html5",
+ "version": "2.9.0",
+ "version_normalized": "2.9.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Masterminds/html5-php.git",
+ "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
+ "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9"
+ },
+ "time": "2024-03-31T07:05:07+00:00",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.7-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Masterminds\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Matt Butcher",
+ "email": "technosophos@gmail.com"
+ },
+ {
+ "name": "Matt Farina",
+ "email": "matt@mattfarina.com"
+ },
+ {
+ "name": "Asmir Mustafic",
+ "email": "goetas@gmail.com"
+ }
+ ],
+ "description": "An HTML5 parser and serializer.",
+ "homepage": "http://masterminds.github.io/html5-php",
+ "keywords": [
+ "HTML5",
+ "dom",
+ "html",
+ "parser",
+ "querypath",
+ "serializer",
+ "xml"
+ ],
+ "support": {
+ "issues": "https://github.com/Masterminds/html5-php/issues",
+ "source": "https://github.com/Masterminds/html5-php/tree/2.9.0"
+ },
+ "install-path": "../masterminds/html5"
+ },
{
"name": "mexitek/phpcolors",
"version": "v1.0.4",
@@ -2481,6 +2507,10 @@
"log",
"normalizer"
],
+ "support": {
+ "issues": "https://github.com/nextcloud/lognormalizer/issues",
+ "source": "https://github.com/nextcloud/lognormalizer/tree/v1.0.0"
+ },
"install-path": "../nextcloud/lognormalizer"
},
{
@@ -3879,12 +3909,12 @@
},
"installation-source": "dist",
"autoload": {
- "psr-4": {
- "Ramsey\\Uuid\\": "src/"
- },
"files": [
"src/functions.php"
- ]
+ ],
+ "psr-4": {
+ "Ramsey\\Uuid\\": "src/"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -3906,6 +3936,10 @@
{
"url": "https://github.com/ramsey",
"type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/ramsey/uuid",
+ "type": "tidelift"
}
],
"install-path": "../ramsey/uuid"
@@ -4645,6 +4679,10 @@
}
],
"description": "Automatic BASH completion for Symfony Console Component based applications.",
+ "support": {
+ "issues": "https://github.com/stecman/symfony-console-completion/issues",
+ "source": "https://github.com/stecman/symfony-console-completion/tree/0.11.0"
+ },
"install-path": "../stecman/symfony-console-completion"
},
{
@@ -4751,24 +4789,23 @@
},
{
"name": "symfony/css-selector",
- "version": "v5.4.11",
- "version_normalized": "5.4.11.0",
+ "version": "v6.4.3",
+ "version_normalized": "6.4.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
- "reference": "c1681789f059ab756001052164726ae88512ae3d"
+ "reference": "ee0f7ed5cf298cc019431bb3b3977ebc52b86229"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/css-selector/zipball/c1681789f059ab756001052164726ae88512ae3d",
- "reference": "c1681789f059ab756001052164726ae88512ae3d",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/ee0f7ed5cf298cc019431bb3b3977ebc52b86229",
+ "reference": "ee0f7ed5cf298cc019431bb3b3977ebc52b86229",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
- "symfony/polyfill-php80": "^1.16"
+ "php": ">=8.1"
},
- "time": "2022-06-27T16:58:25+00:00",
+ "time": "2024-01-23T14:51:35+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -4800,7 +4837,7 @@
"description": "Converts CSS selectors to XPath expressions",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/css-selector/tree/v5.4.11"
+ "source": "https://github.com/symfony/css-selector/tree/v6.4.3"
},
"funding": [
{
@@ -4890,37 +4927,29 @@
},
{
"name": "symfony/dom-crawler",
- "version": "v5.4.11",
- "version_normalized": "5.4.11.0",
+ "version": "v6.4.4",
+ "version_normalized": "6.4.4.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/dom-crawler.git",
- "reference": "0b900ca5576ecd59e08c76127e616667cfe427a7"
+ "reference": "f0e7ec3fa17000e2d0cb4557b4b47c88a6a63531"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/0b900ca5576ecd59e08c76127e616667cfe427a7",
- "reference": "0b900ca5576ecd59e08c76127e616667cfe427a7",
+ "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/f0e7ec3fa17000e2d0cb4557b4b47c88a6a63531",
+ "reference": "f0e7ec3fa17000e2d0cb4557b4b47c88a6a63531",
"shasum": ""
},
"require": {
- "php": ">=7.2.5",
- "symfony/deprecation-contracts": "^2.1|^3",
+ "masterminds/html5": "^2.6",
+ "php": ">=8.1",
"symfony/polyfill-ctype": "~1.8",
- "symfony/polyfill-mbstring": "~1.0",
- "symfony/polyfill-php80": "^1.16"
- },
- "conflict": {
- "masterminds/html5": "<2.6"
+ "symfony/polyfill-mbstring": "~1.0"
},
"require-dev": {
- "masterminds/html5": "^2.6",
- "symfony/css-selector": "^4.4|^5.0|^6.0"
+ "symfony/css-selector": "^5.4|^6.0|^7.0"
},
- "suggest": {
- "symfony/css-selector": ""
- },
- "time": "2022-06-27T16:58:25+00:00",
+ "time": "2024-02-07T09:17:57+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@@ -4948,7 +4977,7 @@
"description": "Eases DOM navigation for HTML and XML documents",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/dom-crawler/tree/v5.4.11"
+ "source": "https://github.com/symfony/dom-crawler/tree/v6.4.4"
},
"funding": [
{
@@ -5383,17 +5412,17 @@
},
{
"name": "symfony/polyfill-ctype",
- "version": "v1.28.0",
- "version_normalized": "1.28.0.0",
+ "version": "v1.29.0",
+ "version_normalized": "1.29.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
- "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb"
+ "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
- "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4",
+ "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4",
"shasum": ""
},
"require": {
@@ -5405,12 +5434,9 @@
"suggest": {
"ext-ctype": "For best performance"
},
- "time": "2023-01-26T09:26:14+00:00",
+ "time": "2024-01-29T20:11:03+00:00",
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.28-dev"
- },
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@@ -5448,7 +5474,7 @@
"portable"
],
"support": {
- "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0"
+ "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0"
},
"funding": [
{
@@ -5729,17 +5755,17 @@
},
{
"name": "symfony/polyfill-mbstring",
- "version": "v1.28.0",
- "version_normalized": "1.28.0.0",
+ "version": "v1.29.0",
+ "version_normalized": "1.29.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
- "reference": "42292d99c55abe617799667f454222c54c60e229"
+ "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229",
- "reference": "42292d99c55abe617799667f454222c54c60e229",
+ "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
+ "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec",
"shasum": ""
},
"require": {
@@ -5751,12 +5777,9 @@
"suggest": {
"ext-mbstring": "For best performance"
},
- "time": "2023-07-28T09:04:16+00:00",
+ "time": "2024-01-29T20:11:03+00:00",
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.28-dev"
- },
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@@ -5795,7 +5818,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0"
+ "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0"
},
"funding": [
{
@@ -5976,28 +5999,25 @@
},
{
"name": "symfony/polyfill-php80",
- "version": "v1.28.0",
- "version_normalized": "1.28.0.0",
+ "version": "v1.29.0",
+ "version_normalized": "1.29.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
- "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5"
+ "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
- "reference": "6caa57379c4aec19c0a12a38b59b26487dcfe4b5",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
+ "reference": "87b68208d5c1188808dd7839ee1e6c8ec3b02f1b",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
- "time": "2023-01-26T09:26:14+00:00",
+ "time": "2024-01-29T20:11:03+00:00",
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.28-dev"
- },
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
@@ -6042,7 +6062,7 @@
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php80/tree/v1.28.0"
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.29.0"
},
"funding": [
{
@@ -6604,13 +6624,6 @@
},
"installation-source": "dist",
"autoload": {
- "psr-4": {
- "Safe\\": [
- "lib/",
- "deprecated/",
- "generated/"
- ]
- },
"files": [
"deprecated/apc.php",
"deprecated/libevent.php",
@@ -6701,7 +6714,14 @@
"generated/yaz.php",
"generated/zip.php",
"generated/zlib.php"
- ]
+ ],
+ "psr-4": {
+ "Safe\\": [
+ "lib/",
+ "deprecated/",
+ "generated/"
+ ]
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
diff --git a/composer/installed.php b/composer/installed.php
index 02cd6d6f0..abef2c443 100644
--- a/composer/installed.php
+++ b/composer/installed.php
@@ -3,7 +3,7 @@
'name' => 'nextcloud/3rdparty',
'pretty_version' => 'dev-master',
'version' => 'dev-master',
- 'reference' => 'fe17c4147f7aa5ae661c99e6a52a5cf5e1a994bb',
+ 'reference' => '35894785112a2404cadb68d40a52158c76791b7c',
'type' => 'library',
'install_path' => __DIR__ . '/../',
'aliases' => array(),
@@ -145,19 +145,10 @@
'aliases' => array(),
'dev_requirement' => false,
),
- 'fusonic/linq' => array(
- 'pretty_version' => 'v1.1.0',
- 'version' => '1.1.0.0',
- 'reference' => '63520ef1470ca771acbd26871efb945dd4a7a5d8',
- 'type' => 'library',
- 'install_path' => __DIR__ . '/../fusonic/linq',
- 'aliases' => array(),
- 'dev_requirement' => false,
- ),
'fusonic/opengraph' => array(
- 'pretty_version' => 'v2.2.0',
- 'version' => '2.2.0.0',
- 'reference' => 'a63b588fbe56c175ae06e158f1513642653ee3c1',
+ 'pretty_version' => 'v2.3.0',
+ 'version' => '2.3.0.0',
+ 'reference' => '09dca70b04b4221c9dc664e1689e876a3faa50c2',
'type' => 'library',
'install_path' => __DIR__ . '/../fusonic/opengraph',
'aliases' => array(),
@@ -271,6 +262,15 @@
'aliases' => array(),
'dev_requirement' => false,
),
+ 'masterminds/html5' => array(
+ 'pretty_version' => '2.9.0',
+ 'version' => '2.9.0.0',
+ 'reference' => 'f5ac2c0b0a2eefca70b2ce32a5809992227e75a6',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../masterminds/html5',
+ 'aliases' => array(),
+ 'dev_requirement' => false,
+ ),
'mexitek/phpcolors' => array(
'pretty_version' => 'v1.0.4',
'version' => '1.0.4.0',
@@ -319,7 +319,7 @@
'nextcloud/3rdparty' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
- 'reference' => 'fe17c4147f7aa5ae661c99e6a52a5cf5e1a994bb',
+ 'reference' => '35894785112a2404cadb68d40a52158c76791b7c',
'type' => 'library',
'install_path' => __DIR__ . '/../',
'aliases' => array(),
@@ -698,9 +698,9 @@
'dev_requirement' => false,
),
'symfony/css-selector' => array(
- 'pretty_version' => 'v5.4.11',
- 'version' => '5.4.11.0',
- 'reference' => 'c1681789f059ab756001052164726ae88512ae3d',
+ 'pretty_version' => 'v6.4.3',
+ 'version' => '6.4.3.0',
+ 'reference' => 'ee0f7ed5cf298cc019431bb3b3977ebc52b86229',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/css-selector',
'aliases' => array(),
@@ -716,9 +716,9 @@
'dev_requirement' => false,
),
'symfony/dom-crawler' => array(
- 'pretty_version' => 'v5.4.11',
- 'version' => '5.4.11.0',
- 'reference' => '0b900ca5576ecd59e08c76127e616667cfe427a7',
+ 'pretty_version' => 'v6.4.4',
+ 'version' => '6.4.4.0',
+ 'reference' => 'f0e7ec3fa17000e2d0cb4557b4b47c88a6a63531',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/dom-crawler',
'aliases' => array(),
@@ -776,9 +776,9 @@
'dev_requirement' => false,
),
'symfony/polyfill-ctype' => array(
- 'pretty_version' => 'v1.28.0',
- 'version' => '1.28.0.0',
- 'reference' => 'ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb',
+ 'pretty_version' => 'v1.29.0',
+ 'version' => '1.29.0.0',
+ 'reference' => 'ef4d7e442ca910c4764bce785146269b30cb5fc4',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-ctype',
'aliases' => array(),
@@ -812,9 +812,9 @@
'dev_requirement' => false,
),
'symfony/polyfill-mbstring' => array(
- 'pretty_version' => 'v1.28.0',
- 'version' => '1.28.0.0',
- 'reference' => '42292d99c55abe617799667f454222c54c60e229',
+ 'pretty_version' => 'v1.29.0',
+ 'version' => '1.29.0.0',
+ 'reference' => '9773676c8a1bb1f8d4340a62efe641cf76eda7ec',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
'aliases' => array(),
@@ -839,9 +839,9 @@
'dev_requirement' => false,
),
'symfony/polyfill-php80' => array(
- 'pretty_version' => 'v1.28.0',
- 'version' => '1.28.0.0',
- 'reference' => '6caa57379c4aec19c0a12a38b59b26487dcfe4b5',
+ 'pretty_version' => 'v1.29.0',
+ 'version' => '1.29.0.0',
+ 'reference' => '87b68208d5c1188808dd7839ee1e6c8ec3b02f1b',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
'aliases' => array(),
diff --git a/fusonic/linq/LICENSE b/fusonic/linq/LICENSE
deleted file mode 100644
index 1617dcba4..000000000
--- a/fusonic/linq/LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) 2014 Fusonic GmbH (http://www.fusonic.net)
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/fusonic/linq/src/Fusonic/Linq/GroupedLinq.php b/fusonic/linq/src/Fusonic/Linq/GroupedLinq.php
deleted file mode 100644
index 2a2bb5107..000000000
--- a/fusonic/linq/src/Fusonic/Linq/GroupedLinq.php
+++ /dev/null
@@ -1,35 +0,0 @@
-groupKey = $groupKey;
- }
-
- public function key()
- {
- return $this->groupKey;
- }
-}
\ No newline at end of file
diff --git a/fusonic/linq/src/Fusonic/Linq/Helper/LinqHelper.php b/fusonic/linq/src/Fusonic/Linq/Helper/LinqHelper.php
deleted file mode 100644
index f3e8c9aea..000000000
--- a/fusonic/linq/src/Fusonic/Linq/Helper/LinqHelper.php
+++ /dev/null
@@ -1,67 +0,0 @@
-getIterator();
- }
- else if($value instanceof \Iterator) {
- return $value;
- }
-
- throw new \UnexpectedValueException("Value must be an array, or implement either the \IteratorAggregate or \Iterator interface");
- }
-
- public static function isIterable($param)
- {
- return is_array($param)
- || $param instanceof \IteratorAggregate
- || $param instanceof \Iterator;
- }
-}
\ No newline at end of file
diff --git a/fusonic/linq/src/Fusonic/Linq/Iterator/DistinctIterator.php b/fusonic/linq/src/Fusonic/Linq/Iterator/DistinctIterator.php
deleted file mode 100644
index 7651fbfd3..000000000
--- a/fusonic/linq/src/Fusonic/Linq/Iterator/DistinctIterator.php
+++ /dev/null
@@ -1,62 +0,0 @@
-iterator = $iterator;
- }
-
- public function current()
- {
- return $this->distinct->current();
- }
-
- public function next()
- {
- $this->distinct->next();
- }
-
- public function key()
- {
- return $this->distinct->key();
- }
-
- public function valid()
- {
- return $this->distinct->valid();
- }
-
- public function rewind()
- {
- if ($this->distinct === null) {
- $this->getDistincts();
- }
-
- $this->distinct->rewind();
- }
-
- private function getDistincts()
- {
- $data = iterator_to_array($this->iterator);
- $distinct = array_unique($data);
- $this->distinct = new \ArrayIterator($distinct);
- }
-}
diff --git a/fusonic/linq/src/Fusonic/Linq/Iterator/ExceptIterator.php b/fusonic/linq/src/Fusonic/Linq/Iterator/ExceptIterator.php
deleted file mode 100644
index 18d3c2ea0..000000000
--- a/fusonic/linq/src/Fusonic/Linq/Iterator/ExceptIterator.php
+++ /dev/null
@@ -1,65 +0,0 @@
-first = $first;
- $this->second = $second;
- }
-
- public function current()
- {
- return $this->result->current();
- }
-
- public function next()
- {
- $this->result->next();
- }
-
- public function key()
- {
- return $this->result->key();
- }
-
- public function valid()
- {
- return $this->result->valid();
- }
-
- public function rewind()
- {
- if ($this->result === null) {
- $this->getResult();
- }
-
- $this->result->rewind();
- }
-
- private function getResult()
- {
- $firstArray = iterator_to_array($this->first);
- $secondArray = iterator_to_array($this->second);
- $this->result = new ArrayIterator(array_diff($firstArray, $secondArray));
- }
-}
diff --git a/fusonic/linq/src/Fusonic/Linq/Iterator/GroupIterator.php b/fusonic/linq/src/Fusonic/Linq/Iterator/GroupIterator.php
deleted file mode 100644
index 0b2501790..000000000
--- a/fusonic/linq/src/Fusonic/Linq/Iterator/GroupIterator.php
+++ /dev/null
@@ -1,74 +0,0 @@
-iterator = $iterator;
- $this->keySelector = $keySelector;
- }
-
- public function current()
- {
- $current = $this->grouped->current();
- return new GroupedLinq($current['key'], new \ArrayIterator($current['values']));
- }
-
- public function next()
- {
- $this->grouped->next();
- }
-
- public function key()
- {
- return $this->grouped->key();
- }
-
- public function valid()
- {
- return $this->grouped->valid();
- }
-
- public function rewind()
- {
- if ($this->grouped === null) {
- $this->doGroup();
- }
-
- $this->grouped->rewind();
- }
-
- private function doGroup()
- {
- $keySelector = $this->keySelector;
- $this->grouped = new \ArrayIterator(array());
- foreach ($this->iterator as $value) {
- $key = $keySelector($value);
- if (!isset($this->grouped[$key])) {
- $this->grouped[$key] = array('key' => $key, 'values'=> array());
- }
-
- $this->grouped[$key]['values'][] = $value;
- }
- }
-}
diff --git a/fusonic/linq/src/Fusonic/Linq/Iterator/IntersectIterator.php b/fusonic/linq/src/Fusonic/Linq/Iterator/IntersectIterator.php
deleted file mode 100644
index d08c2efe1..000000000
--- a/fusonic/linq/src/Fusonic/Linq/Iterator/IntersectIterator.php
+++ /dev/null
@@ -1,65 +0,0 @@
-first = $first;
- $this->second = $second;
- }
-
- public function current()
- {
- return $this->intersections->current();
- }
-
- public function next()
- {
- $this->intersections->next();
- }
-
- public function key()
- {
- return $this->intersections->key();
- }
-
- public function valid()
- {
- return $this->intersections->valid();
- }
-
- public function rewind()
- {
- if ($this->intersections === null) {
- $this->calcIntersections();
- }
-
- $this->intersections->rewind();
- }
-
- private function calcIntersections()
- {
- $firstArray = iterator_to_array($this->first);
- $secondArray = iterator_to_array($this->second);
- $this->intersections = new ArrayIterator(array_intersect($firstArray, $secondArray));
- }
-}
diff --git a/fusonic/linq/src/Fusonic/Linq/Iterator/OfTypeIterator.php b/fusonic/linq/src/Fusonic/Linq/Iterator/OfTypeIterator.php
deleted file mode 100644
index 46a59482f..000000000
--- a/fusonic/linq/src/Fusonic/Linq/Iterator/OfTypeIterator.php
+++ /dev/null
@@ -1,94 +0,0 @@
-
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Fusonic\Linq\Iterator;
-
-use FilterIterator;
-use Fusonic\Linq\Helper;
-use Iterator;
-
-/**
- * Iterator for filtering the Linq query with a specified type.
- * @package Fusonic\Linq\Iterator
- */
-final class OfTypeIterator
- extends
- \FilterIterator
-{
- /**
- * @var callable $acceptCallback
- */
- private $acceptCallback;
-
- /**
- * Initializes an instance of OfTypeIterator.
- *
- * @param Iterator $iterator
- * @param string $type
- */
- public function __construct(Iterator $iterator, $type)
- {
- parent::__construct($iterator);
-
- switch (strtolower($type))
- {
- case 'int':
- case 'integer':
- $this->acceptCallback = function ($current)
- {
- return is_int($current);
- };
- break;
- case 'float':
- case 'double':
- $this->acceptCallback = function ($current)
- {
- return is_float($current);
- };
- break;
- case 'string':
- $this->acceptCallback = function ($current)
- {
- return is_string($current);
- };
- break;
- case 'bool':
- case 'boolean':
- $this->acceptCallback = function ($current)
- {
- return is_bool($current);
- };
- break;
-
- default:
- $this->acceptCallback = function ($current) use ($type)
- {
- return $current instanceof $type;
- };
- }
- }
-
- /**
- * (PHP 5 >= 5.1.0)
- * Check whether the current element of the iterator is acceptable
- * @link http://php.net/manual/en/filteriterator.accept.php
- * @return bool true if the current element is acceptable, otherwise false.
- */
- public function accept()
- {
- /** @var mixed $current */
- $current = $this->current();
- /** @var callable $func */
- $func = $this->acceptCallback;
-
- return $func($current);
- }
-}
\ No newline at end of file
diff --git a/fusonic/linq/src/Fusonic/Linq/Iterator/OrderIterator.php b/fusonic/linq/src/Fusonic/Linq/Iterator/OrderIterator.php
deleted file mode 100644
index 732e79eb2..000000000
--- a/fusonic/linq/src/Fusonic/Linq/Iterator/OrderIterator.php
+++ /dev/null
@@ -1,113 +0,0 @@
-iterator = $items;
- $this->direction = $direction;
- $this->orderKeyFunc = $orderKeyFunc;
- }
-
- public function current()
- {
- return $this->orderedIterator->current();
- }
-
- public function next()
- {
- $this->orderedIterator->next();
- }
-
- public function key()
- {
- return $this->orderedIterator->key();
- }
-
- public function valid()
- {
- return $this->orderedIterator->valid();
- }
-
- public function rewind()
- {
- if ($this->orderedIterator == null) {
- $this->orderItems();
- }
- $this->orderedIterator->rewind();
- }
-
- public function orderItems()
- {
- $orderKeyFunc = $this->orderKeyFunc;
- $direction = $this->direction;
-
- $itemIterator = $this->iterator;
- $itemIterator->rewind();
- if (!$itemIterator->valid()) {
- $this->orderedIterator = new ArrayIterator();
- return;
- }
-
- $firstOrderKey = $orderKeyFunc($itemIterator->current());
-
- $sortType = Helper\LinqHelper::LINQ_ORDER_TYPE_NUMERIC;
-
- if ($firstOrderKey instanceof \DateTime) {
- $sortType = Helper\LinqHelper::LINQ_ORDER_TYPE_DATETIME;
- } elseif (!is_numeric($firstOrderKey)) {
- $sortType = Helper\LinqHelper::LINQ_ORDER_TYPE_ALPHANUMERIC;
- }
-
- $keyMap = array();
- $valueMap = array();
-
- foreach ($itemIterator as $value) {
- $orderKey = $orderKeyFunc != null ? $orderKeyFunc($value) : $value;
- if ($sortType == Helper\LinqHelper::LINQ_ORDER_TYPE_DATETIME) {
- $orderKey = $orderKey->getTimeStamp();
- }
- $keyMap[] = $orderKey;
- $valueMap[] = $value;
- }
-
- if ($sortType == Helper\LinqHelper::LINQ_ORDER_TYPE_DATETIME) {
- $sortType = Helper\LinqHelper::LINQ_ORDER_TYPE_NUMERIC;
- }
-
- if ($direction == Helper\LinqHelper::LINQ_ORDER_ASC) {
- asort($keyMap, $sortType == Helper\LinqHelper::LINQ_ORDER_TYPE_NUMERIC ? SORT_NUMERIC : SORT_LOCALE_STRING);
- } else {
- arsort($keyMap, $sortType == Helper\LinqHelper::LINQ_ORDER_TYPE_NUMERIC ? SORT_NUMERIC : SORT_LOCALE_STRING);
- }
-
- $sorted = new ArrayIterator(array());
- foreach ($keyMap as $key => $value) {
- $sorted[] = $valueMap[$key];
- }
-
- $this->orderedIterator = $sorted;
- }
-}
diff --git a/fusonic/linq/src/Fusonic/Linq/Iterator/SelectIterator.php b/fusonic/linq/src/Fusonic/Linq/Iterator/SelectIterator.php
deleted file mode 100644
index aa698b3d2..000000000
--- a/fusonic/linq/src/Fusonic/Linq/Iterator/SelectIterator.php
+++ /dev/null
@@ -1,37 +0,0 @@
-selector = $selector;
- }
-
- public function current()
- {
- $selector = $this->selector;
- return $selector(parent::current());
- }
-}
diff --git a/fusonic/linq/src/Fusonic/Linq/Iterator/SelectManyIterator.php b/fusonic/linq/src/Fusonic/Linq/Iterator/SelectManyIterator.php
deleted file mode 100644
index 310b8bdb6..000000000
--- a/fusonic/linq/src/Fusonic/Linq/Iterator/SelectManyIterator.php
+++ /dev/null
@@ -1,84 +0,0 @@
-iterator = $iterator;
- }
-
- public function current()
- {
- if ($this->currentIterator != null) {
- return $this->currentIterator->current();
- }
-
- return null;
- }
-
- public function next()
- {
- if ($this->currentIterator != null) {
- $this->currentIterator->next();
-
- if (!$this->currentIterator->valid()) {
- $this->iterator->next();
- if ($this->iterator->valid()) {
- $this->currentIterator = Helper\LinqHelper::getIteratorOrThrow($this->iterator->current());
- if ($this->currentIterator != null) {
- $this->currentIterator->rewind();
- $this->key++;
- }
- }
- } else {
- $this->key++;
- }
- }
- }
-
- public function key()
- {
- return $this->key;
- }
-
- public function valid()
- {
- $current = $this->currentIterator;
- return $current != null && $current->valid();
- }
-
- public function rewind()
- {
- $this->iterator->rewind();
- if ($this->iterator->valid()) {
- $current = $this->iterator->current();
- $this->currentIterator = Helper\LinqHelper::getIteratorOrThrow($current);
- if ($this->currentIterator != null) {
- $this->currentIterator->rewind();
- }
- } else {
- $this->currentIterator = null;
- }
-
- $this->key = 0;
- }
-}
\ No newline at end of file
diff --git a/fusonic/linq/src/Fusonic/Linq/Iterator/WhereIterator.php b/fusonic/linq/src/Fusonic/Linq/Iterator/WhereIterator.php
deleted file mode 100644
index 1a757e008..000000000
--- a/fusonic/linq/src/Fusonic/Linq/Iterator/WhereIterator.php
+++ /dev/null
@@ -1,34 +0,0 @@
-func = $func;
- }
-
- public function accept()
- {
- $func = $this->func;
- $current = $this->current();
- return Helper\LinqHelper::getBoolOrThrowException($func($current));
- }
-}
\ No newline at end of file
diff --git a/fusonic/linq/src/Fusonic/Linq/Linq.php b/fusonic/linq/src/Fusonic/Linq/Linq.php
deleted file mode 100644
index f9a568105..000000000
--- a/fusonic/linq/src/Fusonic/Linq/Linq.php
+++ /dev/null
@@ -1,732 +0,0 @@
-.
- */
-class Linq implements IteratorAggregate, Countable
-{
- private $iterator;
-
- /**
- * Creates a new Linq object using the provided dataSource.
- *
- * @param array|\Iterator|IteratorAggregate $dataSource A Traversable sequence as data source.
- */
- public function __construct($dataSource)
- {
- LinqHelper::assertArgumentIsIterable($dataSource, "dataSource");
- $dataSource = LinqHelper::getIteratorOrThrow($dataSource);
-
- $this->iterator = $dataSource;
- }
-
- /**
- * Creates a new Linq object using the provided dataDataSource.
- * This is the recommended way for getting a new Linq instance.
- *
- * @param array|\Iterator|IteratorAggregate $dataSource A Traversable sequence as data source.
- * @return Linq
- */
- public static function from($dataSource)
- {
- return new Linq($dataSource);
- }
-
- /**
- * Generates a sequence of integral numbers within a specified range.
- *
- * @param $start The value of the first integer in the sequence.
- * @param $count The number of sequential integers to generate.
- * @return Linq An sequence that contains a range of sequential int numbers.
- * @throws \OutOfRangeException
- */
- public static function range($start, $count)
- {
- if ($count < 0) {
- throw new OutOfRangeException('$count must be not be negative.');
- }
-
- return new Linq(range($start, $start + $count - 1));
- }
-
- /**
- * Filters the Linq object according to func return result.
- *
- * @param callback $func A func that returns boolean
- * @return Linq Filtered results according to $func
- */
- public function where($func)
- {
- return new Linq(new WhereIterator($this->iterator, $func));
- }
-
- /**
- * Filters the Linq object according to type.
- *
- * @param string $type
- *
- * @return Linq Filtered results according to $func
- */
- public function ofType($type)
- {
- return new Linq(new OfTypeIterator($this->iterator, $type));
- }
-
- /**
- * Bypasses a specified number of elements and then returns the remaining elements.
- *
- * @param int $count The number of elements to skip before returning the remaining elements.
- * @return Linq A Linq object that contains the elements that occur after the specified index.
- */
- public function skip($count)
- {
- // If its an array iterator we must check the arrays bounds are greater than the skip count.
- // This is because the LimitIterator will use the seek() method which will throw an exception if $count > array.bounds.
- $innerIterator = $this->iterator;
- if ($innerIterator instanceof \ArrayIterator) {
- if ($count >= $innerIterator->count()) {
- return new Linq(array());
- }
- }
-
- return new Linq(new \LimitIterator($innerIterator, $count, -1));
- }
-
- /**
- * Returns a specified number of contiguous elements from the start of a sequence
- *
- * @param int $count The number of elements to return.
- * @return Linq A Linq object that contains the specified number of elements from the start.
- */
- public function take($count)
- {
- if ($count == 0) {
- return new Linq(array());
- }
-
- return new Linq(new \LimitIterator($this->iterator, 0, $count));
- }
-
- /**
- * Applies an accumulator function over a sequence.
- * The aggregate method makes it simple to perform a calculation over a sequence of values.
- * This method works by calling $func one time for each element.
- * The first element of source is used as the initial aggregate value if $seed parameter is not specified.
- * If $seed is specified, this value will be used as the first value.
- *
- * @param callback $func An accumulator function to be invoked on each element.
- * @param mixed $seed
- * @throws \RuntimeException if the input sequence contains no elements.
- * @return mixed Returns the final result of $func.
- */
- public function aggregate($func, $seed = null)
- {
- $result = null;
- $first = true;
-
- if ($seed !== null) {
- $result = $seed;
- $first = false;
- }
-
- foreach ($this->iterator as $current) {
- if (!$first) {
- $result = $func($result, $current);
- } else {
- $result = $current;
- $first = false;
- }
- }
- if ($first) {
- throw new \RuntimeException("The input sequence contains no elements.");
- }
- return $result;
- }
-
- /**
- * Splits the sequence in chunks according to $chunksize.
- *
- * @param $chunksize Specifies how many elements are grouped together per chunk.
- * @throws \InvalidArgumentException
- * @return Linq
- */
- public function chunk($chunksize)
- {
- if ($chunksize < 1) {
- throw new \InvalidArgumentException("chunksize", $chunksize);
- }
-
- $i = -1;
- return $this->select(
- function ($x) use (&$i) {
- $i++;
- return array("index" => $i, "value" => $x);
- }
- )
- ->groupBy(
- function ($pair) use ($chunksize) {
- return $pair["index"] / $chunksize;
- }
- )
- ->select(
- function (GroupedLinq $group) {
- return $group->select(
- function ($v) {
- return $v["value"];
- }
- );
- }
- );
- }
-
- /**
- * Determines whether all elements satisfy a condition.
- *
- * @param callback $func A function to test each element for a condition.
- * @return bool True if every element passes the test in the specified func, or if the sequence is empty; otherwise, false.
- */
- public function all($func)
- {
- foreach ($this->iterator as $current) {
- $match = LinqHelper::getBoolOrThrowException($func($current));
- if (!$match) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * Determines whether any element exists or satisfies a condition by invoking $func.
- *
- * @param callback $func A function to test each element for a condition or NULL to determine if any element exists.
- * @return bool True if no $func given and the source sequence contains any elements or True if any elements passed the test in the specified func; otherwise, false.
- */
- public function any($func = null)
- {
- foreach ($this->iterator as $current) {
- if ($func === null) {
- return true;
- }
-
- $match = LinqHelper::getBoolOrThrowException($func($current));
- if ($match) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Counts the elements of this Linq sequence.
- * @return int
- */
- public function count()
- {
- if ($this->iterator instanceof Countable) {
- return $this->iterator->count();
- }
-
- return iterator_count($this->iterator);
- }
-
- /**
- * Computes the average of all numeric values. Uses $func to obtain the value on each element.
- *
- * @param callback $func A func that returns any numeric type (int, float etc.)
- * @throws \UnexpectedValueException if an item of the sequence is not a numeric value.
- * @return double Average of items
- */
- public function average($func = null)
- {
- $resultTotal = 0;
- $itemCount = 0;
-
- $source = $this->getSelectIteratorOrInnerIterator($func);
-
- foreach ($source as $item) {
- if (!is_numeric($item)) {
- throw new UnexpectedValueException("Cannot calculate an average on a none numeric value");
- }
-
- $resultTotal += $item;
- $itemCount++;
- }
- return $itemCount == 0 ? 0 : ($resultTotal / $itemCount);
- }
-
- /**
- * Sorts the elements in ascending order according to a key provided by $func.
- *
- * @param callback $func A function to extract a key from an element.
- * @return Linq A new Linq instance whose elements are sorted ascending according to a key.
- */
- public function orderBy($func)
- {
- return $this->order($func, LinqHelper::LINQ_ORDER_ASC);
- }
-
- /**
- * Sorts the elements in descending order according to a key provided by $func.
- *
- * @param callback $func A function to extract a key from an element.
- * @return Linq A new Linq instance whose elements are sorted descending according to a key.
- */
- public function orderByDescending($func)
- {
- return $this->order($func, LinqHelper::LINQ_ORDER_DESC);
- }
-
- private function order($func, $direction = LinqHelper::LINQ_ORDER_ASC)
- {
- return new Linq(new OrderIterator($this->iterator, $func, $direction));
- }
-
- /**
- * Gets the sum of all items or by invoking a transform function on each item to get a numeric value.
- *
- * @param callback $func A func that returns any numeric type (int, float etc.) from the given element, or NULL to use the element itself.
- * @throws \UnexpectedValueException if any element is not a numeric value.
- * @return double The sum of all items.
- */
- public function sum($func = null)
- {
- $sum = 0;
- $iterator = $this->getSelectIteratorOrInnerIterator($func);
- foreach ($iterator as $value) {
- if (!is_numeric($value)) {
- throw new UnexpectedValueException("sum() only works on numeric values.");
- }
-
- $sum += $value;
- }
- return $sum;
- }
-
- /**
- * Gets the minimum item value of all items or by invoking a transform function on each item to get a numeric value.
- *
- * @param callback $func A func that returns any numeric type (int, float etc.) from the given element, or NULL to use the element itself.
- * @throws \RuntimeException if the sequence contains no elements
- * @throws \UnexpectedValueException
- * @return double Minimum item value
- */
- public function min($func = null)
- {
- $min = null;
- $iterator = $this->getSelectIteratorOrInnerIterator($func);
- foreach ($iterator as $value) {
- if (!is_numeric($value) && !is_string($value) && !($value instanceof \DateTime)) {
- throw new UnexpectedValueException("min() only works on numeric values, strings and DateTime objects.");
- }
-
- if (is_null($min)) {
- $min = $value;
- } elseif ($min > $value) {
- $min = $value;
- }
- }
-
- if ($min === null) {
- throw new \RuntimeException("Cannot calculate min() as the Linq sequence contains no elements.");
- }
-
- return $min;
- }
-
- /**
- * Returns the maximum item value according to $func
- *
- * @param callback $func A func that returns any numeric type (int, float etc.)
- * @throws \RuntimeException if the sequence contains no elements
- * @throws \UnexpectedValueException if any element is not a numeric value or a string.
- * @return double Maximum item value
- */
- public function max($func = null)
- {
- $max = null;
- $iterator = $this->getSelectIteratorOrInnerIterator($func);
- foreach ($iterator as $value) {
- if (!is_numeric($value) && !is_string($value) && !($value instanceof \DateTime)) {
- throw new UnexpectedValueException("max() only works on numeric values, strings and DateTime objects.");
- }
-
- if (is_null($max)) {
- $max = $value;
- } elseif ($max < $value) {
- $max = $value;
- }
- }
-
- if ($max === null) {
- throw new \RuntimeException("Cannot calculate max() as the Linq sequence contains no elements.");
- }
-
- return $max;
- }
-
- /**
- * Projects each element into a new form by invoking the selector function.
- *
- * @param callback $func A transform function to apply to each element.
- * @return Linq A new Linq object whose elements are the result of invoking the transform function on each element of the original Linq object.
- */
- public function select($func)
- {
- return new Linq(new SelectIterator($this->iterator, $func));
- }
-
- /**
- * Projects each element of a sequence to a new Linq and flattens the resulting sequences into one sequence.
- *
- * @param callback $func A func that returns a sequence (array, Linq, Iterator).
- * @throws \UnexpectedValueException if an element is not a traversable sequence.
- * @return Linq A new Linq object whose elements are the result of invoking the one-to-many transform function on each element of the input sequence.
- */
- public function selectMany($func)
- {
- return new Linq(new SelectManyIterator(new SelectIterator($this->iterator, $func)));
- }
-
- /**
- * Performs the specified action on each element of the Linq sequence and returns the Linq sequence.
- * @param callback $func A func that will be evaluated for each item in the linq sequence.
- * @return Linq The original Linq sequence that was used to perform the foreach.
- */
- public function each($func)
- {
- foreach ($this->iterator as $item) {
- $func($item);
- }
- return $this;
- }
-
- /**
- * Determines whether a sequence contains a specified element.
- * This function will use php strict comparison (===). If you need custom comparison use the Linq::any($func) method.
- *
- * @param mixed $value The value to locate in the sequence.
- * @return bool True if $value is found within the sequence; otherwise false.
- */
- public function contains($value)
- {
- return $this->any(
- function ($x) use ($value) {
- return $x === $value;
- }
- );
- }
-
- /**
- * Concatenates this Linq object with the given sequence.
- *
- * @param array|\Iterator $second A sequence which will be concatenated with this Linq object.
- * @throws InvalidArgumentException if the given sequence is not traversable.
- * @return Linq A new Linq object that contains the concatenated elements of the input sequence and the original Linq sequence.
- */
- public function concat($second)
- {
- LinqHelper::assertArgumentIsIterable($second, "second");
-
- $allItems = new \ArrayIterator(array($this->iterator, $second));
-
- return new Linq(new SelectManyIterator($allItems));
- }
-
- /**
- * Returns distinct item values of this
- *
- * @param callback $func
- * @return Linq Distinct item values of this
- */
- public function distinct($func = null)
- {
- return new Linq(new DistinctIterator($this->getSelectIteratorOrInnerIterator($func)));
- }
-
- /**
- * Intersects the Linq sequence with second Iterable sequence.
- *
- * @param \Iterator|array An iterator to intersect with:
- * @return Linq intersected items
- */
- public function intersect($second)
- {
- LinqHelper::assertArgumentIsIterable($second, "second");
- return new Linq(new IntersectIterator($this->iterator, LinqHelper::getIteratorOrThrow($second)));
- }
-
- /**
- * Returns all elements except the ones of the given sequence.
- *
- * @param array|\Iterator $second
- * @return Linq Returns all items of this not occuring in $second
- */
- public function except($second)
- {
- LinqHelper::assertArgumentIsIterable($second, "second");
- return new Linq(new ExceptIterator($this->iterator, LinqHelper::getIteratorOrThrow($second)));
- }
-
- /**
- * Returns the element at a specified index.
- * This method throws an exception if index is out of range.
- * To instead return NULL when the specified index is out of range, use the elementAtOrNull method.
- *
- * @throws \OutOfRangeException if index is less than 0 or greater than or equal to the number of elements in the sequence.
- * @param int $index
- * @return mixed Item at $index
- */
- public function elementAt($index)
- {
- return $this->getValueAt($index, true);
- }
-
- /**
- * Returns the element at a specified index or NULL if the index is out of range.
- *
- * @param $index
- * @return mixed Item at $index
- */
- public function elementAtOrNull($index)
- {
- return $this->getValueAt($index, false);
- }
-
- private function getValueAt($index, $throwEx)
- {
- $i = 0;
- foreach ($this->iterator as $value) {
- if ($i == $index) {
- return $value;
- }
- $i++;
- }
-
- if ($throwEx) {
- throw new OutOfRangeException("Index is less than 0 or greater than or equal to the number of elements in the sequence.");
- }
-
- return null;
- }
-
- /**
- * Groups the object according to the $func generated key
- *
- * @param callback $keySelector a func that returns an item as key, item can be any type.
- * @return GroupedLinq
- */
- public function groupBy($keySelector)
- {
- return new Linq(new GroupIterator($this->iterator, $keySelector));
- }
-
- /**
- * Returns the last element that satisfies a specified condition.
- * @throws \RuntimeException if no element satisfies the condition in predicate or the source sequence is empty.
- *
- * @param callback $func a func that returns boolean.
- * @return Object Last item in this
- */
- public function last($func = null)
- {
- return $this->getLast($func, true);
- }
-
- /**
- * Returns the last element that satisfies a condition or NULL if no such element is found.
- *
- * @param callback $func a func that returns boolean.
- * @return mixed
- */
- public function lastOrNull($func = null)
- {
- return $this->getLast($func, false);
- }
-
- /**
- * Returns the first element that satisfies a specified condition
- * @throws \RuntimeException if no element satisfies the condition in predicate -or- the source sequence is empty / does not match any elements.
- *
- * @param callback $func a func that returns boolean.
- * @return mixed
- */
- public function first($func = null)
- {
- return $this->getFirst($func, true);
- }
-
- /**
- * Returns the first element, or NULL if the sequence contains no elements.
- *
- * @param callback $func a func that returns boolean.
- * @return mixed
- */
- public function firstOrNull($func = null)
- {
- return $this->getFirst($func, false);
- }
-
- /**
- * Returns the only element that satisfies a specified condition.
- *
- * @throws \RuntimeException if no element exists or if more than one element exists.
- * @param callback $func a func that returns boolean.
- * @return mixed
- */
- public function single($func = null)
- {
- return $this->getSingle($func, true);
- }
-
- /**
- * Returns the only element that satisfies a specified condition or NULL if no such element exists.
- *
- * @throws \RuntimeException if more than one element satisfies the condition.
- * @param callback $func a func that returns boolean.
- * @return mixed
- */
- public function singleOrNull($func = null)
- {
- return $this->getSingle($func, false);
- }
-
-
- private function getWhereIteratorOrInnerIterator($func)
- {
- return $func === null ? $this->iterator : new WhereIterator($this->iterator, $func);
- }
-
- private function getSelectIteratorOrInnerIterator($func)
- {
- return $func === null ? $this->iterator : new SelectIterator($this->iterator, $func);
- }
-
- private function getSingle($func, $throw)
- {
- $source = $this->getWhereIteratorOrInnerIterator($func);
-
- $count = 0;
- $single = null;
-
- foreach ($source as $stored) {
- $count++;
-
- if ($count > 1) {
- throw new \RuntimeException("The input sequence contains more than 1 elements.");
- }
-
- $single = $stored;
- }
-
- if ($count == 0 && $throw) {
- throw new \RuntimeException("The input sequence contains no matching element.");
- }
-
- return $single;
- }
-
- private function getFirst($func, $throw)
- {
- $source = $this->getWhereIteratorOrInnerIterator($func);
-
- $count = 0;
- $first = null;
-
- foreach ($source as $stored) {
- $count++;
- $first = $stored;
- break;
- }
-
- if ($count == 0 && $throw) {
- throw new \RuntimeException("The input sequence contains no matching element.");
- }
-
- return $first;
- }
-
- private function getLast($func, $throw)
- {
- $source = $this->getWhereIteratorOrInnerIterator($func);
-
- $count = 0;
- $last = null;
-
- foreach ($source as $stored) {
- $count++;
- $last = $stored;
- }
-
- if ($count == 0 && $throw) {
- throw new \RuntimeException("The input sequence contains no matching element.");
- }
-
- return $last;
- }
-
- /**
- * Creates an Array from this Linq object with key/value selector(s).
- *
- * @param callback $keySelector a func that returns the array-key for each element.
- * @param callback $valueSelector a func that returns the array-value for each element.
- *
- * @return Array An array with all values.
- */
- public function toArray($keySelector = null, $valueSelector = null)
- {
- if ($keySelector === null && $valueSelector === null) {
- return iterator_to_array($this, false);
- } elseif ($keySelector == null) {
- return iterator_to_array(new SelectIterator($this->getIterator(), $valueSelector), false);
- } else {
- $array = array();
- foreach ($this as $value) {
- $key = $keySelector($value);
- $array[$key] = $valueSelector == null ? $value : $valueSelector($value);
- }
- return $array;
- }
- }
-
- /**
- * Retrieves the iterator of this Linq class.
- * @link http://php.net/manual/en/iteratoraggregate.getiterator.php
- * @return Traversable An instance of an object implementing Iterator or
- * Traversable
- */
- public function getIterator()
- {
- return $this->iterator;
- }
-}
\ No newline at end of file
diff --git a/fusonic/opengraph/src/Consumer.php b/fusonic/opengraph/src/Consumer.php
index 25b03febc..74aec6a64 100644
--- a/fusonic/opengraph/src/Consumer.php
+++ b/fusonic/opengraph/src/Consumer.php
@@ -2,8 +2,6 @@
namespace Fusonic\OpenGraph;
-use DOMElement;
-use Fusonic\Linq\Linq;
use Fusonic\OpenGraph\Objects\ObjectBase;
use Fusonic\OpenGraph\Objects\Website;
use LogicException;
@@ -97,32 +95,20 @@ private function extractOpenGraphData(string $content): ObjectBase
{
// Get all meta-tags starting with "og:"
$ogMetaTags = $crawler->filter("meta[{$t}^='og:']");
+
// Create clean property array
- $props = Linq::from($ogMetaTags)
- ->select(
- function (DOMElement $tag) use ($t) {
- $name = strtolower(trim($tag->getAttribute($t)));
- $value = trim($tag->getAttribute("content"));
- return new Property($name, $value);
- }
- )
- ->toArray();
+ $props = [];
+ foreach ($ogMetaTags as $tag) {
+ $name = strtolower(trim($tag->getAttribute($t)));
+ $value = trim($tag->getAttribute("content"));
+ $props[] = new Property($name, $value);
+ }
+
$properties = array_merge($properties, $props);
-
}
- // Create new object of the correct type
- $typeProperty = Linq::from($properties)
- ->firstOrNull(
- function (Property $property) {
- return $property->key === Property::TYPE;
- }
- );
- switch ($typeProperty !== null ? $typeProperty->value : null) {
- default:
- $object = new Website();
- break;
- }
+ // Create new object
+ $object = new Website();
// Assign all properties to the object
$object->assignProperties($properties, $this->debug);
diff --git a/masterminds/html5/LICENSE.txt b/masterminds/html5/LICENSE.txt
new file mode 100644
index 000000000..3c275b54a
--- /dev/null
+++ b/masterminds/html5/LICENSE.txt
@@ -0,0 +1,66 @@
+## HTML5-PHP License
+
+Copyright (c) 2013 The Authors of HTML5-PHP
+
+Matt Butcher - mattbutcher@google.com
+Matt Farina - matt@mattfarina.com
+Asmir Mustafic - goetas@gmail.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+## HTML5Lib License
+
+Portions of this are based on html5lib's PHP version, which was a
+sub-project of html5lib. The following is the list of contributors from
+html5lib:
+
+html5lib:
+
+Copyright (c) 2006-2009 The Authors
+
+Contributors:
+James Graham - jg307@cam.ac.uk
+Anne van Kesteren - annevankesteren@gmail.com
+Lachlan Hunt - lachlan.hunt@lachy.id.au
+Matt McDonald - kanashii@kanashii.ca
+Sam Ruby - rubys@intertwingly.net
+Ian Hickson (Google) - ian@hixie.ch
+Thomas Broyer - t.broyer@ltgt.net
+Jacques Distler - distler@golem.ph.utexas.edu
+Henri Sivonen - hsivonen@iki.fi
+Adam Barth - abarth@webkit.org
+Eric Seidel - eric@webkit.org
+The Mozilla Foundation (contributions from Henri Sivonen since 2008)
+David Flanagan (Mozilla) - dflanagan@mozilla.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/masterminds/html5/src/HTML5.php b/masterminds/html5/src/HTML5.php
new file mode 100644
index 000000000..c857145fb
--- /dev/null
+++ b/masterminds/html5/src/HTML5.php
@@ -0,0 +1,246 @@
+ false,
+
+ // Prevents the parser from automatically assigning the HTML5 namespace to the DOM document.
+ 'disable_html_ns' => false,
+ );
+
+ protected $errors = array();
+
+ public function __construct(array $defaultOptions = array())
+ {
+ $this->defaultOptions = array_merge($this->defaultOptions, $defaultOptions);
+ }
+
+ /**
+ * Get the current default options.
+ *
+ * @return array
+ */
+ public function getOptions()
+ {
+ return $this->defaultOptions;
+ }
+
+ /**
+ * Load and parse an HTML file.
+ *
+ * This will apply the HTML5 parser, which is tolerant of many
+ * varieties of HTML, including XHTML 1, HTML 4, and well-formed HTML
+ * 3. Note that in these cases, not all of the old data will be
+ * preserved. For example, XHTML's XML declaration will be removed.
+ *
+ * The rules governing parsing are set out in the HTML 5 spec.
+ *
+ * @param string|resource $file The path to the file to parse. If this is a resource, it is
+ * assumed to be an open stream whose pointer is set to the first
+ * byte of input.
+ * @param array $options Configuration options when parsing the HTML.
+ *
+ * @return \DOMDocument A DOM document. These object type is defined by the libxml
+ * library, and should have been included with your version of PHP.
+ */
+ public function load($file, array $options = array())
+ {
+ // Handle the case where file is a resource.
+ if (is_resource($file)) {
+ return $this->parse(stream_get_contents($file), $options);
+ }
+
+ return $this->parse(file_get_contents($file), $options);
+ }
+
+ /**
+ * Parse a HTML Document from a string.
+ *
+ * Take a string of HTML 5 (or earlier) and parse it into a
+ * DOMDocument.
+ *
+ * @param string $string A html5 document as a string.
+ * @param array $options Configuration options when parsing the HTML.
+ *
+ * @return \DOMDocument A DOM document. DOM is part of libxml, which is included with
+ * almost all distribtions of PHP.
+ */
+ public function loadHTML($string, array $options = array())
+ {
+ return $this->parse($string, $options);
+ }
+
+ /**
+ * Convenience function to load an HTML file.
+ *
+ * This is here to provide backwards compatibility with the
+ * PHP DOM implementation. It simply calls load().
+ *
+ * @param string $file The path to the file to parse. If this is a resource, it is
+ * assumed to be an open stream whose pointer is set to the first
+ * byte of input.
+ * @param array $options Configuration options when parsing the HTML.
+ *
+ * @return \DOMDocument A DOM document. These object type is defined by the libxml
+ * library, and should have been included with your version of PHP.
+ */
+ public function loadHTMLFile($file, array $options = array())
+ {
+ return $this->load($file, $options);
+ }
+
+ /**
+ * Parse a HTML fragment from a string.
+ *
+ * @param string $string the HTML5 fragment as a string
+ * @param array $options Configuration options when parsing the HTML
+ *
+ * @return \DOMDocumentFragment A DOM fragment. The DOM is part of libxml, which is included with
+ * almost all distributions of PHP.
+ */
+ public function loadHTMLFragment($string, array $options = array())
+ {
+ return $this->parseFragment($string, $options);
+ }
+
+ /**
+ * Return all errors encountered into parsing phase.
+ *
+ * @return array
+ */
+ public function getErrors()
+ {
+ return $this->errors;
+ }
+
+ /**
+ * Return true it some errors were encountered into parsing phase.
+ *
+ * @return bool
+ */
+ public function hasErrors()
+ {
+ return count($this->errors) > 0;
+ }
+
+ /**
+ * Parse an input string.
+ *
+ * @param string $input
+ * @param array $options
+ *
+ * @return \DOMDocument
+ */
+ public function parse($input, array $options = array())
+ {
+ $this->errors = array();
+ $options = array_merge($this->defaultOptions, $options);
+ $events = new DOMTreeBuilder(false, $options);
+ $scanner = new Scanner($input, !empty($options['encoding']) ? $options['encoding'] : 'UTF-8');
+ $parser = new Tokenizer($scanner, $events, !empty($options['xmlNamespaces']) ? Tokenizer::CONFORMANT_XML : Tokenizer::CONFORMANT_HTML);
+
+ $parser->parse();
+ $this->errors = $events->getErrors();
+
+ return $events->document();
+ }
+
+ /**
+ * Parse an input stream where the stream is a fragment.
+ *
+ * Lower-level loading function. This requires an input stream instead
+ * of a string, file, or resource.
+ *
+ * @param string $input The input data to parse in the form of a string.
+ * @param array $options An array of options.
+ *
+ * @return \DOMDocumentFragment
+ */
+ public function parseFragment($input, array $options = array())
+ {
+ $options = array_merge($this->defaultOptions, $options);
+ $events = new DOMTreeBuilder(true, $options);
+ $scanner = new Scanner($input, !empty($options['encoding']) ? $options['encoding'] : 'UTF-8');
+ $parser = new Tokenizer($scanner, $events, !empty($options['xmlNamespaces']) ? Tokenizer::CONFORMANT_XML : Tokenizer::CONFORMANT_HTML);
+
+ $parser->parse();
+ $this->errors = $events->getErrors();
+
+ return $events->fragment();
+ }
+
+ /**
+ * Save a DOM into a given file as HTML5.
+ *
+ * @param mixed $dom The DOM to be serialized.
+ * @param string|resource $file The filename to be written or resource to write to.
+ * @param array $options Configuration options when serializing the DOM. These include:
+ * - encode_entities: Text written to the output is escaped by default and not all
+ * entities are encoded. If this is set to true all entities will be encoded.
+ * Defaults to false.
+ */
+ public function save($dom, $file, $options = array())
+ {
+ $close = true;
+ if (is_resource($file)) {
+ $stream = $file;
+ $close = false;
+ } else {
+ $stream = fopen($file, 'wb');
+ }
+ $options = array_merge($this->defaultOptions, $options);
+ $rules = new OutputRules($stream, $options);
+ $trav = new Traverser($dom, $stream, $rules, $options);
+
+ $trav->walk();
+ /*
+ * release the traverser to avoid cyclic references and allow PHP to free memory without waiting for gc_collect_cycles
+ */
+ $rules->unsetTraverser();
+ if ($close) {
+ fclose($stream);
+ }
+ }
+
+ /**
+ * Convert a DOM into an HTML5 string.
+ *
+ * @param mixed $dom The DOM to be serialized.
+ * @param array $options Configuration options when serializing the DOM. These include:
+ * - encode_entities: Text written to the output is escaped by default and not all
+ * entities are encoded. If this is set to true all entities will be encoded.
+ * Defaults to false.
+ *
+ * @return string A HTML5 documented generated from the DOM.
+ */
+ public function saveHTML($dom, $options = array())
+ {
+ $stream = fopen('php://temp', 'wb');
+ $this->save($dom, $stream, array_merge($this->defaultOptions, $options));
+
+ $html = stream_get_contents($stream, -1, 0);
+
+ fclose($stream);
+
+ return $html;
+ }
+}
diff --git a/masterminds/html5/src/HTML5/Elements.php b/masterminds/html5/src/HTML5/Elements.php
new file mode 100644
index 000000000..5d8cfd442
--- /dev/null
+++ b/masterminds/html5/src/HTML5/Elements.php
@@ -0,0 +1,637 @@
+ [PARENT-TAG-NAME-TO-CLOSE1, PARENT-TAG-NAME-TO-CLOSE2, ...].
+ *
+ * Order is important, after auto-closing one parent with might have to close also their parent.
+ *
+ * @var array
+ */
+ public static $optionalEndElementsParentsToClose = array(
+ 'tr' => array('td', 'tr'),
+ 'td' => array('td', 'th'),
+ 'th' => array('td', 'th'),
+ 'tfoot' => array('td', 'th', 'tr', 'tbody', 'thead'),
+ 'tbody' => array('td', 'th', 'tr', 'thead'),
+ );
+
+ /**
+ * The HTML5 elements as defined in http://dev.w3.org/html5/markup/elements.html.
+ *
+ * @var array
+ */
+ public static $html5 = array(
+ 'a' => 1,
+ 'abbr' => 1,
+ 'address' => 65, // NORMAL | BLOCK_TAG
+ 'area' => 9, // NORMAL | VOID_TAG
+ 'article' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'aside' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'audio' => 1, // NORMAL
+ 'b' => 1,
+ 'base' => 9, // NORMAL | VOID_TAG
+ 'bdi' => 1,
+ 'bdo' => 1,
+ 'blockquote' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'body' => 1,
+ 'br' => 9, // NORMAL | VOID_TAG
+ 'button' => 1,
+ 'canvas' => 65, // NORMAL | BLOCK_TAG
+ 'caption' => 1,
+ 'cite' => 1,
+ 'code' => 1,
+ 'col' => 9, // NORMAL | VOID_TAG
+ 'colgroup' => 1,
+ 'command' => 9, // NORMAL | VOID_TAG
+ // "data" => 1, // This is highly experimental and only part of the whatwg spec (not w3c). See https://developer.mozilla.org/en-US/docs/HTML/Element/data
+ 'datalist' => 1,
+ 'dd' => 65, // NORMAL | BLOCK_TAG
+ 'del' => 1,
+ 'details' => 17, // NORMAL | AUTOCLOSE_P,
+ 'dfn' => 1,
+ 'dialog' => 17, // NORMAL | AUTOCLOSE_P,
+ 'div' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'dl' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'dt' => 1,
+ 'em' => 1,
+ 'embed' => 9, // NORMAL | VOID_TAG
+ 'fieldset' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'figcaption' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'figure' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'footer' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'form' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'h1' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'h2' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'h3' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'h4' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'h5' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'h6' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'head' => 1,
+ 'header' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'hgroup' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'hr' => 73, // NORMAL | VOID_TAG
+ 'html' => 1,
+ 'i' => 1,
+ 'iframe' => 3, // NORMAL | TEXT_RAW
+ 'img' => 9, // NORMAL | VOID_TAG
+ 'input' => 9, // NORMAL | VOID_TAG
+ 'kbd' => 1,
+ 'ins' => 1,
+ 'keygen' => 9, // NORMAL | VOID_TAG
+ 'label' => 1,
+ 'legend' => 1,
+ 'li' => 1,
+ 'link' => 9, // NORMAL | VOID_TAG
+ 'map' => 1,
+ 'mark' => 1,
+ 'menu' => 17, // NORMAL | AUTOCLOSE_P,
+ 'meta' => 9, // NORMAL | VOID_TAG
+ 'meter' => 1,
+ 'nav' => 17, // NORMAL | AUTOCLOSE_P,
+ 'noscript' => 65, // NORMAL | BLOCK_TAG
+ 'object' => 1,
+ 'ol' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'optgroup' => 1,
+ 'option' => 1,
+ 'output' => 65, // NORMAL | BLOCK_TAG
+ 'p' => 209, // NORMAL | AUTOCLOSE_P | BLOCK_TAG | BLOCK_ONLY_INLINE
+ 'param' => 9, // NORMAL | VOID_TAG
+ 'pre' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'progress' => 1,
+ 'q' => 1,
+ 'rp' => 1,
+ 'rt' => 1,
+ 'ruby' => 1,
+ 's' => 1,
+ 'samp' => 1,
+ 'script' => 3, // NORMAL | TEXT_RAW
+ 'section' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'select' => 1,
+ 'small' => 1,
+ 'source' => 9, // NORMAL | VOID_TAG
+ 'span' => 1,
+ 'strong' => 1,
+ 'style' => 3, // NORMAL | TEXT_RAW
+ 'sub' => 1,
+ 'summary' => 17, // NORMAL | AUTOCLOSE_P,
+ 'sup' => 1,
+ 'table' => 65, // NORMAL | BLOCK_TAG
+ 'tbody' => 1,
+ 'td' => 1,
+ 'textarea' => 5, // NORMAL | TEXT_RCDATA
+ 'tfoot' => 65, // NORMAL | BLOCK_TAG
+ 'th' => 1,
+ 'thead' => 1,
+ 'time' => 1,
+ 'title' => 5, // NORMAL | TEXT_RCDATA
+ 'tr' => 1,
+ 'track' => 9, // NORMAL | VOID_TAG
+ 'u' => 1,
+ 'ul' => 81, // NORMAL | AUTOCLOSE_P | BLOCK_TAG
+ 'var' => 1,
+ 'video' => 1,
+ 'wbr' => 9, // NORMAL | VOID_TAG
+
+ // Legacy?
+ 'basefont' => 8, // VOID_TAG
+ 'bgsound' => 8, // VOID_TAG
+ 'noframes' => 2, // RAW_TEXT
+ 'frame' => 9, // NORMAL | VOID_TAG
+ 'frameset' => 1,
+ 'center' => 16,
+ 'dir' => 16,
+ 'listing' => 16, // AUTOCLOSE_P
+ 'plaintext' => 48, // AUTOCLOSE_P | TEXT_PLAINTEXT
+ 'applet' => 0,
+ 'marquee' => 0,
+ 'isindex' => 8, // VOID_TAG
+ 'xmp' => 20, // AUTOCLOSE_P | VOID_TAG | RAW_TEXT
+ 'noembed' => 2, // RAW_TEXT
+ );
+
+ /**
+ * The MathML elements.
+ * See http://www.w3.org/wiki/MathML/Elements.
+ *
+ * In our case we are only concerned with presentation MathML and not content
+ * MathML. There is a nice list of this subset at https://developer.mozilla.org/en-US/docs/MathML/Element.
+ *
+ * @var array
+ */
+ public static $mathml = array(
+ 'maction' => 1,
+ 'maligngroup' => 1,
+ 'malignmark' => 1,
+ 'math' => 1,
+ 'menclose' => 1,
+ 'merror' => 1,
+ 'mfenced' => 1,
+ 'mfrac' => 1,
+ 'mglyph' => 1,
+ 'mi' => 1,
+ 'mlabeledtr' => 1,
+ 'mlongdiv' => 1,
+ 'mmultiscripts' => 1,
+ 'mn' => 1,
+ 'mo' => 1,
+ 'mover' => 1,
+ 'mpadded' => 1,
+ 'mphantom' => 1,
+ 'mroot' => 1,
+ 'mrow' => 1,
+ 'ms' => 1,
+ 'mscarries' => 1,
+ 'mscarry' => 1,
+ 'msgroup' => 1,
+ 'msline' => 1,
+ 'mspace' => 1,
+ 'msqrt' => 1,
+ 'msrow' => 1,
+ 'mstack' => 1,
+ 'mstyle' => 1,
+ 'msub' => 1,
+ 'msup' => 1,
+ 'msubsup' => 1,
+ 'mtable' => 1,
+ 'mtd' => 1,
+ 'mtext' => 1,
+ 'mtr' => 1,
+ 'munder' => 1,
+ 'munderover' => 1,
+ );
+
+ /**
+ * The svg elements.
+ *
+ * The Mozilla documentation has a good list at https://developer.mozilla.org/en-US/docs/SVG/Element.
+ * The w3c list appears to be lacking in some areas like filter effect elements.
+ * That list can be found at http://www.w3.org/wiki/SVG/Elements.
+ *
+ * Note, FireFox appears to do a better job rendering filter effects than chrome.
+ * While they are in the spec I'm not sure how widely implemented they are.
+ *
+ * @var array
+ */
+ public static $svg = array(
+ 'a' => 1,
+ 'altGlyph' => 1,
+ 'altGlyphDef' => 1,
+ 'altGlyphItem' => 1,
+ 'animate' => 1,
+ 'animateColor' => 1,
+ 'animateMotion' => 1,
+ 'animateTransform' => 1,
+ 'circle' => 1,
+ 'clipPath' => 1,
+ 'color-profile' => 1,
+ 'cursor' => 1,
+ 'defs' => 1,
+ 'desc' => 1,
+ 'ellipse' => 1,
+ 'feBlend' => 1,
+ 'feColorMatrix' => 1,
+ 'feComponentTransfer' => 1,
+ 'feComposite' => 1,
+ 'feConvolveMatrix' => 1,
+ 'feDiffuseLighting' => 1,
+ 'feDisplacementMap' => 1,
+ 'feDistantLight' => 1,
+ 'feFlood' => 1,
+ 'feFuncA' => 1,
+ 'feFuncB' => 1,
+ 'feFuncG' => 1,
+ 'feFuncR' => 1,
+ 'feGaussianBlur' => 1,
+ 'feImage' => 1,
+ 'feMerge' => 1,
+ 'feMergeNode' => 1,
+ 'feMorphology' => 1,
+ 'feOffset' => 1,
+ 'fePointLight' => 1,
+ 'feSpecularLighting' => 1,
+ 'feSpotLight' => 1,
+ 'feTile' => 1,
+ 'feTurbulence' => 1,
+ 'filter' => 1,
+ 'font' => 1,
+ 'font-face' => 1,
+ 'font-face-format' => 1,
+ 'font-face-name' => 1,
+ 'font-face-src' => 1,
+ 'font-face-uri' => 1,
+ 'foreignObject' => 1,
+ 'g' => 1,
+ 'glyph' => 1,
+ 'glyphRef' => 1,
+ 'hkern' => 1,
+ 'image' => 1,
+ 'line' => 1,
+ 'linearGradient' => 1,
+ 'marker' => 1,
+ 'mask' => 1,
+ 'metadata' => 1,
+ 'missing-glyph' => 1,
+ 'mpath' => 1,
+ 'path' => 1,
+ 'pattern' => 1,
+ 'polygon' => 1,
+ 'polyline' => 1,
+ 'radialGradient' => 1,
+ 'rect' => 1,
+ 'script' => 3, // NORMAL | RAW_TEXT
+ 'set' => 1,
+ 'stop' => 1,
+ 'style' => 3, // NORMAL | RAW_TEXT
+ 'svg' => 1,
+ 'switch' => 1,
+ 'symbol' => 1,
+ 'text' => 1,
+ 'textPath' => 1,
+ 'title' => 1,
+ 'tref' => 1,
+ 'tspan' => 1,
+ 'use' => 1,
+ 'view' => 1,
+ 'vkern' => 1,
+ );
+
+ /**
+ * Some attributes in SVG are case sensitive.
+ *
+ * This map contains key/value pairs with the key as the lowercase attribute
+ * name and the value with the correct casing.
+ */
+ public static $svgCaseSensitiveAttributeMap = array(
+ 'attributename' => 'attributeName',
+ 'attributetype' => 'attributeType',
+ 'basefrequency' => 'baseFrequency',
+ 'baseprofile' => 'baseProfile',
+ 'calcmode' => 'calcMode',
+ 'clippathunits' => 'clipPathUnits',
+ 'contentscripttype' => 'contentScriptType',
+ 'contentstyletype' => 'contentStyleType',
+ 'diffuseconstant' => 'diffuseConstant',
+ 'edgemode' => 'edgeMode',
+ 'externalresourcesrequired' => 'externalResourcesRequired',
+ 'filterres' => 'filterRes',
+ 'filterunits' => 'filterUnits',
+ 'glyphref' => 'glyphRef',
+ 'gradienttransform' => 'gradientTransform',
+ 'gradientunits' => 'gradientUnits',
+ 'kernelmatrix' => 'kernelMatrix',
+ 'kernelunitlength' => 'kernelUnitLength',
+ 'keypoints' => 'keyPoints',
+ 'keysplines' => 'keySplines',
+ 'keytimes' => 'keyTimes',
+ 'lengthadjust' => 'lengthAdjust',
+ 'limitingconeangle' => 'limitingConeAngle',
+ 'markerheight' => 'markerHeight',
+ 'markerunits' => 'markerUnits',
+ 'markerwidth' => 'markerWidth',
+ 'maskcontentunits' => 'maskContentUnits',
+ 'maskunits' => 'maskUnits',
+ 'numoctaves' => 'numOctaves',
+ 'pathlength' => 'pathLength',
+ 'patterncontentunits' => 'patternContentUnits',
+ 'patterntransform' => 'patternTransform',
+ 'patternunits' => 'patternUnits',
+ 'pointsatx' => 'pointsAtX',
+ 'pointsaty' => 'pointsAtY',
+ 'pointsatz' => 'pointsAtZ',
+ 'preservealpha' => 'preserveAlpha',
+ 'preserveaspectratio' => 'preserveAspectRatio',
+ 'primitiveunits' => 'primitiveUnits',
+ 'refx' => 'refX',
+ 'refy' => 'refY',
+ 'repeatcount' => 'repeatCount',
+ 'repeatdur' => 'repeatDur',
+ 'requiredextensions' => 'requiredExtensions',
+ 'requiredfeatures' => 'requiredFeatures',
+ 'specularconstant' => 'specularConstant',
+ 'specularexponent' => 'specularExponent',
+ 'spreadmethod' => 'spreadMethod',
+ 'startoffset' => 'startOffset',
+ 'stddeviation' => 'stdDeviation',
+ 'stitchtiles' => 'stitchTiles',
+ 'surfacescale' => 'surfaceScale',
+ 'systemlanguage' => 'systemLanguage',
+ 'tablevalues' => 'tableValues',
+ 'targetx' => 'targetX',
+ 'targety' => 'targetY',
+ 'textlength' => 'textLength',
+ 'viewbox' => 'viewBox',
+ 'viewtarget' => 'viewTarget',
+ 'xchannelselector' => 'xChannelSelector',
+ 'ychannelselector' => 'yChannelSelector',
+ 'zoomandpan' => 'zoomAndPan',
+ );
+
+ /**
+ * Some SVG elements are case sensitive.
+ * This map contains these.
+ *
+ * The map contains key/value store of the name is lowercase as the keys and
+ * the correct casing as the value.
+ */
+ public static $svgCaseSensitiveElementMap = array(
+ 'altglyph' => 'altGlyph',
+ 'altglyphdef' => 'altGlyphDef',
+ 'altglyphitem' => 'altGlyphItem',
+ 'animatecolor' => 'animateColor',
+ 'animatemotion' => 'animateMotion',
+ 'animatetransform' => 'animateTransform',
+ 'clippath' => 'clipPath',
+ 'feblend' => 'feBlend',
+ 'fecolormatrix' => 'feColorMatrix',
+ 'fecomponenttransfer' => 'feComponentTransfer',
+ 'fecomposite' => 'feComposite',
+ 'feconvolvematrix' => 'feConvolveMatrix',
+ 'fediffuselighting' => 'feDiffuseLighting',
+ 'fedisplacementmap' => 'feDisplacementMap',
+ 'fedistantlight' => 'feDistantLight',
+ 'feflood' => 'feFlood',
+ 'fefunca' => 'feFuncA',
+ 'fefuncb' => 'feFuncB',
+ 'fefuncg' => 'feFuncG',
+ 'fefuncr' => 'feFuncR',
+ 'fegaussianblur' => 'feGaussianBlur',
+ 'feimage' => 'feImage',
+ 'femerge' => 'feMerge',
+ 'femergenode' => 'feMergeNode',
+ 'femorphology' => 'feMorphology',
+ 'feoffset' => 'feOffset',
+ 'fepointlight' => 'fePointLight',
+ 'fespecularlighting' => 'feSpecularLighting',
+ 'fespotlight' => 'feSpotLight',
+ 'fetile' => 'feTile',
+ 'feturbulence' => 'feTurbulence',
+ 'foreignobject' => 'foreignObject',
+ 'glyphref' => 'glyphRef',
+ 'lineargradient' => 'linearGradient',
+ 'radialgradient' => 'radialGradient',
+ 'textpath' => 'textPath',
+ );
+
+ /**
+ * Check whether the given element meets the given criterion.
+ *
+ * Example:
+ *
+ * Elements::isA('script', Elements::TEXT_RAW); // Returns true.
+ *
+ * Elements::isA('script', Elements::TEXT_RCDATA); // Returns false.
+ *
+ * @param string $name The element name.
+ * @param int $mask One of the constants on this class.
+ *
+ * @return bool true if the element matches the mask, false otherwise.
+ */
+ public static function isA($name, $mask)
+ {
+ return (static::element($name) & $mask) === $mask;
+ }
+
+ /**
+ * Test if an element is a valid html5 element.
+ *
+ * @param string $name The name of the element.
+ *
+ * @return bool true if a html5 element and false otherwise.
+ */
+ public static function isHtml5Element($name)
+ {
+ // html5 element names are case insensitive. Forcing lowercase for the check.
+ // Do we need this check or will all data passed here already be lowercase?
+ return isset(static::$html5[strtolower($name)]);
+ }
+
+ /**
+ * Test if an element name is a valid MathML presentation element.
+ *
+ * @param string $name The name of the element.
+ *
+ * @return bool true if a MathML name and false otherwise.
+ */
+ public static function isMathMLElement($name)
+ {
+ // MathML is case-sensitive unlike html5 elements.
+ return isset(static::$mathml[$name]);
+ }
+
+ /**
+ * Test if an element is a valid SVG element.
+ *
+ * @param string $name The name of the element.
+ *
+ * @return bool true if a SVG element and false otherise.
+ */
+ public static function isSvgElement($name)
+ {
+ // SVG is case-sensitive unlike html5 elements.
+ return isset(static::$svg[$name]);
+ }
+
+ /**
+ * Is an element name valid in an html5 document.
+ * This includes html5 elements along with other allowed embedded content
+ * such as svg and mathml.
+ *
+ * @param string $name The name of the element.
+ *
+ * @return bool true if valid and false otherwise.
+ */
+ public static function isElement($name)
+ {
+ return static::isHtml5Element($name) || static::isMathMLElement($name) || static::isSvgElement($name);
+ }
+
+ /**
+ * Get the element mask for the given element name.
+ *
+ * @param string $name The name of the element.
+ *
+ * @return int the element mask.
+ */
+ public static function element($name)
+ {
+ if (isset(static::$html5[$name])) {
+ return static::$html5[$name];
+ }
+ if (isset(static::$svg[$name])) {
+ return static::$svg[$name];
+ }
+ if (isset(static::$mathml[$name])) {
+ return static::$mathml[$name];
+ }
+
+ return 0;
+ }
+
+ /**
+ * Normalize a SVG element name to its proper case and form.
+ *
+ * @param string $name The name of the element.
+ *
+ * @return string the normalized form of the element name.
+ */
+ public static function normalizeSvgElement($name)
+ {
+ $name = strtolower($name);
+ if (isset(static::$svgCaseSensitiveElementMap[$name])) {
+ $name = static::$svgCaseSensitiveElementMap[$name];
+ }
+
+ return $name;
+ }
+
+ /**
+ * Normalize a SVG attribute name to its proper case and form.
+ *
+ * @param string $name The name of the attribute.
+ *
+ * @return string The normalized form of the attribute name.
+ */
+ public static function normalizeSvgAttribute($name)
+ {
+ $name = strtolower($name);
+ if (isset(static::$svgCaseSensitiveAttributeMap[$name])) {
+ $name = static::$svgCaseSensitiveAttributeMap[$name];
+ }
+
+ return $name;
+ }
+
+ /**
+ * Normalize a MathML attribute name to its proper case and form.
+ * Note, all MathML element names are lowercase.
+ *
+ * @param string $name The name of the attribute.
+ *
+ * @return string The normalized form of the attribute name.
+ */
+ public static function normalizeMathMlAttribute($name)
+ {
+ $name = strtolower($name);
+
+ // Only one attribute has a mixed case form for MathML.
+ if ('definitionurl' === $name) {
+ $name = 'definitionURL';
+ }
+
+ return $name;
+ }
+}
diff --git a/masterminds/html5/src/HTML5/Entities.php b/masterminds/html5/src/HTML5/Entities.php
new file mode 100644
index 000000000..0e7227dc1
--- /dev/null
+++ b/masterminds/html5/src/HTML5/Entities.php
@@ -0,0 +1,2236 @@
+ 'Á',
+ 'Aacut' => 'Á',
+ 'aacute' => 'á',
+ 'aacut' => 'á',
+ 'Abreve' => 'Ă',
+ 'abreve' => 'ă',
+ 'ac' => '∾',
+ 'acd' => '∿',
+ 'acE' => '∾̳',
+ 'Acirc' => 'Â',
+ 'Acir' => 'Â',
+ 'acirc' => 'â',
+ 'acir' => 'â',
+ 'acute' => '´',
+ 'acut' => '´',
+ 'Acy' => 'А',
+ 'acy' => 'а',
+ 'AElig' => 'Æ',
+ 'AEli' => 'Æ',
+ 'aelig' => 'æ',
+ 'aeli' => 'æ',
+ 'af' => '',
+ 'Afr' => '𝔄',
+ 'afr' => '𝔞',
+ 'Agrave' => 'À',
+ 'Agrav' => 'À',
+ 'agrave' => 'à',
+ 'agrav' => 'à',
+ 'alefsym' => 'ℵ',
+ 'aleph' => 'ℵ',
+ 'Alpha' => 'Α',
+ 'alpha' => 'α',
+ 'Amacr' => 'Ā',
+ 'amacr' => 'ā',
+ 'amalg' => '⨿',
+ 'AMP' => '&',
+ 'AM' => '&',
+ 'amp' => '&',
+ 'am' => '&',
+ 'And' => '⩓',
+ 'and' => '∧',
+ 'andand' => '⩕',
+ 'andd' => '⩜',
+ 'andslope' => '⩘',
+ 'andv' => '⩚',
+ 'ang' => '∠',
+ 'ange' => '⦤',
+ 'angle' => '∠',
+ 'angmsd' => '∡',
+ 'angmsdaa' => '⦨',
+ 'angmsdab' => '⦩',
+ 'angmsdac' => '⦪',
+ 'angmsdad' => '⦫',
+ 'angmsdae' => '⦬',
+ 'angmsdaf' => '⦭',
+ 'angmsdag' => '⦮',
+ 'angmsdah' => '⦯',
+ 'angrt' => '∟',
+ 'angrtvb' => '⊾',
+ 'angrtvbd' => '⦝',
+ 'angsph' => '∢',
+ 'angst' => 'Å',
+ 'angzarr' => '⍼',
+ 'Aogon' => 'Ą',
+ 'aogon' => 'ą',
+ 'Aopf' => '𝔸',
+ 'aopf' => '𝕒',
+ 'ap' => '≈',
+ 'apacir' => '⩯',
+ 'apE' => '⩰',
+ 'ape' => '≊',
+ 'apid' => '≋',
+ 'apos' => '\'',
+ 'ApplyFunction' => '',
+ 'approx' => '≈',
+ 'approxeq' => '≊',
+ 'Aring' => 'Å',
+ 'Arin' => 'Å',
+ 'aring' => 'å',
+ 'arin' => 'å',
+ 'Ascr' => '𝒜',
+ 'ascr' => '𝒶',
+ 'Assign' => '≔',
+ 'ast' => '*',
+ 'asymp' => '≈',
+ 'asympeq' => '≍',
+ 'Atilde' => 'Ã',
+ 'Atild' => 'Ã',
+ 'atilde' => 'ã',
+ 'atild' => 'ã',
+ 'Auml' => 'Ä',
+ 'Aum' => 'Ä',
+ 'auml' => 'ä',
+ 'aum' => 'ä',
+ 'awconint' => '∳',
+ 'awint' => '⨑',
+ 'backcong' => '≌',
+ 'backepsilon' => '϶',
+ 'backprime' => '‵',
+ 'backsim' => '∽',
+ 'backsimeq' => '⋍',
+ 'Backslash' => '∖',
+ 'Barv' => '⫧',
+ 'barvee' => '⊽',
+ 'Barwed' => '⌆',
+ 'barwed' => '⌅',
+ 'barwedge' => '⌅',
+ 'bbrk' => '⎵',
+ 'bbrktbrk' => '⎶',
+ 'bcong' => '≌',
+ 'Bcy' => 'Б',
+ 'bcy' => 'б',
+ 'bdquo' => '„',
+ 'becaus' => '∵',
+ 'Because' => '∵',
+ 'because' => '∵',
+ 'bemptyv' => '⦰',
+ 'bepsi' => '϶',
+ 'bernou' => 'ℬ',
+ 'Bernoullis' => 'ℬ',
+ 'Beta' => 'Β',
+ 'beta' => 'β',
+ 'beth' => 'ℶ',
+ 'between' => '≬',
+ 'Bfr' => '𝔅',
+ 'bfr' => '𝔟',
+ 'bigcap' => '⋂',
+ 'bigcirc' => '◯',
+ 'bigcup' => '⋃',
+ 'bigodot' => '⨀',
+ 'bigoplus' => '⨁',
+ 'bigotimes' => '⨂',
+ 'bigsqcup' => '⨆',
+ 'bigstar' => '★',
+ 'bigtriangledown' => '▽',
+ 'bigtriangleup' => '△',
+ 'biguplus' => '⨄',
+ 'bigvee' => '⋁',
+ 'bigwedge' => '⋀',
+ 'bkarow' => '⤍',
+ 'blacklozenge' => '⧫',
+ 'blacksquare' => '▪',
+ 'blacktriangle' => '▴',
+ 'blacktriangledown' => '▾',
+ 'blacktriangleleft' => '◂',
+ 'blacktriangleright' => '▸',
+ 'blank' => '␣',
+ 'blk12' => '▒',
+ 'blk14' => '░',
+ 'blk34' => '▓',
+ 'block' => '█',
+ 'bne' => '=⃥',
+ 'bnequiv' => '≡⃥',
+ 'bNot' => '⫭',
+ 'bnot' => '⌐',
+ 'Bopf' => '𝔹',
+ 'bopf' => '𝕓',
+ 'bot' => '⊥',
+ 'bottom' => '⊥',
+ 'bowtie' => '⋈',
+ 'boxbox' => '⧉',
+ 'boxDL' => '╗',
+ 'boxDl' => '╖',
+ 'boxdL' => '╕',
+ 'boxdl' => '┐',
+ 'boxDR' => '╔',
+ 'boxDr' => '╓',
+ 'boxdR' => '╒',
+ 'boxdr' => '┌',
+ 'boxH' => '═',
+ 'boxh' => '─',
+ 'boxHD' => '╦',
+ 'boxHd' => '╤',
+ 'boxhD' => '╥',
+ 'boxhd' => '┬',
+ 'boxHU' => '╩',
+ 'boxHu' => '╧',
+ 'boxhU' => '╨',
+ 'boxhu' => '┴',
+ 'boxminus' => '⊟',
+ 'boxplus' => '⊞',
+ 'boxtimes' => '⊠',
+ 'boxUL' => '╝',
+ 'boxUl' => '╜',
+ 'boxuL' => '╛',
+ 'boxul' => '┘',
+ 'boxUR' => '╚',
+ 'boxUr' => '╙',
+ 'boxuR' => '╘',
+ 'boxur' => '└',
+ 'boxV' => '║',
+ 'boxv' => '│',
+ 'boxVH' => '╬',
+ 'boxVh' => '╫',
+ 'boxvH' => '╪',
+ 'boxvh' => '┼',
+ 'boxVL' => '╣',
+ 'boxVl' => '╢',
+ 'boxvL' => '╡',
+ 'boxvl' => '┤',
+ 'boxVR' => '╠',
+ 'boxVr' => '╟',
+ 'boxvR' => '╞',
+ 'boxvr' => '├',
+ 'bprime' => '‵',
+ 'Breve' => '˘',
+ 'breve' => '˘',
+ 'brvbar' => '¦',
+ 'brvba' => '¦',
+ 'Bscr' => 'ℬ',
+ 'bscr' => '𝒷',
+ 'bsemi' => '⁏',
+ 'bsim' => '∽',
+ 'bsime' => '⋍',
+ 'bsol' => '\\',
+ 'bsolb' => '⧅',
+ 'bsolhsub' => '⟈',
+ 'bull' => '•',
+ 'bullet' => '•',
+ 'bump' => '≎',
+ 'bumpE' => '⪮',
+ 'bumpe' => '≏',
+ 'Bumpeq' => '≎',
+ 'bumpeq' => '≏',
+ 'Cacute' => 'Ć',
+ 'cacute' => 'ć',
+ 'Cap' => '⋒',
+ 'cap' => '∩',
+ 'capand' => '⩄',
+ 'capbrcup' => '⩉',
+ 'capcap' => '⩋',
+ 'capcup' => '⩇',
+ 'capdot' => '⩀',
+ 'CapitalDifferentialD' => 'ⅅ',
+ 'caps' => '∩︀',
+ 'caret' => '⁁',
+ 'caron' => 'ˇ',
+ 'Cayleys' => 'ℭ',
+ 'ccaps' => '⩍',
+ 'Ccaron' => 'Č',
+ 'ccaron' => 'č',
+ 'Ccedil' => 'Ç',
+ 'Ccedi' => 'Ç',
+ 'ccedil' => 'ç',
+ 'ccedi' => 'ç',
+ 'Ccirc' => 'Ĉ',
+ 'ccirc' => 'ĉ',
+ 'Cconint' => '∰',
+ 'ccups' => '⩌',
+ 'ccupssm' => '⩐',
+ 'Cdot' => 'Ċ',
+ 'cdot' => 'ċ',
+ 'cedil' => '¸',
+ 'cedi' => '¸',
+ 'Cedilla' => '¸',
+ 'cemptyv' => '⦲',
+ 'cent' => '¢',
+ 'cen' => '¢',
+ 'CenterDot' => '·',
+ 'centerdot' => '·',
+ 'Cfr' => 'ℭ',
+ 'cfr' => '𝔠',
+ 'CHcy' => 'Ч',
+ 'chcy' => 'ч',
+ 'check' => '✓',
+ 'checkmark' => '✓',
+ 'Chi' => 'Χ',
+ 'chi' => 'χ',
+ 'cir' => '○',
+ 'circ' => 'ˆ',
+ 'circeq' => '≗',
+ 'circlearrowleft' => '↺',
+ 'circlearrowright' => '↻',
+ 'circledast' => '⊛',
+ 'circledcirc' => '⊚',
+ 'circleddash' => '⊝',
+ 'CircleDot' => '⊙',
+ 'circledR' => '®',
+ 'circledS' => 'Ⓢ',
+ 'CircleMinus' => '⊖',
+ 'CirclePlus' => '⊕',
+ 'CircleTimes' => '⊗',
+ 'cirE' => '⧃',
+ 'cire' => '≗',
+ 'cirfnint' => '⨐',
+ 'cirmid' => '⫯',
+ 'cirscir' => '⧂',
+ 'ClockwiseContourIntegral' => '∲',
+ 'CloseCurlyDoubleQuote' => '”',
+ 'CloseCurlyQuote' => '’',
+ 'clubs' => '♣',
+ 'clubsuit' => '♣',
+ 'Colon' => '∷',
+ 'colon' => ':',
+ 'Colone' => '⩴',
+ 'colone' => '≔',
+ 'coloneq' => '≔',
+ 'comma' => ',',
+ 'commat' => '@',
+ 'comp' => '∁',
+ 'compfn' => '∘',
+ 'complement' => '∁',
+ 'complexes' => 'ℂ',
+ 'cong' => '≅',
+ 'congdot' => '⩭',
+ 'Congruent' => '≡',
+ 'Conint' => '∯',
+ 'conint' => '∮',
+ 'ContourIntegral' => '∮',
+ 'Copf' => 'ℂ',
+ 'copf' => '𝕔',
+ 'coprod' => '∐',
+ 'Coproduct' => '∐',
+ 'COPY' => '©',
+ 'COP' => '©',
+ 'copy' => '©',
+ 'cop' => '©',
+ 'copysr' => '℗',
+ 'CounterClockwiseContourIntegral' => '∳',
+ 'crarr' => '↵',
+ 'Cross' => '⨯',
+ 'cross' => '✗',
+ 'Cscr' => '𝒞',
+ 'cscr' => '𝒸',
+ 'csub' => '⫏',
+ 'csube' => '⫑',
+ 'csup' => '⫐',
+ 'csupe' => '⫒',
+ 'ctdot' => '⋯',
+ 'cudarrl' => '⤸',
+ 'cudarrr' => '⤵',
+ 'cuepr' => '⋞',
+ 'cuesc' => '⋟',
+ 'cularr' => '↶',
+ 'cularrp' => '⤽',
+ 'Cup' => '⋓',
+ 'cup' => '∪',
+ 'cupbrcap' => '⩈',
+ 'CupCap' => '≍',
+ 'cupcap' => '⩆',
+ 'cupcup' => '⩊',
+ 'cupdot' => '⊍',
+ 'cupor' => '⩅',
+ 'cups' => '∪︀',
+ 'curarr' => '↷',
+ 'curarrm' => '⤼',
+ 'curlyeqprec' => '⋞',
+ 'curlyeqsucc' => '⋟',
+ 'curlyvee' => '⋎',
+ 'curlywedge' => '⋏',
+ 'curren' => '¤',
+ 'curre' => '¤',
+ 'curvearrowleft' => '↶',
+ 'curvearrowright' => '↷',
+ 'cuvee' => '⋎',
+ 'cuwed' => '⋏',
+ 'cwconint' => '∲',
+ 'cwint' => '∱',
+ 'cylcty' => '⌭',
+ 'Dagger' => '‡',
+ 'dagger' => '†',
+ 'daleth' => 'ℸ',
+ 'Darr' => '↡',
+ 'dArr' => '⇓',
+ 'darr' => '↓',
+ 'dash' => '‐',
+ 'Dashv' => '⫤',
+ 'dashv' => '⊣',
+ 'dbkarow' => '⤏',
+ 'dblac' => '˝',
+ 'Dcaron' => 'Ď',
+ 'dcaron' => 'ď',
+ 'Dcy' => 'Д',
+ 'dcy' => 'д',
+ 'DD' => 'ⅅ',
+ 'dd' => 'ⅆ',
+ 'ddagger' => '‡',
+ 'ddarr' => '⇊',
+ 'DDotrahd' => '⤑',
+ 'ddotseq' => '⩷',
+ 'deg' => '°',
+ 'de' => '°',
+ 'Del' => '∇',
+ 'Delta' => 'Δ',
+ 'delta' => 'δ',
+ 'demptyv' => '⦱',
+ 'dfisht' => '⥿',
+ 'Dfr' => '𝔇',
+ 'dfr' => '𝔡',
+ 'dHar' => '⥥',
+ 'dharl' => '⇃',
+ 'dharr' => '⇂',
+ 'DiacriticalAcute' => '´',
+ 'DiacriticalDot' => '˙',
+ 'DiacriticalDoubleAcute' => '˝',
+ 'DiacriticalGrave' => '`',
+ 'DiacriticalTilde' => '˜',
+ 'diam' => '⋄',
+ 'Diamond' => '⋄',
+ 'diamond' => '⋄',
+ 'diamondsuit' => '♦',
+ 'diams' => '♦',
+ 'die' => '¨',
+ 'DifferentialD' => 'ⅆ',
+ 'digamma' => 'ϝ',
+ 'disin' => '⋲',
+ 'div' => '÷',
+ 'divide' => '÷',
+ 'divid' => '÷',
+ 'divideontimes' => '⋇',
+ 'divonx' => '⋇',
+ 'DJcy' => 'Ђ',
+ 'djcy' => 'ђ',
+ 'dlcorn' => '⌞',
+ 'dlcrop' => '⌍',
+ 'dollar' => '$',
+ 'Dopf' => '𝔻',
+ 'dopf' => '𝕕',
+ 'Dot' => '¨',
+ 'dot' => '˙',
+ 'DotDot' => '⃜',
+ 'doteq' => '≐',
+ 'doteqdot' => '≑',
+ 'DotEqual' => '≐',
+ 'dotminus' => '∸',
+ 'dotplus' => '∔',
+ 'dotsquare' => '⊡',
+ 'doublebarwedge' => '⌆',
+ 'DoubleContourIntegral' => '∯',
+ 'DoubleDot' => '¨',
+ 'DoubleDownArrow' => '⇓',
+ 'DoubleLeftArrow' => '⇐',
+ 'DoubleLeftRightArrow' => '⇔',
+ 'DoubleLeftTee' => '⫤',
+ 'DoubleLongLeftArrow' => '⟸',
+ 'DoubleLongLeftRightArrow' => '⟺',
+ 'DoubleLongRightArrow' => '⟹',
+ 'DoubleRightArrow' => '⇒',
+ 'DoubleRightTee' => '⊨',
+ 'DoubleUpArrow' => '⇑',
+ 'DoubleUpDownArrow' => '⇕',
+ 'DoubleVerticalBar' => '∥',
+ 'DownArrow' => '↓',
+ 'Downarrow' => '⇓',
+ 'downarrow' => '↓',
+ 'DownArrowBar' => '⤓',
+ 'DownArrowUpArrow' => '⇵',
+ 'DownBreve' => '̑',
+ 'downdownarrows' => '⇊',
+ 'downharpoonleft' => '⇃',
+ 'downharpoonright' => '⇂',
+ 'DownLeftRightVector' => '⥐',
+ 'DownLeftTeeVector' => '⥞',
+ 'DownLeftVector' => '↽',
+ 'DownLeftVectorBar' => '⥖',
+ 'DownRightTeeVector' => '⥟',
+ 'DownRightVector' => '⇁',
+ 'DownRightVectorBar' => '⥗',
+ 'DownTee' => '⊤',
+ 'DownTeeArrow' => '↧',
+ 'drbkarow' => '⤐',
+ 'drcorn' => '⌟',
+ 'drcrop' => '⌌',
+ 'Dscr' => '𝒟',
+ 'dscr' => '𝒹',
+ 'DScy' => 'Ѕ',
+ 'dscy' => 'ѕ',
+ 'dsol' => '⧶',
+ 'Dstrok' => 'Đ',
+ 'dstrok' => 'đ',
+ 'dtdot' => '⋱',
+ 'dtri' => '▿',
+ 'dtrif' => '▾',
+ 'duarr' => '⇵',
+ 'duhar' => '⥯',
+ 'dwangle' => '⦦',
+ 'DZcy' => 'Џ',
+ 'dzcy' => 'џ',
+ 'dzigrarr' => '⟿',
+ 'Eacute' => 'É',
+ 'Eacut' => 'É',
+ 'eacute' => 'é',
+ 'eacut' => 'é',
+ 'easter' => '⩮',
+ 'Ecaron' => 'Ě',
+ 'ecaron' => 'ě',
+ 'ecir' => 'ê',
+ 'Ecirc' => 'Ê',
+ 'Ecir' => 'Ê',
+ 'ecirc' => 'ê',
+ 'ecolon' => '≕',
+ 'Ecy' => 'Э',
+ 'ecy' => 'э',
+ 'eDDot' => '⩷',
+ 'Edot' => 'Ė',
+ 'eDot' => '≑',
+ 'edot' => 'ė',
+ 'ee' => 'ⅇ',
+ 'efDot' => '≒',
+ 'Efr' => '𝔈',
+ 'efr' => '𝔢',
+ 'eg' => '⪚',
+ 'Egrave' => 'È',
+ 'Egrav' => 'È',
+ 'egrave' => 'è',
+ 'egrav' => 'è',
+ 'egs' => '⪖',
+ 'egsdot' => '⪘',
+ 'el' => '⪙',
+ 'Element' => '∈',
+ 'elinters' => '⏧',
+ 'ell' => 'ℓ',
+ 'els' => '⪕',
+ 'elsdot' => '⪗',
+ 'Emacr' => 'Ē',
+ 'emacr' => 'ē',
+ 'empty' => '∅',
+ 'emptyset' => '∅',
+ 'EmptySmallSquare' => '◻',
+ 'emptyv' => '∅',
+ 'EmptyVerySmallSquare' => '▫',
+ 'emsp' => ' ',
+ 'emsp13' => ' ',
+ 'emsp14' => ' ',
+ 'ENG' => 'Ŋ',
+ 'eng' => 'ŋ',
+ 'ensp' => ' ',
+ 'Eogon' => 'Ę',
+ 'eogon' => 'ę',
+ 'Eopf' => '𝔼',
+ 'eopf' => '𝕖',
+ 'epar' => '⋕',
+ 'eparsl' => '⧣',
+ 'eplus' => '⩱',
+ 'epsi' => 'ε',
+ 'Epsilon' => 'Ε',
+ 'epsilon' => 'ε',
+ 'epsiv' => 'ϵ',
+ 'eqcirc' => '≖',
+ 'eqcolon' => '≕',
+ 'eqsim' => '≂',
+ 'eqslantgtr' => '⪖',
+ 'eqslantless' => '⪕',
+ 'Equal' => '⩵',
+ 'equals' => '=',
+ 'EqualTilde' => '≂',
+ 'equest' => '≟',
+ 'Equilibrium' => '⇌',
+ 'equiv' => '≡',
+ 'equivDD' => '⩸',
+ 'eqvparsl' => '⧥',
+ 'erarr' => '⥱',
+ 'erDot' => '≓',
+ 'Escr' => 'ℰ',
+ 'escr' => 'ℯ',
+ 'esdot' => '≐',
+ 'Esim' => '⩳',
+ 'esim' => '≂',
+ 'Eta' => 'Η',
+ 'eta' => 'η',
+ 'ETH' => 'Ð',
+ 'ET' => 'Ð',
+ 'eth' => 'ð',
+ 'et' => 'ð',
+ 'Euml' => 'Ë',
+ 'Eum' => 'Ë',
+ 'euml' => 'ë',
+ 'eum' => 'ë',
+ 'euro' => '€',
+ 'excl' => '!',
+ 'exist' => '∃',
+ 'Exists' => '∃',
+ 'expectation' => 'ℰ',
+ 'ExponentialE' => 'ⅇ',
+ 'exponentiale' => 'ⅇ',
+ 'fallingdotseq' => '≒',
+ 'Fcy' => 'Ф',
+ 'fcy' => 'ф',
+ 'female' => '♀',
+ 'ffilig' => 'ffi',
+ 'fflig' => 'ff',
+ 'ffllig' => 'ffl',
+ 'Ffr' => '𝔉',
+ 'ffr' => '𝔣',
+ 'filig' => 'fi',
+ 'FilledSmallSquare' => '◼',
+ 'FilledVerySmallSquare' => '▪',
+ 'fjlig' => 'fj',
+ 'flat' => '♭',
+ 'fllig' => 'fl',
+ 'fltns' => '▱',
+ 'fnof' => 'ƒ',
+ 'Fopf' => '𝔽',
+ 'fopf' => '𝕗',
+ 'ForAll' => '∀',
+ 'forall' => '∀',
+ 'fork' => '⋔',
+ 'forkv' => '⫙',
+ 'Fouriertrf' => 'ℱ',
+ 'fpartint' => '⨍',
+ 'frac12' => '½',
+ 'frac1' => '¼',
+ 'frac13' => '⅓',
+ 'frac14' => '¼',
+ 'frac15' => '⅕',
+ 'frac16' => '⅙',
+ 'frac18' => '⅛',
+ 'frac23' => '⅔',
+ 'frac25' => '⅖',
+ 'frac34' => '¾',
+ 'frac3' => '¾',
+ 'frac35' => '⅗',
+ 'frac38' => '⅜',
+ 'frac45' => '⅘',
+ 'frac56' => '⅚',
+ 'frac58' => '⅝',
+ 'frac78' => '⅞',
+ 'frasl' => '⁄',
+ 'frown' => '⌢',
+ 'Fscr' => 'ℱ',
+ 'fscr' => '𝒻',
+ 'gacute' => 'ǵ',
+ 'Gamma' => 'Γ',
+ 'gamma' => 'γ',
+ 'Gammad' => 'Ϝ',
+ 'gammad' => 'ϝ',
+ 'gap' => '⪆',
+ 'Gbreve' => 'Ğ',
+ 'gbreve' => 'ğ',
+ 'Gcedil' => 'Ģ',
+ 'Gcirc' => 'Ĝ',
+ 'gcirc' => 'ĝ',
+ 'Gcy' => 'Г',
+ 'gcy' => 'г',
+ 'Gdot' => 'Ġ',
+ 'gdot' => 'ġ',
+ 'gE' => '≧',
+ 'ge' => '≥',
+ 'gEl' => '⪌',
+ 'gel' => '⋛',
+ 'geq' => '≥',
+ 'geqq' => '≧',
+ 'geqslant' => '⩾',
+ 'ges' => '⩾',
+ 'gescc' => '⪩',
+ 'gesdot' => '⪀',
+ 'gesdoto' => '⪂',
+ 'gesdotol' => '⪄',
+ 'gesl' => '⋛︀',
+ 'gesles' => '⪔',
+ 'Gfr' => '𝔊',
+ 'gfr' => '𝔤',
+ 'Gg' => '⋙',
+ 'gg' => '≫',
+ 'ggg' => '⋙',
+ 'gimel' => 'ℷ',
+ 'GJcy' => 'Ѓ',
+ 'gjcy' => 'ѓ',
+ 'gl' => '≷',
+ 'gla' => '⪥',
+ 'glE' => '⪒',
+ 'glj' => '⪤',
+ 'gnap' => '⪊',
+ 'gnapprox' => '⪊',
+ 'gnE' => '≩',
+ 'gne' => '⪈',
+ 'gneq' => '⪈',
+ 'gneqq' => '≩',
+ 'gnsim' => '⋧',
+ 'Gopf' => '𝔾',
+ 'gopf' => '𝕘',
+ 'grave' => '`',
+ 'GreaterEqual' => '≥',
+ 'GreaterEqualLess' => '⋛',
+ 'GreaterFullEqual' => '≧',
+ 'GreaterGreater' => '⪢',
+ 'GreaterLess' => '≷',
+ 'GreaterSlantEqual' => '⩾',
+ 'GreaterTilde' => '≳',
+ 'Gscr' => '𝒢',
+ 'gscr' => 'ℊ',
+ 'gsim' => '≳',
+ 'gsime' => '⪎',
+ 'gsiml' => '⪐',
+ 'GT' => '>',
+ 'G' => '>',
+ 'Gt' => '≫',
+ 'gt' => '>',
+ 'g' => '>',
+ 'gtcc' => '⪧',
+ 'gtcir' => '⩺',
+ 'gtdot' => '⋗',
+ 'gtlPar' => '⦕',
+ 'gtquest' => '⩼',
+ 'gtrapprox' => '⪆',
+ 'gtrarr' => '⥸',
+ 'gtrdot' => '⋗',
+ 'gtreqless' => '⋛',
+ 'gtreqqless' => '⪌',
+ 'gtrless' => '≷',
+ 'gtrsim' => '≳',
+ 'gvertneqq' => '≩︀',
+ 'gvnE' => '≩︀',
+ 'Hacek' => 'ˇ',
+ 'hairsp' => ' ',
+ 'half' => '½',
+ 'hamilt' => 'ℋ',
+ 'HARDcy' => 'Ъ',
+ 'hardcy' => 'ъ',
+ 'hArr' => '⇔',
+ 'harr' => '↔',
+ 'harrcir' => '⥈',
+ 'harrw' => '↭',
+ 'Hat' => '^',
+ 'hbar' => 'ℏ',
+ 'Hcirc' => 'Ĥ',
+ 'hcirc' => 'ĥ',
+ 'hearts' => '♥',
+ 'heartsuit' => '♥',
+ 'hellip' => '…',
+ 'hercon' => '⊹',
+ 'Hfr' => 'ℌ',
+ 'hfr' => '𝔥',
+ 'HilbertSpace' => 'ℋ',
+ 'hksearow' => '⤥',
+ 'hkswarow' => '⤦',
+ 'hoarr' => '⇿',
+ 'homtht' => '∻',
+ 'hookleftarrow' => '↩',
+ 'hookrightarrow' => '↪',
+ 'Hopf' => 'ℍ',
+ 'hopf' => '𝕙',
+ 'horbar' => '―',
+ 'HorizontalLine' => '─',
+ 'Hscr' => 'ℋ',
+ 'hscr' => '𝒽',
+ 'hslash' => 'ℏ',
+ 'Hstrok' => 'Ħ',
+ 'hstrok' => 'ħ',
+ 'HumpDownHump' => '≎',
+ 'HumpEqual' => '≏',
+ 'hybull' => '⁃',
+ 'hyphen' => '‐',
+ 'Iacute' => 'Í',
+ 'Iacut' => 'Í',
+ 'iacute' => 'í',
+ 'iacut' => 'í',
+ 'ic' => '',
+ 'Icirc' => 'Î',
+ 'Icir' => 'Î',
+ 'icirc' => 'î',
+ 'icir' => 'î',
+ 'Icy' => 'И',
+ 'icy' => 'и',
+ 'Idot' => 'İ',
+ 'IEcy' => 'Е',
+ 'iecy' => 'е',
+ 'iexcl' => '¡',
+ 'iexc' => '¡',
+ 'iff' => '⇔',
+ 'Ifr' => 'ℑ',
+ 'ifr' => '𝔦',
+ 'Igrave' => 'Ì',
+ 'Igrav' => 'Ì',
+ 'igrave' => 'ì',
+ 'igrav' => 'ì',
+ 'ii' => 'ⅈ',
+ 'iiiint' => '⨌',
+ 'iiint' => '∭',
+ 'iinfin' => '⧜',
+ 'iiota' => '℩',
+ 'IJlig' => 'IJ',
+ 'ijlig' => 'ij',
+ 'Im' => 'ℑ',
+ 'Imacr' => 'Ī',
+ 'imacr' => 'ī',
+ 'image' => 'ℑ',
+ 'ImaginaryI' => 'ⅈ',
+ 'imagline' => 'ℐ',
+ 'imagpart' => 'ℑ',
+ 'imath' => 'ı',
+ 'imof' => '⊷',
+ 'imped' => 'Ƶ',
+ 'Implies' => '⇒',
+ 'in' => '∈',
+ 'incare' => '℅',
+ 'infin' => '∞',
+ 'infintie' => '⧝',
+ 'inodot' => 'ı',
+ 'Int' => '∬',
+ 'int' => '∫',
+ 'intcal' => '⊺',
+ 'integers' => 'ℤ',
+ 'Integral' => '∫',
+ 'intercal' => '⊺',
+ 'Intersection' => '⋂',
+ 'intlarhk' => '⨗',
+ 'intprod' => '⨼',
+ 'InvisibleComma' => '',
+ 'InvisibleTimes' => '',
+ 'IOcy' => 'Ё',
+ 'iocy' => 'ё',
+ 'Iogon' => 'Į',
+ 'iogon' => 'į',
+ 'Iopf' => '𝕀',
+ 'iopf' => '𝕚',
+ 'Iota' => 'Ι',
+ 'iota' => 'ι',
+ 'iprod' => '⨼',
+ 'iquest' => '¿',
+ 'iques' => '¿',
+ 'Iscr' => 'ℐ',
+ 'iscr' => '𝒾',
+ 'isin' => '∈',
+ 'isindot' => '⋵',
+ 'isinE' => '⋹',
+ 'isins' => '⋴',
+ 'isinsv' => '⋳',
+ 'isinv' => '∈',
+ 'it' => '',
+ 'Itilde' => 'Ĩ',
+ 'itilde' => 'ĩ',
+ 'Iukcy' => 'І',
+ 'iukcy' => 'і',
+ 'Iuml' => 'Ï',
+ 'Ium' => 'Ï',
+ 'iuml' => 'ï',
+ 'ium' => 'ï',
+ 'Jcirc' => 'Ĵ',
+ 'jcirc' => 'ĵ',
+ 'Jcy' => 'Й',
+ 'jcy' => 'й',
+ 'Jfr' => '𝔍',
+ 'jfr' => '𝔧',
+ 'jmath' => 'ȷ',
+ 'Jopf' => '𝕁',
+ 'jopf' => '𝕛',
+ 'Jscr' => '𝒥',
+ 'jscr' => '𝒿',
+ 'Jsercy' => 'Ј',
+ 'jsercy' => 'ј',
+ 'Jukcy' => 'Є',
+ 'jukcy' => 'є',
+ 'Kappa' => 'Κ',
+ 'kappa' => 'κ',
+ 'kappav' => 'ϰ',
+ 'Kcedil' => 'Ķ',
+ 'kcedil' => 'ķ',
+ 'Kcy' => 'К',
+ 'kcy' => 'к',
+ 'Kfr' => '𝔎',
+ 'kfr' => '𝔨',
+ 'kgreen' => 'ĸ',
+ 'KHcy' => 'Х',
+ 'khcy' => 'х',
+ 'KJcy' => 'Ќ',
+ 'kjcy' => 'ќ',
+ 'Kopf' => '𝕂',
+ 'kopf' => '𝕜',
+ 'Kscr' => '𝒦',
+ 'kscr' => '𝓀',
+ 'lAarr' => '⇚',
+ 'Lacute' => 'Ĺ',
+ 'lacute' => 'ĺ',
+ 'laemptyv' => '⦴',
+ 'lagran' => 'ℒ',
+ 'Lambda' => 'Λ',
+ 'lambda' => 'λ',
+ 'Lang' => '⟪',
+ 'lang' => '⟨',
+ 'langd' => '⦑',
+ 'langle' => '⟨',
+ 'lap' => '⪅',
+ 'Laplacetrf' => 'ℒ',
+ 'laquo' => '«',
+ 'laqu' => '«',
+ 'Larr' => '↞',
+ 'lArr' => '⇐',
+ 'larr' => '←',
+ 'larrb' => '⇤',
+ 'larrbfs' => '⤟',
+ 'larrfs' => '⤝',
+ 'larrhk' => '↩',
+ 'larrlp' => '↫',
+ 'larrpl' => '⤹',
+ 'larrsim' => '⥳',
+ 'larrtl' => '↢',
+ 'lat' => '⪫',
+ 'lAtail' => '⤛',
+ 'latail' => '⤙',
+ 'late' => '⪭',
+ 'lates' => '⪭︀',
+ 'lBarr' => '⤎',
+ 'lbarr' => '⤌',
+ 'lbbrk' => '❲',
+ 'lbrace' => '{',
+ 'lbrack' => '[',
+ 'lbrke' => '⦋',
+ 'lbrksld' => '⦏',
+ 'lbrkslu' => '⦍',
+ 'Lcaron' => 'Ľ',
+ 'lcaron' => 'ľ',
+ 'Lcedil' => 'Ļ',
+ 'lcedil' => 'ļ',
+ 'lceil' => '⌈',
+ 'lcub' => '{',
+ 'Lcy' => 'Л',
+ 'lcy' => 'л',
+ 'ldca' => '⤶',
+ 'ldquo' => '“',
+ 'ldquor' => '„',
+ 'ldrdhar' => '⥧',
+ 'ldrushar' => '⥋',
+ 'ldsh' => '↲',
+ 'lE' => '≦',
+ 'le' => '≤',
+ 'LeftAngleBracket' => '⟨',
+ 'LeftArrow' => '←',
+ 'Leftarrow' => '⇐',
+ 'leftarrow' => '←',
+ 'LeftArrowBar' => '⇤',
+ 'LeftArrowRightArrow' => '⇆',
+ 'leftarrowtail' => '↢',
+ 'LeftCeiling' => '⌈',
+ 'LeftDoubleBracket' => '⟦',
+ 'LeftDownTeeVector' => '⥡',
+ 'LeftDownVector' => '⇃',
+ 'LeftDownVectorBar' => '⥙',
+ 'LeftFloor' => '⌊',
+ 'leftharpoondown' => '↽',
+ 'leftharpoonup' => '↼',
+ 'leftleftarrows' => '⇇',
+ 'LeftRightArrow' => '↔',
+ 'Leftrightarrow' => '⇔',
+ 'leftrightarrow' => '↔',
+ 'leftrightarrows' => '⇆',
+ 'leftrightharpoons' => '⇋',
+ 'leftrightsquigarrow' => '↭',
+ 'LeftRightVector' => '⥎',
+ 'LeftTee' => '⊣',
+ 'LeftTeeArrow' => '↤',
+ 'LeftTeeVector' => '⥚',
+ 'leftthreetimes' => '⋋',
+ 'LeftTriangle' => '⊲',
+ 'LeftTriangleBar' => '⧏',
+ 'LeftTriangleEqual' => '⊴',
+ 'LeftUpDownVector' => '⥑',
+ 'LeftUpTeeVector' => '⥠',
+ 'LeftUpVector' => '↿',
+ 'LeftUpVectorBar' => '⥘',
+ 'LeftVector' => '↼',
+ 'LeftVectorBar' => '⥒',
+ 'lEg' => '⪋',
+ 'leg' => '⋚',
+ 'leq' => '≤',
+ 'leqq' => '≦',
+ 'leqslant' => '⩽',
+ 'les' => '⩽',
+ 'lescc' => '⪨',
+ 'lesdot' => '⩿',
+ 'lesdoto' => '⪁',
+ 'lesdotor' => '⪃',
+ 'lesg' => '⋚︀',
+ 'lesges' => '⪓',
+ 'lessapprox' => '⪅',
+ 'lessdot' => '⋖',
+ 'lesseqgtr' => '⋚',
+ 'lesseqqgtr' => '⪋',
+ 'LessEqualGreater' => '⋚',
+ 'LessFullEqual' => '≦',
+ 'LessGreater' => '≶',
+ 'lessgtr' => '≶',
+ 'LessLess' => '⪡',
+ 'lesssim' => '≲',
+ 'LessSlantEqual' => '⩽',
+ 'LessTilde' => '≲',
+ 'lfisht' => '⥼',
+ 'lfloor' => '⌊',
+ 'Lfr' => '𝔏',
+ 'lfr' => '𝔩',
+ 'lg' => '≶',
+ 'lgE' => '⪑',
+ 'lHar' => '⥢',
+ 'lhard' => '↽',
+ 'lharu' => '↼',
+ 'lharul' => '⥪',
+ 'lhblk' => '▄',
+ 'LJcy' => 'Љ',
+ 'ljcy' => 'љ',
+ 'Ll' => '⋘',
+ 'll' => '≪',
+ 'llarr' => '⇇',
+ 'llcorner' => '⌞',
+ 'Lleftarrow' => '⇚',
+ 'llhard' => '⥫',
+ 'lltri' => '◺',
+ 'Lmidot' => 'Ŀ',
+ 'lmidot' => 'ŀ',
+ 'lmoust' => '⎰',
+ 'lmoustache' => '⎰',
+ 'lnap' => '⪉',
+ 'lnapprox' => '⪉',
+ 'lnE' => '≨',
+ 'lne' => '⪇',
+ 'lneq' => '⪇',
+ 'lneqq' => '≨',
+ 'lnsim' => '⋦',
+ 'loang' => '⟬',
+ 'loarr' => '⇽',
+ 'lobrk' => '⟦',
+ 'LongLeftArrow' => '⟵',
+ 'Longleftarrow' => '⟸',
+ 'longleftarrow' => '⟵',
+ 'LongLeftRightArrow' => '⟷',
+ 'Longleftrightarrow' => '⟺',
+ 'longleftrightarrow' => '⟷',
+ 'longmapsto' => '⟼',
+ 'LongRightArrow' => '⟶',
+ 'Longrightarrow' => '⟹',
+ 'longrightarrow' => '⟶',
+ 'looparrowleft' => '↫',
+ 'looparrowright' => '↬',
+ 'lopar' => '⦅',
+ 'Lopf' => '𝕃',
+ 'lopf' => '𝕝',
+ 'loplus' => '⨭',
+ 'lotimes' => '⨴',
+ 'lowast' => '∗',
+ 'lowbar' => '_',
+ 'LowerLeftArrow' => '↙',
+ 'LowerRightArrow' => '↘',
+ 'loz' => '◊',
+ 'lozenge' => '◊',
+ 'lozf' => '⧫',
+ 'lpar' => '(',
+ 'lparlt' => '⦓',
+ 'lrarr' => '⇆',
+ 'lrcorner' => '⌟',
+ 'lrhar' => '⇋',
+ 'lrhard' => '⥭',
+ 'lrm' => '',
+ 'lrtri' => '⊿',
+ 'lsaquo' => '‹',
+ 'Lscr' => 'ℒ',
+ 'lscr' => '𝓁',
+ 'Lsh' => '↰',
+ 'lsh' => '↰',
+ 'lsim' => '≲',
+ 'lsime' => '⪍',
+ 'lsimg' => '⪏',
+ 'lsqb' => '[',
+ 'lsquo' => '‘',
+ 'lsquor' => '‚',
+ 'Lstrok' => 'Ł',
+ 'lstrok' => 'ł',
+ 'LT' => '<',
+ 'L' => '<',
+ 'Lt' => '≪',
+ 'lt' => '<',
+ 'l' => '<',
+ 'ltcc' => '⪦',
+ 'ltcir' => '⩹',
+ 'ltdot' => '⋖',
+ 'lthree' => '⋋',
+ 'ltimes' => '⋉',
+ 'ltlarr' => '⥶',
+ 'ltquest' => '⩻',
+ 'ltri' => '◃',
+ 'ltrie' => '⊴',
+ 'ltrif' => '◂',
+ 'ltrPar' => '⦖',
+ 'lurdshar' => '⥊',
+ 'luruhar' => '⥦',
+ 'lvertneqq' => '≨︀',
+ 'lvnE' => '≨︀',
+ 'macr' => '¯',
+ 'mac' => '¯',
+ 'male' => '♂',
+ 'malt' => '✠',
+ 'maltese' => '✠',
+ 'Map' => '⤅',
+ 'map' => '↦',
+ 'mapsto' => '↦',
+ 'mapstodown' => '↧',
+ 'mapstoleft' => '↤',
+ 'mapstoup' => '↥',
+ 'marker' => '▮',
+ 'mcomma' => '⨩',
+ 'Mcy' => 'М',
+ 'mcy' => 'м',
+ 'mdash' => '—',
+ 'mDDot' => '∺',
+ 'measuredangle' => '∡',
+ 'MediumSpace' => ' ',
+ 'Mellintrf' => 'ℳ',
+ 'Mfr' => '𝔐',
+ 'mfr' => '𝔪',
+ 'mho' => '℧',
+ 'micro' => 'µ',
+ 'micr' => 'µ',
+ 'mid' => '∣',
+ 'midast' => '*',
+ 'midcir' => '⫰',
+ 'middot' => '·',
+ 'middo' => '·',
+ 'minus' => '−',
+ 'minusb' => '⊟',
+ 'minusd' => '∸',
+ 'minusdu' => '⨪',
+ 'MinusPlus' => '∓',
+ 'mlcp' => '⫛',
+ 'mldr' => '…',
+ 'mnplus' => '∓',
+ 'models' => '⊧',
+ 'Mopf' => '𝕄',
+ 'mopf' => '𝕞',
+ 'mp' => '∓',
+ 'Mscr' => 'ℳ',
+ 'mscr' => '𝓂',
+ 'mstpos' => '∾',
+ 'Mu' => 'Μ',
+ 'mu' => 'μ',
+ 'multimap' => '⊸',
+ 'mumap' => '⊸',
+ 'nabla' => '∇',
+ 'Nacute' => 'Ń',
+ 'nacute' => 'ń',
+ 'nang' => '∠⃒',
+ 'nap' => '≉',
+ 'napE' => '⩰̸',
+ 'napid' => '≋̸',
+ 'napos' => 'ʼn',
+ 'napprox' => '≉',
+ 'natur' => '♮',
+ 'natural' => '♮',
+ 'naturals' => 'ℕ',
+ 'nbsp' => ' ',
+ 'nbs' => ' ',
+ 'nbump' => '≎̸',
+ 'nbumpe' => '≏̸',
+ 'ncap' => '⩃',
+ 'Ncaron' => 'Ň',
+ 'ncaron' => 'ň',
+ 'Ncedil' => 'Ņ',
+ 'ncedil' => 'ņ',
+ 'ncong' => '≇',
+ 'ncongdot' => '⩭̸',
+ 'ncup' => '⩂',
+ 'Ncy' => 'Н',
+ 'ncy' => 'н',
+ 'ndash' => '–',
+ 'ne' => '≠',
+ 'nearhk' => '⤤',
+ 'neArr' => '⇗',
+ 'nearr' => '↗',
+ 'nearrow' => '↗',
+ 'nedot' => '≐̸',
+ 'NegativeMediumSpace' => '',
+ 'NegativeThickSpace' => '',
+ 'NegativeThinSpace' => '',
+ 'NegativeVeryThinSpace' => '',
+ 'nequiv' => '≢',
+ 'nesear' => '⤨',
+ 'nesim' => '≂̸',
+ 'NestedGreaterGreater' => '≫',
+ 'NestedLessLess' => '≪',
+ 'NewLine' => '
+',
+ 'nexist' => '∄',
+ 'nexists' => '∄',
+ 'Nfr' => '𝔑',
+ 'nfr' => '𝔫',
+ 'ngE' => '≧̸',
+ 'nge' => '≱',
+ 'ngeq' => '≱',
+ 'ngeqq' => '≧̸',
+ 'ngeqslant' => '⩾̸',
+ 'nges' => '⩾̸',
+ 'nGg' => '⋙̸',
+ 'ngsim' => '≵',
+ 'nGt' => '≫⃒',
+ 'ngt' => '≯',
+ 'ngtr' => '≯',
+ 'nGtv' => '≫̸',
+ 'nhArr' => '⇎',
+ 'nharr' => '↮',
+ 'nhpar' => '⫲',
+ 'ni' => '∋',
+ 'nis' => '⋼',
+ 'nisd' => '⋺',
+ 'niv' => '∋',
+ 'NJcy' => 'Њ',
+ 'njcy' => 'њ',
+ 'nlArr' => '⇍',
+ 'nlarr' => '↚',
+ 'nldr' => '‥',
+ 'nlE' => '≦̸',
+ 'nle' => '≰',
+ 'nLeftarrow' => '⇍',
+ 'nleftarrow' => '↚',
+ 'nLeftrightarrow' => '⇎',
+ 'nleftrightarrow' => '↮',
+ 'nleq' => '≰',
+ 'nleqq' => '≦̸',
+ 'nleqslant' => '⩽̸',
+ 'nles' => '⩽̸',
+ 'nless' => '≮',
+ 'nLl' => '⋘̸',
+ 'nlsim' => '≴',
+ 'nLt' => '≪⃒',
+ 'nlt' => '≮',
+ 'nltri' => '⋪',
+ 'nltrie' => '⋬',
+ 'nLtv' => '≪̸',
+ 'nmid' => '∤',
+ 'NoBreak' => '',
+ 'NonBreakingSpace' => ' ',
+ 'Nopf' => 'ℕ',
+ 'nopf' => '𝕟',
+ 'Not' => '⫬',
+ 'not' => '¬',
+ 'no' => '¬',
+ 'NotCongruent' => '≢',
+ 'NotCupCap' => '≭',
+ 'NotDoubleVerticalBar' => '∦',
+ 'NotElement' => '∉',
+ 'NotEqual' => '≠',
+ 'NotEqualTilde' => '≂̸',
+ 'NotExists' => '∄',
+ 'NotGreater' => '≯',
+ 'NotGreaterEqual' => '≱',
+ 'NotGreaterFullEqual' => '≧̸',
+ 'NotGreaterGreater' => '≫̸',
+ 'NotGreaterLess' => '≹',
+ 'NotGreaterSlantEqual' => '⩾̸',
+ 'NotGreaterTilde' => '≵',
+ 'NotHumpDownHump' => '≎̸',
+ 'NotHumpEqual' => '≏̸',
+ 'notin' => '∉',
+ 'notindot' => '⋵̸',
+ 'notinE' => '⋹̸',
+ 'notinva' => '∉',
+ 'notinvb' => '⋷',
+ 'notinvc' => '⋶',
+ 'NotLeftTriangle' => '⋪',
+ 'NotLeftTriangleBar' => '⧏̸',
+ 'NotLeftTriangleEqual' => '⋬',
+ 'NotLess' => '≮',
+ 'NotLessEqual' => '≰',
+ 'NotLessGreater' => '≸',
+ 'NotLessLess' => '≪̸',
+ 'NotLessSlantEqual' => '⩽̸',
+ 'NotLessTilde' => '≴',
+ 'NotNestedGreaterGreater' => '⪢̸',
+ 'NotNestedLessLess' => '⪡̸',
+ 'notni' => '∌',
+ 'notniva' => '∌',
+ 'notnivb' => '⋾',
+ 'notnivc' => '⋽',
+ 'NotPrecedes' => '⊀',
+ 'NotPrecedesEqual' => '⪯̸',
+ 'NotPrecedesSlantEqual' => '⋠',
+ 'NotReverseElement' => '∌',
+ 'NotRightTriangle' => '⋫',
+ 'NotRightTriangleBar' => '⧐̸',
+ 'NotRightTriangleEqual' => '⋭',
+ 'NotSquareSubset' => '⊏̸',
+ 'NotSquareSubsetEqual' => '⋢',
+ 'NotSquareSuperset' => '⊐̸',
+ 'NotSquareSupersetEqual' => '⋣',
+ 'NotSubset' => '⊂⃒',
+ 'NotSubsetEqual' => '⊈',
+ 'NotSucceeds' => '⊁',
+ 'NotSucceedsEqual' => '⪰̸',
+ 'NotSucceedsSlantEqual' => '⋡',
+ 'NotSucceedsTilde' => '≿̸',
+ 'NotSuperset' => '⊃⃒',
+ 'NotSupersetEqual' => '⊉',
+ 'NotTilde' => '≁',
+ 'NotTildeEqual' => '≄',
+ 'NotTildeFullEqual' => '≇',
+ 'NotTildeTilde' => '≉',
+ 'NotVerticalBar' => '∤',
+ 'npar' => '∦',
+ 'nparallel' => '∦',
+ 'nparsl' => '⫽⃥',
+ 'npart' => '∂̸',
+ 'npolint' => '⨔',
+ 'npr' => '⊀',
+ 'nprcue' => '⋠',
+ 'npre' => '⪯̸',
+ 'nprec' => '⊀',
+ 'npreceq' => '⪯̸',
+ 'nrArr' => '⇏',
+ 'nrarr' => '↛',
+ 'nrarrc' => '⤳̸',
+ 'nrarrw' => '↝̸',
+ 'nRightarrow' => '⇏',
+ 'nrightarrow' => '↛',
+ 'nrtri' => '⋫',
+ 'nrtrie' => '⋭',
+ 'nsc' => '⊁',
+ 'nsccue' => '⋡',
+ 'nsce' => '⪰̸',
+ 'Nscr' => '𝒩',
+ 'nscr' => '𝓃',
+ 'nshortmid' => '∤',
+ 'nshortparallel' => '∦',
+ 'nsim' => '≁',
+ 'nsime' => '≄',
+ 'nsimeq' => '≄',
+ 'nsmid' => '∤',
+ 'nspar' => '∦',
+ 'nsqsube' => '⋢',
+ 'nsqsupe' => '⋣',
+ 'nsub' => '⊄',
+ 'nsubE' => '⫅̸',
+ 'nsube' => '⊈',
+ 'nsubset' => '⊂⃒',
+ 'nsubseteq' => '⊈',
+ 'nsubseteqq' => '⫅̸',
+ 'nsucc' => '⊁',
+ 'nsucceq' => '⪰̸',
+ 'nsup' => '⊅',
+ 'nsupE' => '⫆̸',
+ 'nsupe' => '⊉',
+ 'nsupset' => '⊃⃒',
+ 'nsupseteq' => '⊉',
+ 'nsupseteqq' => '⫆̸',
+ 'ntgl' => '≹',
+ 'Ntilde' => 'Ñ',
+ 'Ntild' => 'Ñ',
+ 'ntilde' => 'ñ',
+ 'ntild' => 'ñ',
+ 'ntlg' => '≸',
+ 'ntriangleleft' => '⋪',
+ 'ntrianglelefteq' => '⋬',
+ 'ntriangleright' => '⋫',
+ 'ntrianglerighteq' => '⋭',
+ 'Nu' => 'Ν',
+ 'nu' => 'ν',
+ 'num' => '#',
+ 'numero' => '№',
+ 'numsp' => ' ',
+ 'nvap' => '≍⃒',
+ 'nVDash' => '⊯',
+ 'nVdash' => '⊮',
+ 'nvDash' => '⊭',
+ 'nvdash' => '⊬',
+ 'nvge' => '≥⃒',
+ 'nvgt' => '>⃒',
+ 'nvHarr' => '⤄',
+ 'nvinfin' => '⧞',
+ 'nvlArr' => '⤂',
+ 'nvle' => '≤⃒',
+ 'nvlt' => '<⃒',
+ 'nvltrie' => '⊴⃒',
+ 'nvrArr' => '⤃',
+ 'nvrtrie' => '⊵⃒',
+ 'nvsim' => '∼⃒',
+ 'nwarhk' => '⤣',
+ 'nwArr' => '⇖',
+ 'nwarr' => '↖',
+ 'nwarrow' => '↖',
+ 'nwnear' => '⤧',
+ 'Oacute' => 'Ó',
+ 'Oacut' => 'Ó',
+ 'oacute' => 'ó',
+ 'oacut' => 'ó',
+ 'oast' => '⊛',
+ 'ocir' => 'ô',
+ 'Ocirc' => 'Ô',
+ 'Ocir' => 'Ô',
+ 'ocirc' => 'ô',
+ 'Ocy' => 'О',
+ 'ocy' => 'о',
+ 'odash' => '⊝',
+ 'Odblac' => 'Ő',
+ 'odblac' => 'ő',
+ 'odiv' => '⨸',
+ 'odot' => '⊙',
+ 'odsold' => '⦼',
+ 'OElig' => 'Œ',
+ 'oelig' => 'œ',
+ 'ofcir' => '⦿',
+ 'Ofr' => '𝔒',
+ 'ofr' => '𝔬',
+ 'ogon' => '˛',
+ 'Ograve' => 'Ò',
+ 'Ograv' => 'Ò',
+ 'ograve' => 'ò',
+ 'ograv' => 'ò',
+ 'ogt' => '⧁',
+ 'ohbar' => '⦵',
+ 'ohm' => 'Ω',
+ 'oint' => '∮',
+ 'olarr' => '↺',
+ 'olcir' => '⦾',
+ 'olcross' => '⦻',
+ 'oline' => '‾',
+ 'olt' => '⧀',
+ 'Omacr' => 'Ō',
+ 'omacr' => 'ō',
+ 'Omega' => 'Ω',
+ 'omega' => 'ω',
+ 'Omicron' => 'Ο',
+ 'omicron' => 'ο',
+ 'omid' => '⦶',
+ 'ominus' => '⊖',
+ 'Oopf' => '𝕆',
+ 'oopf' => '𝕠',
+ 'opar' => '⦷',
+ 'OpenCurlyDoubleQuote' => '“',
+ 'OpenCurlyQuote' => '‘',
+ 'operp' => '⦹',
+ 'oplus' => '⊕',
+ 'Or' => '⩔',
+ 'or' => '∨',
+ 'orarr' => '↻',
+ 'ord' => 'º',
+ 'order' => 'ℴ',
+ 'orderof' => 'ℴ',
+ 'ordf' => 'ª',
+ 'ordm' => 'º',
+ 'origof' => '⊶',
+ 'oror' => '⩖',
+ 'orslope' => '⩗',
+ 'orv' => '⩛',
+ 'oS' => 'Ⓢ',
+ 'Oscr' => '𝒪',
+ 'oscr' => 'ℴ',
+ 'Oslash' => 'Ø',
+ 'Oslas' => 'Ø',
+ 'oslash' => 'ø',
+ 'oslas' => 'ø',
+ 'osol' => '⊘',
+ 'Otilde' => 'Õ',
+ 'Otild' => 'Õ',
+ 'otilde' => 'õ',
+ 'otild' => 'õ',
+ 'Otimes' => '⨷',
+ 'otimes' => '⊗',
+ 'otimesas' => '⨶',
+ 'Ouml' => 'Ö',
+ 'Oum' => 'Ö',
+ 'ouml' => 'ö',
+ 'oum' => 'ö',
+ 'ovbar' => '⌽',
+ 'OverBar' => '‾',
+ 'OverBrace' => '⏞',
+ 'OverBracket' => '⎴',
+ 'OverParenthesis' => '⏜',
+ 'par' => '¶',
+ 'para' => '¶',
+ 'parallel' => '∥',
+ 'parsim' => '⫳',
+ 'parsl' => '⫽',
+ 'part' => '∂',
+ 'PartialD' => '∂',
+ 'Pcy' => 'П',
+ 'pcy' => 'п',
+ 'percnt' => '%',
+ 'period' => '.',
+ 'permil' => '‰',
+ 'perp' => '⊥',
+ 'pertenk' => '‱',
+ 'Pfr' => '𝔓',
+ 'pfr' => '𝔭',
+ 'Phi' => 'Φ',
+ 'phi' => 'φ',
+ 'phiv' => 'ϕ',
+ 'phmmat' => 'ℳ',
+ 'phone' => '☎',
+ 'Pi' => 'Π',
+ 'pi' => 'π',
+ 'pitchfork' => '⋔',
+ 'piv' => 'ϖ',
+ 'planck' => 'ℏ',
+ 'planckh' => 'ℎ',
+ 'plankv' => 'ℏ',
+ 'plus' => '+',
+ 'plusacir' => '⨣',
+ 'plusb' => '⊞',
+ 'pluscir' => '⨢',
+ 'plusdo' => '∔',
+ 'plusdu' => '⨥',
+ 'pluse' => '⩲',
+ 'PlusMinus' => '±',
+ 'plusmn' => '±',
+ 'plusm' => '±',
+ 'plussim' => '⨦',
+ 'plustwo' => '⨧',
+ 'pm' => '±',
+ 'Poincareplane' => 'ℌ',
+ 'pointint' => '⨕',
+ 'Popf' => 'ℙ',
+ 'popf' => '𝕡',
+ 'pound' => '£',
+ 'poun' => '£',
+ 'Pr' => '⪻',
+ 'pr' => '≺',
+ 'prap' => '⪷',
+ 'prcue' => '≼',
+ 'prE' => '⪳',
+ 'pre' => '⪯',
+ 'prec' => '≺',
+ 'precapprox' => '⪷',
+ 'preccurlyeq' => '≼',
+ 'Precedes' => '≺',
+ 'PrecedesEqual' => '⪯',
+ 'PrecedesSlantEqual' => '≼',
+ 'PrecedesTilde' => '≾',
+ 'preceq' => '⪯',
+ 'precnapprox' => '⪹',
+ 'precneqq' => '⪵',
+ 'precnsim' => '⋨',
+ 'precsim' => '≾',
+ 'Prime' => '″',
+ 'prime' => '′',
+ 'primes' => 'ℙ',
+ 'prnap' => '⪹',
+ 'prnE' => '⪵',
+ 'prnsim' => '⋨',
+ 'prod' => '∏',
+ 'Product' => '∏',
+ 'profalar' => '⌮',
+ 'profline' => '⌒',
+ 'profsurf' => '⌓',
+ 'prop' => '∝',
+ 'Proportion' => '∷',
+ 'Proportional' => '∝',
+ 'propto' => '∝',
+ 'prsim' => '≾',
+ 'prurel' => '⊰',
+ 'Pscr' => '𝒫',
+ 'pscr' => '𝓅',
+ 'Psi' => 'Ψ',
+ 'psi' => 'ψ',
+ 'puncsp' => ' ',
+ 'Qfr' => '𝔔',
+ 'qfr' => '𝔮',
+ 'qint' => '⨌',
+ 'Qopf' => 'ℚ',
+ 'qopf' => '𝕢',
+ 'qprime' => '⁗',
+ 'Qscr' => '𝒬',
+ 'qscr' => '𝓆',
+ 'quaternions' => 'ℍ',
+ 'quatint' => '⨖',
+ 'quest' => '?',
+ 'questeq' => '≟',
+ 'QUOT' => '"',
+ 'QUO' => '"',
+ 'quot' => '"',
+ 'quo' => '"',
+ 'rAarr' => '⇛',
+ 'race' => '∽̱',
+ 'Racute' => 'Ŕ',
+ 'racute' => 'ŕ',
+ 'radic' => '√',
+ 'raemptyv' => '⦳',
+ 'Rang' => '⟫',
+ 'rang' => '⟩',
+ 'rangd' => '⦒',
+ 'range' => '⦥',
+ 'rangle' => '⟩',
+ 'raquo' => '»',
+ 'raqu' => '»',
+ 'Rarr' => '↠',
+ 'rArr' => '⇒',
+ 'rarr' => '→',
+ 'rarrap' => '⥵',
+ 'rarrb' => '⇥',
+ 'rarrbfs' => '⤠',
+ 'rarrc' => '⤳',
+ 'rarrfs' => '⤞',
+ 'rarrhk' => '↪',
+ 'rarrlp' => '↬',
+ 'rarrpl' => '⥅',
+ 'rarrsim' => '⥴',
+ 'Rarrtl' => '⤖',
+ 'rarrtl' => '↣',
+ 'rarrw' => '↝',
+ 'rAtail' => '⤜',
+ 'ratail' => '⤚',
+ 'ratio' => '∶',
+ 'rationals' => 'ℚ',
+ 'RBarr' => '⤐',
+ 'rBarr' => '⤏',
+ 'rbarr' => '⤍',
+ 'rbbrk' => '❳',
+ 'rbrace' => '}',
+ 'rbrack' => ']',
+ 'rbrke' => '⦌',
+ 'rbrksld' => '⦎',
+ 'rbrkslu' => '⦐',
+ 'Rcaron' => 'Ř',
+ 'rcaron' => 'ř',
+ 'Rcedil' => 'Ŗ',
+ 'rcedil' => 'ŗ',
+ 'rceil' => '⌉',
+ 'rcub' => '}',
+ 'Rcy' => 'Р',
+ 'rcy' => 'р',
+ 'rdca' => '⤷',
+ 'rdldhar' => '⥩',
+ 'rdquo' => '”',
+ 'rdquor' => '”',
+ 'rdsh' => '↳',
+ 'Re' => 'ℜ',
+ 'real' => 'ℜ',
+ 'realine' => 'ℛ',
+ 'realpart' => 'ℜ',
+ 'reals' => 'ℝ',
+ 'rect' => '▭',
+ 'REG' => '®',
+ 'RE' => '®',
+ 'reg' => '®',
+ 're' => '®',
+ 'ReverseElement' => '∋',
+ 'ReverseEquilibrium' => '⇋',
+ 'ReverseUpEquilibrium' => '⥯',
+ 'rfisht' => '⥽',
+ 'rfloor' => '⌋',
+ 'Rfr' => 'ℜ',
+ 'rfr' => '𝔯',
+ 'rHar' => '⥤',
+ 'rhard' => '⇁',
+ 'rharu' => '⇀',
+ 'rharul' => '⥬',
+ 'Rho' => 'Ρ',
+ 'rho' => 'ρ',
+ 'rhov' => 'ϱ',
+ 'RightAngleBracket' => '⟩',
+ 'RightArrow' => '→',
+ 'Rightarrow' => '⇒',
+ 'rightarrow' => '→',
+ 'RightArrowBar' => '⇥',
+ 'RightArrowLeftArrow' => '⇄',
+ 'rightarrowtail' => '↣',
+ 'RightCeiling' => '⌉',
+ 'RightDoubleBracket' => '⟧',
+ 'RightDownTeeVector' => '⥝',
+ 'RightDownVector' => '⇂',
+ 'RightDownVectorBar' => '⥕',
+ 'RightFloor' => '⌋',
+ 'rightharpoondown' => '⇁',
+ 'rightharpoonup' => '⇀',
+ 'rightleftarrows' => '⇄',
+ 'rightleftharpoons' => '⇌',
+ 'rightrightarrows' => '⇉',
+ 'rightsquigarrow' => '↝',
+ 'RightTee' => '⊢',
+ 'RightTeeArrow' => '↦',
+ 'RightTeeVector' => '⥛',
+ 'rightthreetimes' => '⋌',
+ 'RightTriangle' => '⊳',
+ 'RightTriangleBar' => '⧐',
+ 'RightTriangleEqual' => '⊵',
+ 'RightUpDownVector' => '⥏',
+ 'RightUpTeeVector' => '⥜',
+ 'RightUpVector' => '↾',
+ 'RightUpVectorBar' => '⥔',
+ 'RightVector' => '⇀',
+ 'RightVectorBar' => '⥓',
+ 'ring' => '˚',
+ 'risingdotseq' => '≓',
+ 'rlarr' => '⇄',
+ 'rlhar' => '⇌',
+ 'rlm' => '',
+ 'rmoust' => '⎱',
+ 'rmoustache' => '⎱',
+ 'rnmid' => '⫮',
+ 'roang' => '⟭',
+ 'roarr' => '⇾',
+ 'robrk' => '⟧',
+ 'ropar' => '⦆',
+ 'Ropf' => 'ℝ',
+ 'ropf' => '𝕣',
+ 'roplus' => '⨮',
+ 'rotimes' => '⨵',
+ 'RoundImplies' => '⥰',
+ 'rpar' => ')',
+ 'rpargt' => '⦔',
+ 'rppolint' => '⨒',
+ 'rrarr' => '⇉',
+ 'Rrightarrow' => '⇛',
+ 'rsaquo' => '›',
+ 'Rscr' => 'ℛ',
+ 'rscr' => '𝓇',
+ 'Rsh' => '↱',
+ 'rsh' => '↱',
+ 'rsqb' => ']',
+ 'rsquo' => '’',
+ 'rsquor' => '’',
+ 'rthree' => '⋌',
+ 'rtimes' => '⋊',
+ 'rtri' => '▹',
+ 'rtrie' => '⊵',
+ 'rtrif' => '▸',
+ 'rtriltri' => '⧎',
+ 'RuleDelayed' => '⧴',
+ 'ruluhar' => '⥨',
+ 'rx' => '℞',
+ 'Sacute' => 'Ś',
+ 'sacute' => 'ś',
+ 'sbquo' => '‚',
+ 'Sc' => '⪼',
+ 'sc' => '≻',
+ 'scap' => '⪸',
+ 'Scaron' => 'Š',
+ 'scaron' => 'š',
+ 'sccue' => '≽',
+ 'scE' => '⪴',
+ 'sce' => '⪰',
+ 'Scedil' => 'Ş',
+ 'scedil' => 'ş',
+ 'Scirc' => 'Ŝ',
+ 'scirc' => 'ŝ',
+ 'scnap' => '⪺',
+ 'scnE' => '⪶',
+ 'scnsim' => '⋩',
+ 'scpolint' => '⨓',
+ 'scsim' => '≿',
+ 'Scy' => 'С',
+ 'scy' => 'с',
+ 'sdot' => '⋅',
+ 'sdotb' => '⊡',
+ 'sdote' => '⩦',
+ 'searhk' => '⤥',
+ 'seArr' => '⇘',
+ 'searr' => '↘',
+ 'searrow' => '↘',
+ 'sect' => '§',
+ 'sec' => '§',
+ 'semi' => ';',
+ 'seswar' => '⤩',
+ 'setminus' => '∖',
+ 'setmn' => '∖',
+ 'sext' => '✶',
+ 'Sfr' => '𝔖',
+ 'sfr' => '𝔰',
+ 'sfrown' => '⌢',
+ 'sharp' => '♯',
+ 'SHCHcy' => 'Щ',
+ 'shchcy' => 'щ',
+ 'SHcy' => 'Ш',
+ 'shcy' => 'ш',
+ 'ShortDownArrow' => '↓',
+ 'ShortLeftArrow' => '←',
+ 'shortmid' => '∣',
+ 'shortparallel' => '∥',
+ 'ShortRightArrow' => '→',
+ 'ShortUpArrow' => '↑',
+ 'shy' => '',
+ 'sh' => '',
+ 'Sigma' => 'Σ',
+ 'sigma' => 'σ',
+ 'sigmaf' => 'ς',
+ 'sigmav' => 'ς',
+ 'sim' => '∼',
+ 'simdot' => '⩪',
+ 'sime' => '≃',
+ 'simeq' => '≃',
+ 'simg' => '⪞',
+ 'simgE' => '⪠',
+ 'siml' => '⪝',
+ 'simlE' => '⪟',
+ 'simne' => '≆',
+ 'simplus' => '⨤',
+ 'simrarr' => '⥲',
+ 'slarr' => '←',
+ 'SmallCircle' => '∘',
+ 'smallsetminus' => '∖',
+ 'smashp' => '⨳',
+ 'smeparsl' => '⧤',
+ 'smid' => '∣',
+ 'smile' => '⌣',
+ 'smt' => '⪪',
+ 'smte' => '⪬',
+ 'smtes' => '⪬︀',
+ 'SOFTcy' => 'Ь',
+ 'softcy' => 'ь',
+ 'sol' => '/',
+ 'solb' => '⧄',
+ 'solbar' => '⌿',
+ 'Sopf' => '𝕊',
+ 'sopf' => '𝕤',
+ 'spades' => '♠',
+ 'spadesuit' => '♠',
+ 'spar' => '∥',
+ 'sqcap' => '⊓',
+ 'sqcaps' => '⊓︀',
+ 'sqcup' => '⊔',
+ 'sqcups' => '⊔︀',
+ 'Sqrt' => '√',
+ 'sqsub' => '⊏',
+ 'sqsube' => '⊑',
+ 'sqsubset' => '⊏',
+ 'sqsubseteq' => '⊑',
+ 'sqsup' => '⊐',
+ 'sqsupe' => '⊒',
+ 'sqsupset' => '⊐',
+ 'sqsupseteq' => '⊒',
+ 'squ' => '□',
+ 'Square' => '□',
+ 'square' => '□',
+ 'SquareIntersection' => '⊓',
+ 'SquareSubset' => '⊏',
+ 'SquareSubsetEqual' => '⊑',
+ 'SquareSuperset' => '⊐',
+ 'SquareSupersetEqual' => '⊒',
+ 'SquareUnion' => '⊔',
+ 'squarf' => '▪',
+ 'squf' => '▪',
+ 'srarr' => '→',
+ 'Sscr' => '𝒮',
+ 'sscr' => '𝓈',
+ 'ssetmn' => '∖',
+ 'ssmile' => '⌣',
+ 'sstarf' => '⋆',
+ 'Star' => '⋆',
+ 'star' => '☆',
+ 'starf' => '★',
+ 'straightepsilon' => 'ϵ',
+ 'straightphi' => 'ϕ',
+ 'strns' => '¯',
+ 'Sub' => '⋐',
+ 'sub' => '⊂',
+ 'subdot' => '⪽',
+ 'subE' => '⫅',
+ 'sube' => '⊆',
+ 'subedot' => '⫃',
+ 'submult' => '⫁',
+ 'subnE' => '⫋',
+ 'subne' => '⊊',
+ 'subplus' => '⪿',
+ 'subrarr' => '⥹',
+ 'Subset' => '⋐',
+ 'subset' => '⊂',
+ 'subseteq' => '⊆',
+ 'subseteqq' => '⫅',
+ 'SubsetEqual' => '⊆',
+ 'subsetneq' => '⊊',
+ 'subsetneqq' => '⫋',
+ 'subsim' => '⫇',
+ 'subsub' => '⫕',
+ 'subsup' => '⫓',
+ 'succ' => '≻',
+ 'succapprox' => '⪸',
+ 'succcurlyeq' => '≽',
+ 'Succeeds' => '≻',
+ 'SucceedsEqual' => '⪰',
+ 'SucceedsSlantEqual' => '≽',
+ 'SucceedsTilde' => '≿',
+ 'succeq' => '⪰',
+ 'succnapprox' => '⪺',
+ 'succneqq' => '⪶',
+ 'succnsim' => '⋩',
+ 'succsim' => '≿',
+ 'SuchThat' => '∋',
+ 'Sum' => '∑',
+ 'sum' => '∑',
+ 'sung' => '♪',
+ 'Sup' => '⋑',
+ 'sup' => '³',
+ 'sup1' => '¹',
+ 'sup2' => '²',
+ 'sup3' => '³',
+ 'supdot' => '⪾',
+ 'supdsub' => '⫘',
+ 'supE' => '⫆',
+ 'supe' => '⊇',
+ 'supedot' => '⫄',
+ 'Superset' => '⊃',
+ 'SupersetEqual' => '⊇',
+ 'suphsol' => '⟉',
+ 'suphsub' => '⫗',
+ 'suplarr' => '⥻',
+ 'supmult' => '⫂',
+ 'supnE' => '⫌',
+ 'supne' => '⊋',
+ 'supplus' => '⫀',
+ 'Supset' => '⋑',
+ 'supset' => '⊃',
+ 'supseteq' => '⊇',
+ 'supseteqq' => '⫆',
+ 'supsetneq' => '⊋',
+ 'supsetneqq' => '⫌',
+ 'supsim' => '⫈',
+ 'supsub' => '⫔',
+ 'supsup' => '⫖',
+ 'swarhk' => '⤦',
+ 'swArr' => '⇙',
+ 'swarr' => '↙',
+ 'swarrow' => '↙',
+ 'swnwar' => '⤪',
+ 'szlig' => 'ß',
+ 'szli' => 'ß',
+ 'Tab' => ' ',
+ 'target' => '⌖',
+ 'Tau' => 'Τ',
+ 'tau' => 'τ',
+ 'tbrk' => '⎴',
+ 'Tcaron' => 'Ť',
+ 'tcaron' => 'ť',
+ 'Tcedil' => 'Ţ',
+ 'tcedil' => 'ţ',
+ 'Tcy' => 'Т',
+ 'tcy' => 'т',
+ 'tdot' => '⃛',
+ 'telrec' => '⌕',
+ 'Tfr' => '𝔗',
+ 'tfr' => '𝔱',
+ 'there4' => '∴',
+ 'Therefore' => '∴',
+ 'therefore' => '∴',
+ 'Theta' => 'Θ',
+ 'theta' => 'θ',
+ 'thetasym' => 'ϑ',
+ 'thetav' => 'ϑ',
+ 'thickapprox' => '≈',
+ 'thicksim' => '∼',
+ 'ThickSpace' => ' ',
+ 'thinsp' => ' ',
+ 'ThinSpace' => ' ',
+ 'thkap' => '≈',
+ 'thksim' => '∼',
+ 'THORN' => 'Þ',
+ 'THOR' => 'Þ',
+ 'thorn' => 'þ',
+ 'thor' => 'þ',
+ 'Tilde' => '∼',
+ 'tilde' => '˜',
+ 'TildeEqual' => '≃',
+ 'TildeFullEqual' => '≅',
+ 'TildeTilde' => '≈',
+ 'times' => '×',
+ 'time' => '×',
+ 'timesb' => '⊠',
+ 'timesbar' => '⨱',
+ 'timesd' => '⨰',
+ 'tint' => '∭',
+ 'toea' => '⤨',
+ 'top' => '⊤',
+ 'topbot' => '⌶',
+ 'topcir' => '⫱',
+ 'Topf' => '𝕋',
+ 'topf' => '𝕥',
+ 'topfork' => '⫚',
+ 'tosa' => '⤩',
+ 'tprime' => '‴',
+ 'TRADE' => '™',
+ 'trade' => '™',
+ 'triangle' => '▵',
+ 'triangledown' => '▿',
+ 'triangleleft' => '◃',
+ 'trianglelefteq' => '⊴',
+ 'triangleq' => '≜',
+ 'triangleright' => '▹',
+ 'trianglerighteq' => '⊵',
+ 'tridot' => '◬',
+ 'trie' => '≜',
+ 'triminus' => '⨺',
+ 'TripleDot' => '⃛',
+ 'triplus' => '⨹',
+ 'trisb' => '⧍',
+ 'tritime' => '⨻',
+ 'trpezium' => '⏢',
+ 'Tscr' => '𝒯',
+ 'tscr' => '𝓉',
+ 'TScy' => 'Ц',
+ 'tscy' => 'ц',
+ 'TSHcy' => 'Ћ',
+ 'tshcy' => 'ћ',
+ 'Tstrok' => 'Ŧ',
+ 'tstrok' => 'ŧ',
+ 'twixt' => '≬',
+ 'twoheadleftarrow' => '↞',
+ 'twoheadrightarrow' => '↠',
+ 'Uacute' => 'Ú',
+ 'Uacut' => 'Ú',
+ 'uacute' => 'ú',
+ 'uacut' => 'ú',
+ 'Uarr' => '↟',
+ 'uArr' => '⇑',
+ 'uarr' => '↑',
+ 'Uarrocir' => '⥉',
+ 'Ubrcy' => 'Ў',
+ 'ubrcy' => 'ў',
+ 'Ubreve' => 'Ŭ',
+ 'ubreve' => 'ŭ',
+ 'Ucirc' => 'Û',
+ 'Ucir' => 'Û',
+ 'ucirc' => 'û',
+ 'ucir' => 'û',
+ 'Ucy' => 'У',
+ 'ucy' => 'у',
+ 'udarr' => '⇅',
+ 'Udblac' => 'Ű',
+ 'udblac' => 'ű',
+ 'udhar' => '⥮',
+ 'ufisht' => '⥾',
+ 'Ufr' => '𝔘',
+ 'ufr' => '𝔲',
+ 'Ugrave' => 'Ù',
+ 'Ugrav' => 'Ù',
+ 'ugrave' => 'ù',
+ 'ugrav' => 'ù',
+ 'uHar' => '⥣',
+ 'uharl' => '↿',
+ 'uharr' => '↾',
+ 'uhblk' => '▀',
+ 'ulcorn' => '⌜',
+ 'ulcorner' => '⌜',
+ 'ulcrop' => '⌏',
+ 'ultri' => '◸',
+ 'Umacr' => 'Ū',
+ 'umacr' => 'ū',
+ 'uml' => '¨',
+ 'um' => '¨',
+ 'UnderBar' => '_',
+ 'UnderBrace' => '⏟',
+ 'UnderBracket' => '⎵',
+ 'UnderParenthesis' => '⏝',
+ 'Union' => '⋃',
+ 'UnionPlus' => '⊎',
+ 'Uogon' => 'Ų',
+ 'uogon' => 'ų',
+ 'Uopf' => '𝕌',
+ 'uopf' => '𝕦',
+ 'UpArrow' => '↑',
+ 'Uparrow' => '⇑',
+ 'uparrow' => '↑',
+ 'UpArrowBar' => '⤒',
+ 'UpArrowDownArrow' => '⇅',
+ 'UpDownArrow' => '↕',
+ 'Updownarrow' => '⇕',
+ 'updownarrow' => '↕',
+ 'UpEquilibrium' => '⥮',
+ 'upharpoonleft' => '↿',
+ 'upharpoonright' => '↾',
+ 'uplus' => '⊎',
+ 'UpperLeftArrow' => '↖',
+ 'UpperRightArrow' => '↗',
+ 'Upsi' => 'ϒ',
+ 'upsi' => 'υ',
+ 'upsih' => 'ϒ',
+ 'Upsilon' => 'Υ',
+ 'upsilon' => 'υ',
+ 'UpTee' => '⊥',
+ 'UpTeeArrow' => '↥',
+ 'upuparrows' => '⇈',
+ 'urcorn' => '⌝',
+ 'urcorner' => '⌝',
+ 'urcrop' => '⌎',
+ 'Uring' => 'Ů',
+ 'uring' => 'ů',
+ 'urtri' => '◹',
+ 'Uscr' => '𝒰',
+ 'uscr' => '𝓊',
+ 'utdot' => '⋰',
+ 'Utilde' => 'Ũ',
+ 'utilde' => 'ũ',
+ 'utri' => '▵',
+ 'utrif' => '▴',
+ 'uuarr' => '⇈',
+ 'Uuml' => 'Ü',
+ 'Uum' => 'Ü',
+ 'uuml' => 'ü',
+ 'uum' => 'ü',
+ 'uwangle' => '⦧',
+ 'vangrt' => '⦜',
+ 'varepsilon' => 'ϵ',
+ 'varkappa' => 'ϰ',
+ 'varnothing' => '∅',
+ 'varphi' => 'ϕ',
+ 'varpi' => 'ϖ',
+ 'varpropto' => '∝',
+ 'vArr' => '⇕',
+ 'varr' => '↕',
+ 'varrho' => 'ϱ',
+ 'varsigma' => 'ς',
+ 'varsubsetneq' => '⊊︀',
+ 'varsubsetneqq' => '⫋︀',
+ 'varsupsetneq' => '⊋︀',
+ 'varsupsetneqq' => '⫌︀',
+ 'vartheta' => 'ϑ',
+ 'vartriangleleft' => '⊲',
+ 'vartriangleright' => '⊳',
+ 'Vbar' => '⫫',
+ 'vBar' => '⫨',
+ 'vBarv' => '⫩',
+ 'Vcy' => 'В',
+ 'vcy' => 'в',
+ 'VDash' => '⊫',
+ 'Vdash' => '⊩',
+ 'vDash' => '⊨',
+ 'vdash' => '⊢',
+ 'Vdashl' => '⫦',
+ 'Vee' => '⋁',
+ 'vee' => '∨',
+ 'veebar' => '⊻',
+ 'veeeq' => '≚',
+ 'vellip' => '⋮',
+ 'Verbar' => '‖',
+ 'verbar' => '|',
+ 'Vert' => '‖',
+ 'vert' => '|',
+ 'VerticalBar' => '∣',
+ 'VerticalLine' => '|',
+ 'VerticalSeparator' => '❘',
+ 'VerticalTilde' => '≀',
+ 'VeryThinSpace' => ' ',
+ 'Vfr' => '𝔙',
+ 'vfr' => '𝔳',
+ 'vltri' => '⊲',
+ 'vnsub' => '⊂⃒',
+ 'vnsup' => '⊃⃒',
+ 'Vopf' => '𝕍',
+ 'vopf' => '𝕧',
+ 'vprop' => '∝',
+ 'vrtri' => '⊳',
+ 'Vscr' => '𝒱',
+ 'vscr' => '𝓋',
+ 'vsubnE' => '⫋︀',
+ 'vsubne' => '⊊︀',
+ 'vsupnE' => '⫌︀',
+ 'vsupne' => '⊋︀',
+ 'Vvdash' => '⊪',
+ 'vzigzag' => '⦚',
+ 'Wcirc' => 'Ŵ',
+ 'wcirc' => 'ŵ',
+ 'wedbar' => '⩟',
+ 'Wedge' => '⋀',
+ 'wedge' => '∧',
+ 'wedgeq' => '≙',
+ 'weierp' => '℘',
+ 'Wfr' => '𝔚',
+ 'wfr' => '𝔴',
+ 'Wopf' => '𝕎',
+ 'wopf' => '𝕨',
+ 'wp' => '℘',
+ 'wr' => '≀',
+ 'wreath' => '≀',
+ 'Wscr' => '𝒲',
+ 'wscr' => '𝓌',
+ 'xcap' => '⋂',
+ 'xcirc' => '◯',
+ 'xcup' => '⋃',
+ 'xdtri' => '▽',
+ 'Xfr' => '𝔛',
+ 'xfr' => '𝔵',
+ 'xhArr' => '⟺',
+ 'xharr' => '⟷',
+ 'Xi' => 'Ξ',
+ 'xi' => 'ξ',
+ 'xlArr' => '⟸',
+ 'xlarr' => '⟵',
+ 'xmap' => '⟼',
+ 'xnis' => '⋻',
+ 'xodot' => '⨀',
+ 'Xopf' => '𝕏',
+ 'xopf' => '𝕩',
+ 'xoplus' => '⨁',
+ 'xotime' => '⨂',
+ 'xrArr' => '⟹',
+ 'xrarr' => '⟶',
+ 'Xscr' => '𝒳',
+ 'xscr' => '𝓍',
+ 'xsqcup' => '⨆',
+ 'xuplus' => '⨄',
+ 'xutri' => '△',
+ 'xvee' => '⋁',
+ 'xwedge' => '⋀',
+ 'Yacute' => 'Ý',
+ 'Yacut' => 'Ý',
+ 'yacute' => 'ý',
+ 'yacut' => 'ý',
+ 'YAcy' => 'Я',
+ 'yacy' => 'я',
+ 'Ycirc' => 'Ŷ',
+ 'ycirc' => 'ŷ',
+ 'Ycy' => 'Ы',
+ 'ycy' => 'ы',
+ 'yen' => '¥',
+ 'ye' => '¥',
+ 'Yfr' => '𝔜',
+ 'yfr' => '𝔶',
+ 'YIcy' => 'Ї',
+ 'yicy' => 'ї',
+ 'Yopf' => '𝕐',
+ 'yopf' => '𝕪',
+ 'Yscr' => '𝒴',
+ 'yscr' => '𝓎',
+ 'YUcy' => 'Ю',
+ 'yucy' => 'ю',
+ 'Yuml' => 'Ÿ',
+ 'yuml' => 'ÿ',
+ 'yum' => 'ÿ',
+ 'Zacute' => 'Ź',
+ 'zacute' => 'ź',
+ 'Zcaron' => 'Ž',
+ 'zcaron' => 'ž',
+ 'Zcy' => 'З',
+ 'zcy' => 'з',
+ 'Zdot' => 'Ż',
+ 'zdot' => 'ż',
+ 'zeetrf' => 'ℨ',
+ 'ZeroWidthSpace' => '',
+ 'Zeta' => 'Ζ',
+ 'zeta' => 'ζ',
+ 'Zfr' => 'ℨ',
+ 'zfr' => '𝔷',
+ 'ZHcy' => 'Ж',
+ 'zhcy' => 'ж',
+ 'zigrarr' => '⇝',
+ 'Zopf' => 'ℤ',
+ 'zopf' => '𝕫',
+ 'Zscr' => '𝒵',
+ 'zscr' => '𝓏',
+ 'zwj' => '',
+ 'zwnj' => '',
+ );
+}
diff --git a/masterminds/html5/src/HTML5/Exception.php b/masterminds/html5/src/HTML5/Exception.php
new file mode 100644
index 000000000..64e97e6ff
--- /dev/null
+++ b/masterminds/html5/src/HTML5/Exception.php
@@ -0,0 +1,10 @@
+ self::NAMESPACE_HTML,
+ 'svg' => self::NAMESPACE_SVG,
+ 'math' => self::NAMESPACE_MATHML,
+ );
+
+ /**
+ * Holds the always available namespaces (which does not require the XMLNS declaration).
+ *
+ * @var array
+ */
+ protected $implicitNamespaces = array(
+ 'xml' => self::NAMESPACE_XML,
+ 'xmlns' => self::NAMESPACE_XMLNS,
+ 'xlink' => self::NAMESPACE_XLINK,
+ );
+
+ /**
+ * Holds a stack of currently active namespaces.
+ *
+ * @var array
+ */
+ protected $nsStack = array();
+
+ /**
+ * Holds the number of namespaces declared by a node.
+ *
+ * @var array
+ */
+ protected $pushes = array();
+
+ /**
+ * Defined in 8.2.5.
+ */
+ const IM_INITIAL = 0;
+
+ const IM_BEFORE_HTML = 1;
+
+ const IM_BEFORE_HEAD = 2;
+
+ const IM_IN_HEAD = 3;
+
+ const IM_IN_HEAD_NOSCRIPT = 4;
+
+ const IM_AFTER_HEAD = 5;
+
+ const IM_IN_BODY = 6;
+
+ const IM_TEXT = 7;
+
+ const IM_IN_TABLE = 8;
+
+ const IM_IN_TABLE_TEXT = 9;
+
+ const IM_IN_CAPTION = 10;
+
+ const IM_IN_COLUMN_GROUP = 11;
+
+ const IM_IN_TABLE_BODY = 12;
+
+ const IM_IN_ROW = 13;
+
+ const IM_IN_CELL = 14;
+
+ const IM_IN_SELECT = 15;
+
+ const IM_IN_SELECT_IN_TABLE = 16;
+
+ const IM_AFTER_BODY = 17;
+
+ const IM_IN_FRAMESET = 18;
+
+ const IM_AFTER_FRAMESET = 19;
+
+ const IM_AFTER_AFTER_BODY = 20;
+
+ const IM_AFTER_AFTER_FRAMESET = 21;
+
+ const IM_IN_SVG = 22;
+
+ const IM_IN_MATHML = 23;
+
+ protected $options = array();
+
+ protected $stack = array();
+
+ protected $current; // Pointer in the tag hierarchy.
+ protected $rules;
+ protected $doc;
+
+ protected $frag;
+
+ protected $processor;
+
+ protected $insertMode = 0;
+
+ /**
+ * Track if we are in an element that allows only inline child nodes.
+ *
+ * @var string|null
+ */
+ protected $onlyInline;
+
+ /**
+ * Quirks mode is enabled by default.
+ * Any document that is missing the DT will be considered to be in quirks mode.
+ */
+ protected $quirks = true;
+
+ protected $errors = array();
+
+ public function __construct($isFragment = false, array $options = array())
+ {
+ $this->options = $options;
+
+ if (isset($options[self::OPT_TARGET_DOC])) {
+ $this->doc = $options[self::OPT_TARGET_DOC];
+ } else {
+ $impl = new \DOMImplementation();
+ // XXX:
+ // Create the doctype. For now, we are always creating HTML5
+ // documents, and attempting to up-convert any older DTDs to HTML5.
+ $dt = $impl->createDocumentType('html');
+ // $this->doc = \DOMImplementation::createDocument(NULL, 'html', $dt);
+ $this->doc = $impl->createDocument(null, '', $dt);
+ $this->doc->encoding = !empty($options['encoding']) ? $options['encoding'] : 'UTF-8';
+ }
+
+ $this->errors = array();
+
+ $this->current = $this->doc; // ->documentElement;
+
+ // Create a rules engine for tags.
+ $this->rules = new TreeBuildingRules();
+
+ $implicitNS = array();
+ if (isset($this->options[self::OPT_IMPLICIT_NS])) {
+ $implicitNS = $this->options[self::OPT_IMPLICIT_NS];
+ } elseif (isset($this->options['implicitNamespaces'])) {
+ $implicitNS = $this->options['implicitNamespaces'];
+ }
+
+ // Fill $nsStack with the defalut HTML5 namespaces, plus the "implicitNamespaces" array taken form $options
+ array_unshift($this->nsStack, $implicitNS + array('' => self::NAMESPACE_HTML) + $this->implicitNamespaces);
+
+ if ($isFragment) {
+ $this->insertMode = static::IM_IN_BODY;
+ $this->frag = $this->doc->createDocumentFragment();
+ $this->current = $this->frag;
+ }
+ }
+
+ /**
+ * Get the document.
+ */
+ public function document()
+ {
+ return $this->doc;
+ }
+
+ /**
+ * Get the DOM fragment for the body.
+ *
+ * This returns a DOMNodeList because a fragment may have zero or more
+ * DOMNodes at its root.
+ *
+ * @see http://www.w3.org/TR/2012/CR-html5-20121217/syntax.html#concept-frag-parse-context
+ *
+ * @return \DOMDocumentFragment
+ */
+ public function fragment()
+ {
+ return $this->frag;
+ }
+
+ /**
+ * Provide an instruction processor.
+ *
+ * This is used for handling Processor Instructions as they are
+ * inserted. If omitted, PI's are inserted directly into the DOM tree.
+ *
+ * @param InstructionProcessor $proc
+ */
+ public function setInstructionProcessor(InstructionProcessor $proc)
+ {
+ $this->processor = $proc;
+ }
+
+ public function doctype($name, $idType = 0, $id = null, $quirks = false)
+ {
+ // This is used solely for setting quirks mode. Currently we don't
+ // try to preserve the inbound DT. We convert it to HTML5.
+ $this->quirks = $quirks;
+
+ if ($this->insertMode > static::IM_INITIAL) {
+ $this->parseError('Illegal placement of DOCTYPE tag. Ignoring: ' . $name);
+
+ return;
+ }
+
+ $this->insertMode = static::IM_BEFORE_HTML;
+ }
+
+ /**
+ * Process the start tag.
+ *
+ * @todo - XMLNS namespace handling (we need to parse, even if it's not valid)
+ * - XLink, MathML and SVG namespace handling
+ * - Omission rules: 8.1.2.4 Optional tags
+ *
+ * @param string $name
+ * @param array $attributes
+ * @param bool $selfClosing
+ *
+ * @return int
+ */
+ public function startTag($name, $attributes = array(), $selfClosing = false)
+ {
+ $lname = $this->normalizeTagName($name);
+
+ // Make sure we have an html element.
+ if (!$this->doc->documentElement && 'html' !== $name && !$this->frag) {
+ $this->startTag('html');
+ }
+
+ // Set quirks mode if we're at IM_INITIAL with no doctype.
+ if ($this->insertMode === static::IM_INITIAL) {
+ $this->quirks = true;
+ $this->parseError('No DOCTYPE specified.');
+ }
+
+ // SPECIAL TAG HANDLING:
+ // Spec says do this, and "don't ask."
+ // find the spec where this is defined... looks problematic
+ if ('image' === $name && !($this->insertMode === static::IM_IN_SVG || $this->insertMode === static::IM_IN_MATHML)) {
+ $name = 'img';
+ }
+
+ // Autoclose p tags where appropriate.
+ if ($this->insertMode >= static::IM_IN_BODY && Elements::isA($name, Elements::AUTOCLOSE_P)) {
+ $this->autoclose('p');
+ }
+
+ // Set insert mode:
+ switch ($name) {
+ case 'html':
+ $this->insertMode = static::IM_BEFORE_HEAD;
+ break;
+ case 'head':
+ if ($this->insertMode > static::IM_BEFORE_HEAD) {
+ $this->parseError('Unexpected head tag outside of head context.');
+ } else {
+ $this->insertMode = static::IM_IN_HEAD;
+ }
+ break;
+ case 'body':
+ $this->insertMode = static::IM_IN_BODY;
+ break;
+ case 'svg':
+ $this->insertMode = static::IM_IN_SVG;
+ break;
+ case 'math':
+ $this->insertMode = static::IM_IN_MATHML;
+ break;
+ case 'noscript':
+ if ($this->insertMode === static::IM_IN_HEAD) {
+ $this->insertMode = static::IM_IN_HEAD_NOSCRIPT;
+ }
+ break;
+ }
+
+ // Special case handling for SVG.
+ if ($this->insertMode === static::IM_IN_SVG) {
+ $lname = Elements::normalizeSvgElement($lname);
+ }
+
+ $pushes = 0;
+ // when we found a tag thats appears inside $nsRoots, we have to switch the defalut namespace
+ if (isset($this->nsRoots[$lname]) && $this->nsStack[0][''] !== $this->nsRoots[$lname]) {
+ array_unshift($this->nsStack, array(
+ '' => $this->nsRoots[$lname],
+ ) + $this->nsStack[0]);
+ ++$pushes;
+ }
+ $needsWorkaround = false;
+ if (isset($this->options['xmlNamespaces']) && $this->options['xmlNamespaces']) {
+ // when xmlNamespaces is true a and we found a 'xmlns' or 'xmlns:*' attribute, we should add a new item to the $nsStack
+ foreach ($attributes as $aName => $aVal) {
+ if ('xmlns' === $aName) {
+ $needsWorkaround = $aVal;
+ array_unshift($this->nsStack, array(
+ '' => $aVal,
+ ) + $this->nsStack[0]);
+ ++$pushes;
+ } elseif ('xmlns' === (($pos = strpos($aName, ':')) ? substr($aName, 0, $pos) : '')) {
+ array_unshift($this->nsStack, array(
+ substr($aName, $pos + 1) => $aVal,
+ ) + $this->nsStack[0]);
+ ++$pushes;
+ }
+ }
+ }
+
+ if ($this->onlyInline && Elements::isA($lname, Elements::BLOCK_TAG)) {
+ $this->autoclose($this->onlyInline);
+ $this->onlyInline = null;
+ }
+
+ // some elements as table related tags might have optional end tags that force us to auto close multiple tags
+ // https://www.w3.org/TR/html401/struct/tables.html
+ if ($this->current instanceof \DOMElement && isset(Elements::$optionalEndElementsParentsToClose[$lname])) {
+ foreach (Elements::$optionalEndElementsParentsToClose[$lname] as $parentElName) {
+ if ($this->current instanceof \DOMElement && $this->current->tagName === $parentElName) {
+ $this->autoclose($parentElName);
+ }
+ }
+ }
+
+ try {
+ $prefix = ($pos = strpos($lname, ':')) ? substr($lname, 0, $pos) : '';
+
+ if (false !== $needsWorkaround) {
+ $xml = "<$lname xmlns=\"$needsWorkaround\" " . (strlen($prefix) && isset($this->nsStack[0][$prefix]) ? ("xmlns:$prefix=\"" . $this->nsStack[0][$prefix] . '"') : '') . '/>';
+
+ $frag = new \DOMDocument('1.0', 'UTF-8');
+ $frag->loadXML($xml);
+
+ $ele = $this->doc->importNode($frag->documentElement, true);
+ } else {
+ if (!isset($this->nsStack[0][$prefix]) || ('' === $prefix && isset($this->options[self::OPT_DISABLE_HTML_NS]) && $this->options[self::OPT_DISABLE_HTML_NS])) {
+ $ele = $this->doc->createElement($lname);
+ } else {
+ $ele = $this->doc->createElementNS($this->nsStack[0][$prefix], $lname);
+ }
+ }
+ } catch (\DOMException $e) {
+ $this->parseError("Illegal tag name: <$lname>. Replaced with .");
+ $ele = $this->doc->createElement('invalid');
+ }
+
+ if (Elements::isA($lname, Elements::BLOCK_ONLY_INLINE)) {
+ $this->onlyInline = $lname;
+ }
+
+ // When we add some namespacess, we have to track them. Later, when "endElement" is invoked, we have to remove them.
+ // When we are on a void tag, we do not need to care about namesapce nesting.
+ if ($pushes > 0 && !Elements::isA($name, Elements::VOID_TAG)) {
+ // PHP tends to free the memory used by DOM,
+ // to avoid spl_object_hash collisions whe have to avoid garbage collection of $ele storing it into $pushes
+ // see https://bugs.php.net/bug.php?id=67459
+ $this->pushes[spl_object_hash($ele)] = array($pushes, $ele);
+ }
+
+ foreach ($attributes as $aName => $aVal) {
+ // xmlns attributes can't be set
+ if ('xmlns' === $aName) {
+ continue;
+ }
+
+ if ($this->insertMode === static::IM_IN_SVG) {
+ $aName = Elements::normalizeSvgAttribute($aName);
+ } elseif ($this->insertMode === static::IM_IN_MATHML) {
+ $aName = Elements::normalizeMathMlAttribute($aName);
+ }
+
+ $aVal = (string) $aVal;
+
+ try {
+ $prefix = ($pos = strpos($aName, ':')) ? substr($aName, 0, $pos) : false;
+
+ if ('xmlns' === $prefix) {
+ $ele->setAttributeNS(self::NAMESPACE_XMLNS, $aName, $aVal);
+ } elseif (false !== $prefix && isset($this->nsStack[0][$prefix])) {
+ $ele->setAttributeNS($this->nsStack[0][$prefix], $aName, $aVal);
+ } else {
+ $ele->setAttribute($aName, $aVal);
+ }
+ } catch (\DOMException $e) {
+ $this->parseError("Illegal attribute name for tag $name. Ignoring: $aName");
+ continue;
+ }
+
+ // This is necessary on a non-DTD schema, like HTML5.
+ if ('id' === $aName) {
+ $ele->setIdAttribute('id', true);
+ }
+ }
+
+ if ($this->frag !== $this->current && $this->rules->hasRules($name)) {
+ // Some elements have special processing rules. Handle those separately.
+ $this->current = $this->rules->evaluate($ele, $this->current);
+ } else {
+ // Otherwise, it's a standard element.
+ $this->current->appendChild($ele);
+
+ if (!Elements::isA($name, Elements::VOID_TAG)) {
+ $this->current = $ele;
+ }
+
+ // Self-closing tags should only be respected on foreign elements
+ // (and are implied on void elements)
+ // See: https://www.w3.org/TR/html5/syntax.html#start-tags
+ if (Elements::isHtml5Element($name)) {
+ $selfClosing = false;
+ }
+ }
+
+ // This is sort of a last-ditch attempt to correct for cases where no head/body
+ // elements are provided.
+ if ($this->insertMode <= static::IM_BEFORE_HEAD && 'head' !== $name && 'html' !== $name) {
+ $this->insertMode = static::IM_IN_BODY;
+ }
+
+ // When we are on a void tag, we do not need to care about namesapce nesting,
+ // but we have to remove the namespaces pushed to $nsStack.
+ if ($pushes > 0 && Elements::isA($name, Elements::VOID_TAG)) {
+ // remove the namespaced definded by current node
+ for ($i = 0; $i < $pushes; ++$i) {
+ array_shift($this->nsStack);
+ }
+ }
+
+ if ($selfClosing) {
+ $this->endTag($name);
+ }
+
+ // Return the element mask, which the tokenizer can then use to set
+ // various processing rules.
+ return Elements::element($name);
+ }
+
+ public function endTag($name)
+ {
+ $lname = $this->normalizeTagName($name);
+
+ // Special case within 12.2.6.4.7: An end tag whose tag name is "br" should be treated as an opening tag
+ if ('br' === $name) {
+ $this->parseError('Closing tag encountered for void element br.');
+
+ $this->startTag('br');
+ }
+ // Ignore closing tags for other unary elements.
+ elseif (Elements::isA($name, Elements::VOID_TAG)) {
+ return;
+ }
+
+ if ($this->insertMode <= static::IM_BEFORE_HTML) {
+ // 8.2.5.4.2
+ if (in_array($name, array(
+ 'html',
+ 'br',
+ 'head',
+ 'title',
+ ))) {
+ $this->startTag('html');
+ $this->endTag($name);
+ $this->insertMode = static::IM_BEFORE_HEAD;
+
+ return;
+ }
+
+ // Ignore the tag.
+ $this->parseError('Illegal closing tag at global scope.');
+
+ return;
+ }
+
+ // Special case handling for SVG.
+ if ($this->insertMode === static::IM_IN_SVG) {
+ $lname = Elements::normalizeSvgElement($lname);
+ }
+
+ $cid = spl_object_hash($this->current);
+
+ // XXX: HTML has no parent. What do we do, though,
+ // if this element appears in the wrong place?
+ if ('html' === $lname) {
+ return;
+ }
+
+ // remove the namespaced definded by current node
+ if (isset($this->pushes[$cid])) {
+ for ($i = 0; $i < $this->pushes[$cid][0]; ++$i) {
+ array_shift($this->nsStack);
+ }
+ unset($this->pushes[$cid]);
+ }
+
+ if (!$this->autoclose($lname)) {
+ $this->parseError('Could not find closing tag for ' . $lname);
+ }
+
+ switch ($lname) {
+ case 'head':
+ $this->insertMode = static::IM_AFTER_HEAD;
+ break;
+ case 'body':
+ $this->insertMode = static::IM_AFTER_BODY;
+ break;
+ case 'svg':
+ case 'mathml':
+ $this->insertMode = static::IM_IN_BODY;
+ break;
+ }
+ }
+
+ public function comment($cdata)
+ {
+ // TODO: Need to handle case where comment appears outside of the HTML tag.
+ $node = $this->doc->createComment($cdata);
+ $this->current->appendChild($node);
+ }
+
+ public function text($data)
+ {
+ // XXX: Hmmm.... should we really be this strict?
+ if ($this->insertMode < static::IM_IN_HEAD) {
+ // Per '8.2.5.4.3 The "before head" insertion mode' the characters
+ // " \t\n\r\f" should be ignored but no mention of a parse error. This is
+ // practical as most documents contain these characters. Other text is not
+ // expected here so recording a parse error is necessary.
+ $dataTmp = trim($data, " \t\n\r\f");
+ if (!empty($dataTmp)) {
+ // fprintf(STDOUT, "Unexpected insert mode: %d", $this->insertMode);
+ $this->parseError('Unexpected text. Ignoring: ' . $dataTmp);
+ }
+
+ return;
+ }
+ // fprintf(STDOUT, "Appending text %s.", $data);
+ $node = $this->doc->createTextNode($data);
+ $this->current->appendChild($node);
+ }
+
+ public function eof()
+ {
+ // If the $current isn't the $root, do we need to do anything?
+ }
+
+ public function parseError($msg, $line = 0, $col = 0)
+ {
+ $this->errors[] = sprintf('Line %d, Col %d: %s', $line, $col, $msg);
+ }
+
+ public function getErrors()
+ {
+ return $this->errors;
+ }
+
+ public function cdata($data)
+ {
+ $node = $this->doc->createCDATASection($data);
+ $this->current->appendChild($node);
+ }
+
+ public function processingInstruction($name, $data = null)
+ {
+ // XXX: Ignore initial XML declaration, per the spec.
+ if ($this->insertMode === static::IM_INITIAL && 'xml' === strtolower($name)) {
+ return;
+ }
+
+ // Important: The processor may modify the current DOM tree however it sees fit.
+ if ($this->processor instanceof InstructionProcessor) {
+ $res = $this->processor->process($this->current, $name, $data);
+ if (!empty($res)) {
+ $this->current = $res;
+ }
+
+ return;
+ }
+
+ // Otherwise, this is just a dumb PI element.
+ $node = $this->doc->createProcessingInstruction($name, $data);
+
+ $this->current->appendChild($node);
+ }
+
+ // ==========================================================================
+ // UTILITIES
+ // ==========================================================================
+
+ /**
+ * Apply normalization rules to a tag name.
+ * See sections 2.9 and 8.1.2.
+ *
+ * @param string $tagName
+ *
+ * @return string The normalized tag name.
+ */
+ protected function normalizeTagName($tagName)
+ {
+ /*
+ * Section 2.9 suggests that we should not do this. if (strpos($name, ':') !== false) { // We know from the grammar that there must be at least one other // char besides :, since : is not a legal tag start. $parts = explode(':', $name); return array_pop($parts); }
+ */
+ return $tagName;
+ }
+
+ protected function quirksTreeResolver($name)
+ {
+ throw new \Exception('Not implemented.');
+ }
+
+ /**
+ * Automatically climb the tree and close the closest node with the matching $tag.
+ *
+ * @param string $tagName
+ *
+ * @return bool
+ */
+ protected function autoclose($tagName)
+ {
+ $working = $this->current;
+ do {
+ if (XML_ELEMENT_NODE !== $working->nodeType) {
+ return false;
+ }
+ if ($working->tagName === $tagName) {
+ $this->current = $working->parentNode;
+
+ return true;
+ }
+ } while ($working = $working->parentNode);
+
+ return false;
+ }
+
+ /**
+ * Checks if the given tagname is an ancestor of the present candidate.
+ *
+ * If $this->current or anything above $this->current matches the given tag
+ * name, this returns true.
+ *
+ * @param string $tagName
+ *
+ * @return bool
+ */
+ protected function isAncestor($tagName)
+ {
+ $candidate = $this->current;
+ while (XML_ELEMENT_NODE === $candidate->nodeType) {
+ if ($candidate->tagName === $tagName) {
+ return true;
+ }
+ $candidate = $candidate->parentNode;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if the immediate parent element is of the given tagname.
+ *
+ * @param string $tagName
+ *
+ * @return bool
+ */
+ protected function isParent($tagName)
+ {
+ return $this->current->tagName === $tagName;
+ }
+}
diff --git a/masterminds/html5/src/HTML5/Parser/EventHandler.php b/masterminds/html5/src/HTML5/Parser/EventHandler.php
new file mode 100644
index 000000000..9893a718b
--- /dev/null
+++ b/masterminds/html5/src/HTML5/Parser/EventHandler.php
@@ -0,0 +1,114 @@
+).
+ *
+ * @return int one of the Tokenizer::TEXTMODE_* constants
+ */
+ public function startTag($name, $attributes = array(), $selfClosing = false);
+
+ /**
+ * An end-tag.
+ */
+ public function endTag($name);
+
+ /**
+ * A comment section (unparsed character data).
+ */
+ public function comment($cdata);
+
+ /**
+ * A unit of parsed character data.
+ *
+ * Entities in this text are *already decoded*.
+ */
+ public function text($cdata);
+
+ /**
+ * Indicates that the document has been entirely processed.
+ */
+ public function eof();
+
+ /**
+ * Emitted when the parser encounters an error condition.
+ */
+ public function parseError($msg, $line, $col);
+
+ /**
+ * A CDATA section.
+ *
+ * @param string $data
+ * The unparsed character data
+ */
+ public function cdata($data);
+
+ /**
+ * This is a holdover from the XML spec.
+ *
+ * While user agents don't get PIs, server-side does.
+ *
+ * @param string $name The name of the processor (e.g. 'php').
+ * @param string $data The unparsed data.
+ */
+ public function processingInstruction($name, $data = null);
+}
diff --git a/masterminds/html5/src/HTML5/Parser/FileInputStream.php b/masterminds/html5/src/HTML5/Parser/FileInputStream.php
new file mode 100644
index 000000000..b081ed96b
--- /dev/null
+++ b/masterminds/html5/src/HTML5/Parser/FileInputStream.php
@@ -0,0 +1,33 @@
+errors = UTF8Utils::checkForIllegalCodepoints($data);
+
+ $data = $this->replaceLinefeeds($data);
+
+ $this->data = $data;
+ $this->char = 0;
+ $this->EOF = strlen($data);
+ }
+
+ /**
+ * Check if upcomming chars match the given sequence.
+ *
+ * This will read the stream for the $sequence. If it's
+ * found, this will return true. If not, return false.
+ * Since this unconsumes any chars it reads, the caller
+ * will still need to read the next sequence, even if
+ * this returns true.
+ *
+ * Example: $this->scanner->sequenceMatches('') will
+ * see if the input stream is at the start of a
+ * '' string.
+ *
+ * @param string $sequence
+ * @param bool $caseSensitive
+ *
+ * @return bool
+ */
+ public function sequenceMatches($sequence, $caseSensitive = true)
+ {
+ $portion = substr($this->data, $this->char, strlen($sequence));
+
+ return $caseSensitive ? $portion === $sequence : 0 === strcasecmp($portion, $sequence);
+ }
+
+ /**
+ * Get the current position.
+ *
+ * @return int The current intiger byte position.
+ */
+ public function position()
+ {
+ return $this->char;
+ }
+
+ /**
+ * Take a peek at the next character in the data.
+ *
+ * @return string The next character.
+ */
+ public function peek()
+ {
+ if (($this->char + 1) < $this->EOF) {
+ return $this->data[$this->char + 1];
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the next character.
+ * Note: This advances the pointer.
+ *
+ * @return string The next character.
+ */
+ public function next()
+ {
+ ++$this->char;
+
+ if ($this->char < $this->EOF) {
+ return $this->data[$this->char];
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the current character.
+ * Note, this does not advance the pointer.
+ *
+ * @return string The current character.
+ */
+ public function current()
+ {
+ if ($this->char < $this->EOF) {
+ return $this->data[$this->char];
+ }
+
+ return false;
+ }
+
+ /**
+ * Silently consume N chars.
+ *
+ * @param int $count
+ */
+ public function consume($count = 1)
+ {
+ $this->char += $count;
+ }
+
+ /**
+ * Unconsume some of the data.
+ * This moves the data pointer backwards.
+ *
+ * @param int $howMany The number of characters to move the pointer back.
+ */
+ public function unconsume($howMany = 1)
+ {
+ if (($this->char - $howMany) >= 0) {
+ $this->char -= $howMany;
+ }
+ }
+
+ /**
+ * Get the next group of that contains hex characters.
+ * Note, along with getting the characters the pointer in the data will be
+ * moved as well.
+ *
+ * @return string The next group that is hex characters.
+ */
+ public function getHex()
+ {
+ return $this->doCharsWhile(static::CHARS_HEX);
+ }
+
+ /**
+ * Get the next group of characters that are ASCII Alpha characters.
+ * Note, along with getting the characters the pointer in the data will be
+ * moved as well.
+ *
+ * @return string The next group of ASCII alpha characters.
+ */
+ public function getAsciiAlpha()
+ {
+ return $this->doCharsWhile(static::CHARS_ALPHA);
+ }
+
+ /**
+ * Get the next group of characters that are ASCII Alpha characters and numbers.
+ * Note, along with getting the characters the pointer in the data will be
+ * moved as well.
+ *
+ * @return string The next group of ASCII alpha characters and numbers.
+ */
+ public function getAsciiAlphaNum()
+ {
+ return $this->doCharsWhile(static::CHARS_ALNUM);
+ }
+
+ /**
+ * Get the next group of numbers.
+ * Note, along with getting the characters the pointer in the data will be
+ * moved as well.
+ *
+ * @return string The next group of numbers.
+ */
+ public function getNumeric()
+ {
+ return $this->doCharsWhile('0123456789');
+ }
+
+ /**
+ * Consume whitespace.
+ * Whitespace in HTML5 is: formfeed, tab, newline, space.
+ *
+ * @return int The length of the matched whitespaces.
+ */
+ public function whitespace()
+ {
+ if ($this->char >= $this->EOF) {
+ return false;
+ }
+
+ $len = strspn($this->data, "\n\t\f ", $this->char);
+
+ $this->char += $len;
+
+ return $len;
+ }
+
+ /**
+ * Returns the current line that is being consumed.
+ *
+ * @return int The current line number.
+ */
+ public function currentLine()
+ {
+ if (empty($this->EOF) || 0 === $this->char) {
+ return 1;
+ }
+
+ // Add one to $this->char because we want the number for the next
+ // byte to be processed.
+ return substr_count($this->data, "\n", 0, min($this->char, $this->EOF)) + 1;
+ }
+
+ /**
+ * Read chars until something in the mask is encountered.
+ *
+ * @param string $mask
+ *
+ * @return mixed
+ */
+ public function charsUntil($mask)
+ {
+ return $this->doCharsUntil($mask);
+ }
+
+ /**
+ * Read chars as long as the mask matches.
+ *
+ * @param string $mask
+ *
+ * @return int
+ */
+ public function charsWhile($mask)
+ {
+ return $this->doCharsWhile($mask);
+ }
+
+ /**
+ * Returns the current column of the current line that the tokenizer is at.
+ *
+ * Newlines are column 0. The first char after a newline is column 1.
+ *
+ * @return int The column number.
+ */
+ public function columnOffset()
+ {
+ // Short circuit for the first char.
+ if (0 === $this->char) {
+ return 0;
+ }
+
+ // strrpos is weird, and the offset needs to be negative for what we
+ // want (i.e., the last \n before $this->char). This needs to not have
+ // one (to make it point to the next character, the one we want the
+ // position of) added to it because strrpos's behaviour includes the
+ // final offset byte.
+ $backwardFrom = $this->char - 1 - strlen($this->data);
+ $lastLine = strrpos($this->data, "\n", $backwardFrom);
+
+ // However, for here we want the length up until the next byte to be
+ // processed, so add one to the current byte ($this->char).
+ if (false !== $lastLine) {
+ $findLengthOf = substr($this->data, $lastLine + 1, $this->char - 1 - $lastLine);
+ } else {
+ // After a newline.
+ $findLengthOf = substr($this->data, 0, $this->char);
+ }
+
+ return UTF8Utils::countChars($findLengthOf);
+ }
+
+ /**
+ * Get all characters until EOF.
+ *
+ * This consumes characters until the EOF.
+ *
+ * @return int The number of characters remaining.
+ */
+ public function remainingChars()
+ {
+ if ($this->char < $this->EOF) {
+ $data = substr($this->data, $this->char);
+ $this->char = $this->EOF;
+
+ return $data;
+ }
+
+ return ''; // false;
+ }
+
+ /**
+ * Replace linefeed characters according to the spec.
+ *
+ * @param $data
+ *
+ * @return string
+ */
+ private function replaceLinefeeds($data)
+ {
+ /*
+ * U+000D CARRIAGE RETURN (CR) characters and U+000A LINE FEED (LF) characters are treated specially.
+ * Any CR characters that are followed by LF characters must be removed, and any CR characters not
+ * followed by LF characters must be converted to LF characters. Thus, newlines in HTML DOMs are
+ * represented by LF characters, and there are never any CR characters in the input to the tokenization
+ * stage.
+ */
+ $crlfTable = array(
+ "\0" => "\xEF\xBF\xBD",
+ "\r\n" => "\n",
+ "\r" => "\n",
+ );
+
+ return strtr($data, $crlfTable);
+ }
+
+ /**
+ * Read to a particular match (or until $max bytes are consumed).
+ *
+ * This operates on byte sequences, not characters.
+ *
+ * Matches as far as possible until we reach a certain set of bytes
+ * and returns the matched substring.
+ *
+ * @param string $bytes Bytes to match.
+ * @param int $max Maximum number of bytes to scan.
+ *
+ * @return mixed Index or false if no match is found. You should use strong
+ * equality when checking the result, since index could be 0.
+ */
+ private function doCharsUntil($bytes, $max = null)
+ {
+ if ($this->char >= $this->EOF) {
+ return false;
+ }
+
+ if (0 === $max || $max) {
+ $len = strcspn($this->data, $bytes, $this->char, $max);
+ } else {
+ $len = strcspn($this->data, $bytes, $this->char);
+ }
+
+ $string = (string) substr($this->data, $this->char, $len);
+ $this->char += $len;
+
+ return $string;
+ }
+
+ /**
+ * Returns the string so long as $bytes matches.
+ *
+ * Matches as far as possible with a certain set of bytes
+ * and returns the matched substring.
+ *
+ * @param string $bytes A mask of bytes to match. If ANY byte in this mask matches the
+ * current char, the pointer advances and the char is part of the
+ * substring.
+ * @param int $max The max number of chars to read.
+ *
+ * @return string
+ */
+ private function doCharsWhile($bytes, $max = null)
+ {
+ if ($this->char >= $this->EOF) {
+ return false;
+ }
+
+ if (0 === $max || $max) {
+ $len = strspn($this->data, $bytes, $this->char, $max);
+ } else {
+ $len = strspn($this->data, $bytes, $this->char);
+ }
+
+ $string = (string) substr($this->data, $this->char, $len);
+ $this->char += $len;
+
+ return $string;
+ }
+}
diff --git a/masterminds/html5/src/HTML5/Parser/StringInputStream.php b/masterminds/html5/src/HTML5/Parser/StringInputStream.php
new file mode 100644
index 000000000..75b088610
--- /dev/null
+++ b/masterminds/html5/src/HTML5/Parser/StringInputStream.php
@@ -0,0 +1,336 @@
+
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+*/
+
+// Some conventions:
+// - /* */ indicates verbatim text from the HTML 5 specification
+// MPB: Not sure which version of the spec. Moving from HTML5lib to
+// HTML5-PHP, I have been using this version:
+// http://www.w3.org/TR/2012/CR-html5-20121217/Overview.html#contents
+//
+// - // indicates regular comments
+
+/**
+ * @deprecated since 2.4, to remove in 3.0. Use a string in the scanner instead.
+ */
+class StringInputStream implements InputStream
+{
+ /**
+ * The string data we're parsing.
+ */
+ private $data;
+
+ /**
+ * The current integer byte position we are in $data.
+ */
+ private $char;
+
+ /**
+ * Length of $data; when $char === $data, we are at the end-of-file.
+ */
+ private $EOF;
+
+ /**
+ * Parse errors.
+ */
+ public $errors = array();
+
+ /**
+ * Create a new InputStream wrapper.
+ *
+ * @param string $data Data to parse.
+ * @param string $encoding The encoding to use for the data.
+ * @param string $debug A fprintf format to use to echo the data on stdout.
+ */
+ public function __construct($data, $encoding = 'UTF-8', $debug = '')
+ {
+ $data = UTF8Utils::convertToUTF8($data, $encoding);
+ if ($debug) {
+ fprintf(STDOUT, $debug, $data, strlen($data));
+ }
+
+ // There is good reason to question whether it makes sense to
+ // do this here, since most of these checks are done during
+ // parsing, and since this check doesn't actually *do* anything.
+ $this->errors = UTF8Utils::checkForIllegalCodepoints($data);
+
+ $data = $this->replaceLinefeeds($data);
+
+ $this->data = $data;
+ $this->char = 0;
+ $this->EOF = strlen($data);
+ }
+
+ public function __toString()
+ {
+ return $this->data;
+ }
+
+ /**
+ * Replace linefeed characters according to the spec.
+ */
+ protected function replaceLinefeeds($data)
+ {
+ /*
+ * U+000D CARRIAGE RETURN (CR) characters and U+000A LINE FEED (LF) characters are treated specially.
+ * Any CR characters that are followed by LF characters must be removed, and any CR characters not
+ * followed by LF characters must be converted to LF characters. Thus, newlines in HTML DOMs are
+ * represented by LF characters, and there are never any CR characters in the input to the tokenization
+ * stage.
+ */
+ $crlfTable = array(
+ "\0" => "\xEF\xBF\xBD",
+ "\r\n" => "\n",
+ "\r" => "\n",
+ );
+
+ return strtr($data, $crlfTable);
+ }
+
+ /**
+ * Returns the current line that the tokenizer is at.
+ */
+ public function currentLine()
+ {
+ if (empty($this->EOF) || 0 === $this->char) {
+ return 1;
+ }
+ // Add one to $this->char because we want the number for the next
+ // byte to be processed.
+ return substr_count($this->data, "\n", 0, min($this->char, $this->EOF)) + 1;
+ }
+
+ /**
+ * @deprecated
+ */
+ public function getCurrentLine()
+ {
+ return $this->currentLine();
+ }
+
+ /**
+ * Returns the current column of the current line that the tokenizer is at.
+ * Newlines are column 0. The first char after a newline is column 1.
+ *
+ * @return int The column number.
+ */
+ public function columnOffset()
+ {
+ // Short circuit for the first char.
+ if (0 === $this->char) {
+ return 0;
+ }
+ // strrpos is weird, and the offset needs to be negative for what we
+ // want (i.e., the last \n before $this->char). This needs to not have
+ // one (to make it point to the next character, the one we want the
+ // position of) added to it because strrpos's behaviour includes the
+ // final offset byte.
+ $backwardFrom = $this->char - 1 - strlen($this->data);
+ $lastLine = strrpos($this->data, "\n", $backwardFrom);
+
+ // However, for here we want the length up until the next byte to be
+ // processed, so add one to the current byte ($this->char).
+ if (false !== $lastLine) {
+ $findLengthOf = substr($this->data, $lastLine + 1, $this->char - 1 - $lastLine);
+ } else {
+ // After a newline.
+ $findLengthOf = substr($this->data, 0, $this->char);
+ }
+
+ return UTF8Utils::countChars($findLengthOf);
+ }
+
+ /**
+ * @deprecated
+ */
+ public function getColumnOffset()
+ {
+ return $this->columnOffset();
+ }
+
+ /**
+ * Get the current character.
+ *
+ * @return string The current character.
+ */
+ #[\ReturnTypeWillChange]
+ public function current()
+ {
+ return $this->data[$this->char];
+ }
+
+ /**
+ * Advance the pointer.
+ * This is part of the Iterator interface.
+ */
+ #[\ReturnTypeWillChange]
+ public function next()
+ {
+ ++$this->char;
+ }
+
+ /**
+ * Rewind to the start of the string.
+ */
+ #[\ReturnTypeWillChange]
+ public function rewind()
+ {
+ $this->char = 0;
+ }
+
+ /**
+ * Is the current pointer location valid.
+ *
+ * @return bool Whether the current pointer location is valid.
+ */
+ #[\ReturnTypeWillChange]
+ public function valid()
+ {
+ return $this->char < $this->EOF;
+ }
+
+ /**
+ * Get all characters until EOF.
+ *
+ * This reads to the end of the file, and sets the read marker at the
+ * end of the file.
+ *
+ * Note this performs bounds checking.
+ *
+ * @return string Returns the remaining text. If called when the InputStream is
+ * already exhausted, it returns an empty string.
+ */
+ public function remainingChars()
+ {
+ if ($this->char < $this->EOF) {
+ $data = substr($this->data, $this->char);
+ $this->char = $this->EOF;
+
+ return $data;
+ }
+
+ return ''; // false;
+ }
+
+ /**
+ * Read to a particular match (or until $max bytes are consumed).
+ *
+ * This operates on byte sequences, not characters.
+ *
+ * Matches as far as possible until we reach a certain set of bytes
+ * and returns the matched substring.
+ *
+ * @param string $bytes Bytes to match.
+ * @param int $max Maximum number of bytes to scan.
+ *
+ * @return mixed Index or false if no match is found. You should use strong
+ * equality when checking the result, since index could be 0.
+ */
+ public function charsUntil($bytes, $max = null)
+ {
+ if ($this->char >= $this->EOF) {
+ return false;
+ }
+
+ if (0 === $max || $max) {
+ $len = strcspn($this->data, $bytes, $this->char, $max);
+ } else {
+ $len = strcspn($this->data, $bytes, $this->char);
+ }
+
+ $string = (string) substr($this->data, $this->char, $len);
+ $this->char += $len;
+
+ return $string;
+ }
+
+ /**
+ * Returns the string so long as $bytes matches.
+ *
+ * Matches as far as possible with a certain set of bytes
+ * and returns the matched substring.
+ *
+ * @param string $bytes A mask of bytes to match. If ANY byte in this mask matches the
+ * current char, the pointer advances and the char is part of the
+ * substring.
+ * @param int $max The max number of chars to read.
+ *
+ * @return string
+ */
+ public function charsWhile($bytes, $max = null)
+ {
+ if ($this->char >= $this->EOF) {
+ return false;
+ }
+
+ if (0 === $max || $max) {
+ $len = strspn($this->data, $bytes, $this->char, $max);
+ } else {
+ $len = strspn($this->data, $bytes, $this->char);
+ }
+ $string = (string) substr($this->data, $this->char, $len);
+ $this->char += $len;
+
+ return $string;
+ }
+
+ /**
+ * Unconsume characters.
+ *
+ * @param int $howMany The number of characters to unconsume.
+ */
+ public function unconsume($howMany = 1)
+ {
+ if (($this->char - $howMany) >= 0) {
+ $this->char -= $howMany;
+ }
+ }
+
+ /**
+ * Look ahead without moving cursor.
+ */
+ public function peek()
+ {
+ if (($this->char + 1) <= $this->EOF) {
+ return $this->data[$this->char + 1];
+ }
+
+ return false;
+ }
+
+ #[\ReturnTypeWillChange]
+ public function key()
+ {
+ return $this->char;
+ }
+}
diff --git a/masterminds/html5/src/HTML5/Parser/Tokenizer.php b/masterminds/html5/src/HTML5/Parser/Tokenizer.php
new file mode 100644
index 000000000..e8b4aa098
--- /dev/null
+++ b/masterminds/html5/src/HTML5/Parser/Tokenizer.php
@@ -0,0 +1,1214 @@
+scanner = $scanner;
+ $this->events = $eventHandler;
+ $this->mode = $mode;
+ }
+
+ /**
+ * Begin parsing.
+ *
+ * This will begin scanning the document, tokenizing as it goes.
+ * Tokens are emitted into the event handler.
+ *
+ * Tokenizing will continue until the document is completely
+ * read. Errors are emitted into the event handler, but
+ * the parser will attempt to continue parsing until the
+ * entire input stream is read.
+ */
+ public function parse()
+ {
+ do {
+ $this->consumeData();
+ // FIXME: Add infinite loop protection.
+ } while ($this->carryOn);
+ }
+
+ /**
+ * Set the text mode for the character data reader.
+ *
+ * HTML5 defines three different modes for reading text:
+ * - Normal: Read until a tag is encountered.
+ * - RCDATA: Read until a tag is encountered, but skip a few otherwise-
+ * special characters.
+ * - Raw: Read until a special closing tag is encountered (viz. pre, script)
+ *
+ * This allows those modes to be set.
+ *
+ * Normally, setting is done by the event handler via a special return code on
+ * startTag(), but it can also be set manually using this function.
+ *
+ * @param int $textmode One of Elements::TEXT_*.
+ * @param string $untilTag The tag that should stop RAW or RCDATA mode. Normal mode does not
+ * use this indicator.
+ */
+ public function setTextMode($textmode, $untilTag = null)
+ {
+ $this->textMode = $textmode & (Elements::TEXT_RAW | Elements::TEXT_RCDATA);
+ $this->untilTag = $untilTag;
+ }
+
+ /**
+ * Consume a character and make a move.
+ * HTML5 8.2.4.1.
+ */
+ protected function consumeData()
+ {
+ $tok = $this->scanner->current();
+
+ if ('&' === $tok) {
+ // Character reference
+ $ref = $this->decodeCharacterReference();
+ $this->buffer($ref);
+
+ $tok = $this->scanner->current();
+ }
+
+ // Parse tag
+ if ('<' === $tok) {
+ // Any buffered text data can go out now.
+ $this->flushBuffer();
+
+ $tok = $this->scanner->next();
+
+ if (false === $tok) {
+ // end of string
+ $this->parseError('Illegal tag opening');
+ } elseif ('!' === $tok) {
+ $this->markupDeclaration();
+ } elseif ('/' === $tok) {
+ $this->endTag();
+ } elseif ('?' === $tok) {
+ $this->processingInstruction();
+ } elseif ($this->is_alpha($tok)) {
+ $this->tagName();
+ } else {
+ $this->parseError('Illegal tag opening');
+ // TODO is this necessary ?
+ $this->characterData();
+ }
+
+ $tok = $this->scanner->current();
+ }
+
+ if (false === $tok) {
+ // Handle end of document
+ $this->eof();
+ } else {
+ // Parse character
+ switch ($this->textMode) {
+ case Elements::TEXT_RAW:
+ $this->rawText($tok);
+ break;
+
+ case Elements::TEXT_RCDATA:
+ $this->rcdata($tok);
+ break;
+
+ default:
+ if ('<' === $tok || '&' === $tok) {
+ break;
+ }
+
+ // NULL character
+ if ("\00" === $tok) {
+ $this->parseError('Received null character.');
+
+ $this->text .= $tok;
+ $this->scanner->consume();
+
+ break;
+ }
+
+ $this->text .= $this->scanner->charsUntil("<&\0");
+ }
+ }
+
+ return $this->carryOn;
+ }
+
+ /**
+ * Parse anything that looks like character data.
+ *
+ * Different rules apply based on the current text mode.
+ *
+ * @see Elements::TEXT_RAW Elements::TEXT_RCDATA.
+ */
+ protected function characterData()
+ {
+ $tok = $this->scanner->current();
+ if (false === $tok) {
+ return false;
+ }
+ switch ($this->textMode) {
+ case Elements::TEXT_RAW:
+ return $this->rawText($tok);
+ case Elements::TEXT_RCDATA:
+ return $this->rcdata($tok);
+ default:
+ if ('<' === $tok || '&' === $tok) {
+ return false;
+ }
+
+ return $this->text($tok);
+ }
+ }
+
+ /**
+ * This buffers the current token as character data.
+ *
+ * @param string $tok The current token.
+ *
+ * @return bool
+ */
+ protected function text($tok)
+ {
+ // This should never happen...
+ if (false === $tok) {
+ return false;
+ }
+
+ // NULL character
+ if ("\00" === $tok) {
+ $this->parseError('Received null character.');
+ }
+
+ $this->buffer($tok);
+ $this->scanner->consume();
+
+ return true;
+ }
+
+ /**
+ * Read text in RAW mode.
+ *
+ * @param string $tok The current token.
+ *
+ * @return bool
+ */
+ protected function rawText($tok)
+ {
+ if (is_null($this->untilTag)) {
+ return $this->text($tok);
+ }
+
+ $sequence = '' . $this->untilTag . '>';
+ $txt = $this->readUntilSequence($sequence);
+ $this->events->text($txt);
+ $this->setTextMode(0);
+
+ return $this->endTag();
+ }
+
+ /**
+ * Read text in RCDATA mode.
+ *
+ * @param string $tok The current token.
+ *
+ * @return bool
+ */
+ protected function rcdata($tok)
+ {
+ if (is_null($this->untilTag)) {
+ return $this->text($tok);
+ }
+
+ $sequence = '' . $this->untilTag;
+ $txt = '';
+
+ $caseSensitive = !Elements::isHtml5Element($this->untilTag);
+ while (false !== $tok && !('<' == $tok && ($this->scanner->sequenceMatches($sequence, $caseSensitive)))) {
+ if ('&' == $tok) {
+ $txt .= $this->decodeCharacterReference();
+ $tok = $this->scanner->current();
+ } else {
+ $txt .= $tok;
+ $tok = $this->scanner->next();
+ }
+ }
+ $len = strlen($sequence);
+ $this->scanner->consume($len);
+ $len += $this->scanner->whitespace();
+ if ('>' !== $this->scanner->current()) {
+ $this->parseError('Unclosed RCDATA end tag');
+ }
+
+ $this->scanner->unconsume($len);
+ $this->events->text($txt);
+ $this->setTextMode(0);
+
+ return $this->endTag();
+ }
+
+ /**
+ * If the document is read, emit an EOF event.
+ */
+ protected function eof()
+ {
+ // fprintf(STDOUT, "EOF");
+ $this->flushBuffer();
+ $this->events->eof();
+ $this->carryOn = false;
+ }
+
+ /**
+ * Look for markup.
+ */
+ protected function markupDeclaration()
+ {
+ $tok = $this->scanner->next();
+
+ // Comment:
+ if ('-' == $tok && '-' == $this->scanner->peek()) {
+ $this->scanner->consume(2);
+
+ return $this->comment();
+ } elseif ('D' == $tok || 'd' == $tok) { // Doctype
+ return $this->doctype();
+ } elseif ('[' == $tok) { // CDATA section
+ return $this->cdataSection();
+ }
+
+ // FINISH
+ $this->parseError('Expected . Emit an empty comment because 8.2.4.46 says to.
+ if ('>' == $tok) {
+ // Parse error. Emit the comment token.
+ $this->parseError("Expected comment data, got '>'");
+ $this->events->comment('');
+ $this->scanner->consume();
+
+ return true;
+ }
+
+ // Replace NULL with the replacement char.
+ if ("\0" == $tok) {
+ $tok = UTF8Utils::FFFD;
+ }
+ while (!$this->isCommentEnd()) {
+ $comment .= $tok;
+ $tok = $this->scanner->next();
+ }
+
+ $this->events->comment($comment);
+ $this->scanner->consume();
+
+ return true;
+ }
+
+ /**
+ * Check if the scanner has reached the end of a comment.
+ *
+ * @return bool
+ */
+ protected function isCommentEnd()
+ {
+ $tok = $this->scanner->current();
+
+ // EOF
+ if (false === $tok) {
+ // Hit the end.
+ $this->parseError('Unexpected EOF in a comment.');
+
+ return true;
+ }
+
+ // If next two tokens are not '--', not the end.
+ if ('-' != $tok || '-' != $this->scanner->peek()) {
+ return false;
+ }
+
+ $this->scanner->consume(2); // Consume '-' and one of '!' or '>'
+
+ // Test for '>'
+ if ('>' == $this->scanner->current()) {
+ return true;
+ }
+ // Test for '!>'
+ if ('!' == $this->scanner->current() && '>' == $this->scanner->peek()) {
+ $this->scanner->consume(); // Consume the last '>'
+ return true;
+ }
+ // Unread '-' and one of '!' or '>';
+ $this->scanner->unconsume(2);
+
+ return false;
+ }
+
+ /**
+ * Parse a DOCTYPE.
+ *
+ * Parse a DOCTYPE declaration. This method has strong bearing on whether or
+ * not Quirksmode is enabled on the event handler.
+ *
+ * @todo This method is a little long. Should probably refactor.
+ *
+ * @return bool
+ */
+ protected function doctype()
+ {
+ // Check that string is DOCTYPE.
+ if ($this->scanner->sequenceMatches('DOCTYPE', false)) {
+ $this->scanner->consume(7);
+ } else {
+ $chars = $this->scanner->charsWhile('DOCTYPEdoctype');
+ $this->parseError('Expected DOCTYPE, got %s', $chars);
+
+ return $this->bogusComment('scanner->whitespace();
+ $tok = $this->scanner->current();
+
+ // EOF: die.
+ if (false === $tok) {
+ $this->events->doctype('html5', EventHandler::DOCTYPE_NONE, '', true);
+ $this->eof();
+
+ return true;
+ }
+
+ // NULL char: convert.
+ if ("\0" === $tok) {
+ $this->parseError('Unexpected null character in DOCTYPE.');
+ }
+
+ $stop = " \n\f>";
+ $doctypeName = $this->scanner->charsUntil($stop);
+ // Lowercase ASCII, replace \0 with FFFD
+ $doctypeName = strtolower(strtr($doctypeName, "\0", UTF8Utils::FFFD));
+
+ $tok = $this->scanner->current();
+
+ // If false, emit a parse error, DOCTYPE, and return.
+ if (false === $tok) {
+ $this->parseError('Unexpected EOF in DOCTYPE declaration.');
+ $this->events->doctype($doctypeName, EventHandler::DOCTYPE_NONE, null, true);
+
+ return true;
+ }
+
+ // Short DOCTYPE, like
+ if ('>' == $tok) {
+ // DOCTYPE without a name.
+ if (0 == strlen($doctypeName)) {
+ $this->parseError('Expected a DOCTYPE name. Got nothing.');
+ $this->events->doctype($doctypeName, 0, null, true);
+ $this->scanner->consume();
+
+ return true;
+ }
+ $this->events->doctype($doctypeName);
+ $this->scanner->consume();
+
+ return true;
+ }
+ $this->scanner->whitespace();
+
+ $pub = strtoupper($this->scanner->getAsciiAlpha());
+ $white = $this->scanner->whitespace();
+
+ // Get ID, and flag it as pub or system.
+ if (('PUBLIC' == $pub || 'SYSTEM' == $pub) && $white > 0) {
+ // Get the sys ID.
+ $type = 'PUBLIC' == $pub ? EventHandler::DOCTYPE_PUBLIC : EventHandler::DOCTYPE_SYSTEM;
+ $id = $this->quotedString("\0>");
+ if (false === $id) {
+ $this->events->doctype($doctypeName, $type, $pub, false);
+
+ return true;
+ }
+
+ // Premature EOF.
+ if (false === $this->scanner->current()) {
+ $this->parseError('Unexpected EOF in DOCTYPE');
+ $this->events->doctype($doctypeName, $type, $id, true);
+
+ return true;
+ }
+
+ // Well-formed complete DOCTYPE.
+ $this->scanner->whitespace();
+ if ('>' == $this->scanner->current()) {
+ $this->events->doctype($doctypeName, $type, $id, false);
+ $this->scanner->consume();
+
+ return true;
+ }
+
+ // If we get here, we have scanner->charsUntil('>');
+ $this->parseError('Malformed DOCTYPE.');
+ $this->events->doctype($doctypeName, $type, $id, true);
+ $this->scanner->consume();
+
+ return true;
+ }
+
+ // Else it's a bogus DOCTYPE.
+ // Consume to > and trash.
+ $this->scanner->charsUntil('>');
+
+ $this->parseError('Expected PUBLIC or SYSTEM. Got %s.', $pub);
+ $this->events->doctype($doctypeName, 0, null, true);
+ $this->scanner->consume();
+
+ return true;
+ }
+
+ /**
+ * Utility for reading a quoted string.
+ *
+ * @param string $stopchars Characters (in addition to a close-quote) that should stop the string.
+ * E.g. sometimes '>' is higher precedence than '"' or "'".
+ *
+ * @return mixed String if one is found (quotations omitted).
+ */
+ protected function quotedString($stopchars)
+ {
+ $tok = $this->scanner->current();
+ if ('"' == $tok || "'" == $tok) {
+ $this->scanner->consume();
+ $ret = $this->scanner->charsUntil($tok . $stopchars);
+ if ($this->scanner->current() == $tok) {
+ $this->scanner->consume();
+ } else {
+ // Parse error because no close quote.
+ $this->parseError('Expected %s, got %s', $tok, $this->scanner->current());
+ }
+
+ return $ret;
+ }
+
+ return false;
+ }
+
+ /**
+ * Handle a CDATA section.
+ *
+ * @return bool
+ */
+ protected function cdataSection()
+ {
+ $cdata = '';
+ $this->scanner->consume();
+
+ $chars = $this->scanner->charsWhile('CDAT');
+ if ('CDATA' != $chars || '[' != $this->scanner->current()) {
+ $this->parseError('Expected [CDATA[, got %s', $chars);
+
+ return $this->bogusComment('scanner->next();
+ do {
+ if (false === $tok) {
+ $this->parseError('Unexpected EOF inside CDATA.');
+ $this->bogusComment('scanner->next();
+ } while (!$this->scanner->sequenceMatches(']]>'));
+
+ // Consume ]]>
+ $this->scanner->consume(3);
+
+ $this->events->cdata($cdata);
+
+ return true;
+ }
+
+ // ================================================================
+ // Non-HTML5
+ // ================================================================
+
+ /**
+ * Handle a processing instruction.
+ *
+ * XML processing instructions are supposed to be ignored in HTML5,
+ * treated as "bogus comments". However, since we're not a user
+ * agent, we allow them. We consume until ?> and then issue a
+ * EventListener::processingInstruction() event.
+ *
+ * @return bool
+ */
+ protected function processingInstruction()
+ {
+ if ('?' != $this->scanner->current()) {
+ return false;
+ }
+
+ $tok = $this->scanner->next();
+ $procName = $this->scanner->getAsciiAlpha();
+ $white = $this->scanner->whitespace();
+
+ // If not a PI, send to bogusComment.
+ if (0 == strlen($procName) || 0 == $white || false == $this->scanner->current()) {
+ $this->parseError("Expected processing instruction name, got $tok");
+ $this->bogusComment('' . $tok . $procName);
+
+ return true;
+ }
+
+ $data = '';
+ // As long as it's not the case that the next two chars are ? and >.
+ while (!('?' == $this->scanner->current() && '>' == $this->scanner->peek())) {
+ $data .= $this->scanner->current();
+
+ $tok = $this->scanner->next();
+ if (false === $tok) {
+ $this->parseError('Unexpected EOF in processing instruction.');
+ $this->events->processingInstruction($procName, $data);
+
+ return true;
+ }
+ }
+
+ $this->scanner->consume(2); // Consume the closing tag
+ $this->events->processingInstruction($procName, $data);
+
+ return true;
+ }
+
+ // ================================================================
+ // UTILITY FUNCTIONS
+ // ================================================================
+
+ /**
+ * Read from the input stream until we get to the desired sequene
+ * or hit the end of the input stream.
+ *
+ * @param string $sequence
+ *
+ * @return string
+ */
+ protected function readUntilSequence($sequence)
+ {
+ $buffer = '';
+
+ // Optimization for reading larger blocks faster.
+ $first = substr($sequence, 0, 1);
+ while (false !== $this->scanner->current()) {
+ $buffer .= $this->scanner->charsUntil($first);
+
+ // Stop as soon as we hit the stopping condition.
+ if ($this->scanner->sequenceMatches($sequence, false)) {
+ return $buffer;
+ }
+ $buffer .= $this->scanner->current();
+ $this->scanner->consume();
+ }
+
+ // If we get here, we hit the EOF.
+ $this->parseError('Unexpected EOF during text read.');
+
+ return $buffer;
+ }
+
+ /**
+ * Check if upcomming chars match the given sequence.
+ *
+ * This will read the stream for the $sequence. If it's
+ * found, this will return true. If not, return false.
+ * Since this unconsumes any chars it reads, the caller
+ * will still need to read the next sequence, even if
+ * this returns true.
+ *
+ * Example: $this->scanner->sequenceMatches('') will
+ * see if the input stream is at the start of a
+ * '' string.
+ *
+ * @param string $sequence
+ * @param bool $caseSensitive
+ *
+ * @return bool
+ */
+ protected function sequenceMatches($sequence, $caseSensitive = true)
+ {
+ @trigger_error(__METHOD__ . ' method is deprecated since version 2.4 and will be removed in 3.0. Use Scanner::sequenceMatches() instead.', E_USER_DEPRECATED);
+
+ return $this->scanner->sequenceMatches($sequence, $caseSensitive);
+ }
+
+ /**
+ * Send a TEXT event with the contents of the text buffer.
+ *
+ * This emits an EventHandler::text() event with the current contents of the
+ * temporary text buffer. (The buffer is used to group as much PCDATA
+ * as we can instead of emitting lots and lots of TEXT events.)
+ */
+ protected function flushBuffer()
+ {
+ if ('' === $this->text) {
+ return;
+ }
+ $this->events->text($this->text);
+ $this->text = '';
+ }
+
+ /**
+ * Add text to the temporary buffer.
+ *
+ * @see flushBuffer()
+ *
+ * @param string $str
+ */
+ protected function buffer($str)
+ {
+ $this->text .= $str;
+ }
+
+ /**
+ * Emit a parse error.
+ *
+ * A parse error always returns false because it never consumes any
+ * characters.
+ *
+ * @param string $msg
+ *
+ * @return string
+ */
+ protected function parseError($msg)
+ {
+ $args = func_get_args();
+
+ if (count($args) > 1) {
+ array_shift($args);
+ $msg = vsprintf($msg, $args);
+ }
+
+ $line = $this->scanner->currentLine();
+ $col = $this->scanner->columnOffset();
+ $this->events->parseError($msg, $line, $col);
+
+ return false;
+ }
+
+ /**
+ * Decode a character reference and return the string.
+ *
+ * If $inAttribute is set to true, a bare & will be returned as-is.
+ *
+ * @param bool $inAttribute Set to true if the text is inside of an attribute value.
+ * false otherwise.
+ *
+ * @return string
+ */
+ protected function decodeCharacterReference($inAttribute = false)
+ {
+ // Next char after &.
+ $tok = $this->scanner->next();
+ $start = $this->scanner->position();
+
+ if (false === $tok) {
+ return '&';
+ }
+
+ // These indicate not an entity. We return just
+ // the &.
+ if ("\t" === $tok || "\n" === $tok || "\f" === $tok || ' ' === $tok || '&' === $tok || '<' === $tok) {
+ // $this->scanner->next();
+ return '&';
+ }
+
+ // Numeric entity
+ if ('#' === $tok) {
+ $tok = $this->scanner->next();
+
+ if (false === $tok) {
+ $this->parseError('Expected DEC; HEX;, got EOF');
+ $this->scanner->unconsume(1);
+
+ return '&';
+ }
+
+ // Hexidecimal encoding.
+ // X[0-9a-fA-F]+;
+ // x[0-9a-fA-F]+;
+ if ('x' === $tok || 'X' === $tok) {
+ $tok = $this->scanner->next(); // Consume x
+
+ // Convert from hex code to char.
+ $hex = $this->scanner->getHex();
+ if (empty($hex)) {
+ $this->parseError('Expected HEX;, got %s', $tok);
+ // We unconsume because we don't know what parser rules might
+ // be in effect for the remaining chars. For example. '>'
+ // might result in a specific parsing rule inside of tag
+ // contexts, while not inside of pcdata context.
+ $this->scanner->unconsume(2);
+
+ return '&';
+ }
+ $entity = CharacterReference::lookupHex($hex);
+ } // Decimal encoding.
+ // [0-9]+;
+ else {
+ // Convert from decimal to char.
+ $numeric = $this->scanner->getNumeric();
+ if (false === $numeric) {
+ $this->parseError('Expected DIGITS;, got %s', $tok);
+ $this->scanner->unconsume(2);
+
+ return '&';
+ }
+ $entity = CharacterReference::lookupDecimal($numeric);
+ }
+ } elseif ('=' === $tok && $inAttribute) {
+ return '&';
+ } else { // String entity.
+ // Attempt to consume a string up to a ';'.
+ // [a-zA-Z0-9]+;
+ $cname = $this->scanner->getAsciiAlphaNum();
+ $entity = CharacterReference::lookupName($cname);
+
+ // When no entity is found provide the name of the unmatched string
+ // and continue on as the & is not part of an entity. The & will
+ // be converted to & elsewhere.
+ if (null === $entity) {
+ if (!$inAttribute || '' === $cname) {
+ $this->parseError("No match in entity table for '%s'", $cname);
+ }
+ $this->scanner->unconsume($this->scanner->position() - $start);
+
+ return '&';
+ }
+ }
+
+ // The scanner has advanced the cursor for us.
+ $tok = $this->scanner->current();
+
+ // We have an entity. We're done here.
+ if (';' === $tok) {
+ $this->scanner->consume();
+
+ return $entity;
+ }
+
+ // Failing to match ; means unconsume the entire string.
+ $this->scanner->unconsume($this->scanner->position() - $start);
+
+ $this->parseError('Expected &ENTITY;, got &ENTITY%s (no trailing ;) ', $tok);
+
+ return '&';
+ }
+
+ /**
+ * Checks whether a (single-byte) character is an ASCII letter or not.
+ *
+ * @param string $input A single-byte string
+ *
+ * @return bool True if it is a letter, False otherwise
+ */
+ protected function is_alpha($input)
+ {
+ $code = ord($input);
+
+ return ($code >= 97 && $code <= 122) || ($code >= 65 && $code <= 90);
+ }
+}
diff --git a/masterminds/html5/src/HTML5/Parser/TreeBuildingRules.php b/masterminds/html5/src/HTML5/Parser/TreeBuildingRules.php
new file mode 100644
index 000000000..00d3951fd
--- /dev/null
+++ b/masterminds/html5/src/HTML5/Parser/TreeBuildingRules.php
@@ -0,0 +1,127 @@
+ 1,
+ 'dd' => 1,
+ 'dt' => 1,
+ 'rt' => 1,
+ 'rp' => 1,
+ 'tr' => 1,
+ 'th' => 1,
+ 'td' => 1,
+ 'thead' => 1,
+ 'tfoot' => 1,
+ 'tbody' => 1,
+ 'table' => 1,
+ 'optgroup' => 1,
+ 'option' => 1,
+ );
+
+ /**
+ * Returns true if the given tagname has special processing rules.
+ */
+ public function hasRules($tagname)
+ {
+ return isset(static::$tags[$tagname]);
+ }
+
+ /**
+ * Evaluate the rule for the current tag name.
+ *
+ * This may modify the existing DOM.
+ *
+ * @return \DOMElement The new Current DOM element.
+ */
+ public function evaluate($new, $current)
+ {
+ switch ($new->tagName) {
+ case 'li':
+ return $this->handleLI($new, $current);
+ case 'dt':
+ case 'dd':
+ return $this->handleDT($new, $current);
+ case 'rt':
+ case 'rp':
+ return $this->handleRT($new, $current);
+ case 'optgroup':
+ return $this->closeIfCurrentMatches($new, $current, array(
+ 'optgroup',
+ ));
+ case 'option':
+ return $this->closeIfCurrentMatches($new, $current, array(
+ 'option',
+ ));
+ case 'tr':
+ return $this->closeIfCurrentMatches($new, $current, array(
+ 'tr',
+ ));
+ case 'td':
+ case 'th':
+ return $this->closeIfCurrentMatches($new, $current, array(
+ 'th',
+ 'td',
+ ));
+ case 'tbody':
+ case 'thead':
+ case 'tfoot':
+ case 'table': // Spec isn't explicit about this, but it's necessary.
+
+ return $this->closeIfCurrentMatches($new, $current, array(
+ 'thead',
+ 'tfoot',
+ 'tbody',
+ ));
+ }
+
+ return $current;
+ }
+
+ protected function handleLI($ele, $current)
+ {
+ return $this->closeIfCurrentMatches($ele, $current, array(
+ 'li',
+ ));
+ }
+
+ protected function handleDT($ele, $current)
+ {
+ return $this->closeIfCurrentMatches($ele, $current, array(
+ 'dt',
+ 'dd',
+ ));
+ }
+
+ protected function handleRT($ele, $current)
+ {
+ return $this->closeIfCurrentMatches($ele, $current, array(
+ 'rt',
+ 'rp',
+ ));
+ }
+
+ protected function closeIfCurrentMatches($ele, $current, $match)
+ {
+ if (in_array($current->tagName, $match, true)) {
+ $current->parentNode->appendChild($ele);
+ } else {
+ $current->appendChild($ele);
+ }
+
+ return $ele;
+ }
+}
diff --git a/masterminds/html5/src/HTML5/Parser/UTF8Utils.php b/masterminds/html5/src/HTML5/Parser/UTF8Utils.php
new file mode 100644
index 000000000..4405e4cc0
--- /dev/null
+++ b/masterminds/html5/src/HTML5/Parser/UTF8Utils.php
@@ -0,0 +1,177 @@
+
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+use Masterminds\HTML5\Exception;
+
+class UTF8Utils
+{
+ /**
+ * The Unicode replacement character.
+ */
+ const FFFD = "\xEF\xBF\xBD";
+
+ /**
+ * Count the number of characters in a string.
+ * UTF-8 aware. This will try (in order) iconv, MB, and finally a custom counter.
+ *
+ * @param string $string
+ *
+ * @return int
+ */
+ public static function countChars($string)
+ {
+ // Get the length for the string we need.
+ if (function_exists('mb_strlen')) {
+ return mb_strlen($string, 'utf-8');
+ }
+
+ if (function_exists('iconv_strlen')) {
+ return iconv_strlen($string, 'utf-8');
+ }
+
+ $count = count_chars($string);
+
+ // 0x80 = 0x7F - 0 + 1 (one added to get inclusive range)
+ // 0x33 = 0xF4 - 0x2C + 1 (one added to get inclusive range)
+ return array_sum(array_slice($count, 0, 0x80)) + array_sum(array_slice($count, 0xC2, 0x33));
+ }
+
+ /**
+ * Convert data from the given encoding to UTF-8.
+ *
+ * This has not yet been tested with charactersets other than UTF-8.
+ * It should work with ISO-8859-1/-13 and standard Latin Win charsets.
+ *
+ * @param string $data The data to convert
+ * @param string $encoding A valid encoding. Examples: http://www.php.net/manual/en/mbstring.supported-encodings.php
+ *
+ * @return string
+ */
+ public static function convertToUTF8($data, $encoding = 'UTF-8')
+ {
+ /*
+ * From the HTML5 spec: Given an encoding, the bytes in the input stream must be converted
+ * to Unicode characters for the tokeniser, as described by the rules for that encoding,
+ * except that the leading U+FEFF BYTE ORDER MARK character, if any, must not be stripped
+ * by the encoding layer (it is stripped by the rule below). Bytes or sequences of bytes
+ * in the original byte stream that could not be converted to Unicode characters must be
+ * converted to U+FFFD REPLACEMENT CHARACTER code points.
+ */
+
+ // mb_convert_encoding is chosen over iconv because of a bug. The best
+ // details for the bug are on http://us1.php.net/manual/en/function.iconv.php#108643
+ // which contains links to the actual but reports as well as work around
+ // details.
+ if (function_exists('mb_convert_encoding')) {
+ // mb library has the following behaviors:
+ // - UTF-16 surrogates result in false.
+ // - Overlongs and outside Plane 16 result in empty strings.
+
+ // Before we run mb_convert_encoding we need to tell it what to do with
+ // characters it does not know. This could be different than the parent
+ // application executing this library so we store the value, change it
+ // to our needs, and then change it back when we are done. This feels
+ // a little excessive and it would be great if there was a better way.
+ $save = mb_substitute_character();
+ mb_substitute_character('none');
+ $data = mb_convert_encoding($data, 'UTF-8', $encoding);
+ mb_substitute_character($save);
+ }
+ // @todo Get iconv running in at least some environments if that is possible.
+ elseif (function_exists('iconv') && 'auto' !== $encoding) {
+ // fprintf(STDOUT, "iconv found\n");
+ // iconv has the following behaviors:
+ // - Overlong representations are ignored.
+ // - Beyond Plane 16 is replaced with a lower char.
+ // - Incomplete sequences generate a warning.
+ $data = @iconv($encoding, 'UTF-8//IGNORE', $data);
+ } else {
+ throw new Exception('Not implemented, please install mbstring or iconv');
+ }
+
+ /*
+ * One leading U+FEFF BYTE ORDER MARK character must be ignored if any are present.
+ */
+ if ("\xEF\xBB\xBF" === substr($data, 0, 3)) {
+ $data = substr($data, 3);
+ }
+
+ return $data;
+ }
+
+ /**
+ * Checks for Unicode code points that are not valid in a document.
+ *
+ * @param string $data A string to analyze
+ *
+ * @return array An array of (string) error messages produced by the scanning
+ */
+ public static function checkForIllegalCodepoints($data)
+ {
+ // Vestigal error handling.
+ $errors = array();
+
+ /*
+ * All U+0000 null characters in the input must be replaced by U+FFFD REPLACEMENT CHARACTERs.
+ * Any occurrences of such characters is a parse error.
+ */
+ for ($i = 0, $count = substr_count($data, "\0"); $i < $count; ++$i) {
+ $errors[] = 'null-character';
+ }
+
+ /*
+ * Any occurrences of any characters in the ranges U+0001 to U+0008, U+000B, U+000E to U+001F, U+007F
+ * to U+009F, U+D800 to U+DFFF , U+FDD0 to U+FDEF, and characters U+FFFE, U+FFFF, U+1FFFE, U+1FFFF,
+ * U+2FFFE, U+2FFFF, U+3FFFE, U+3FFFF, U+4FFFE, U+4FFFF, U+5FFFE, U+5FFFF, U+6FFFE, U+6FFFF, U+7FFFE,
+ * U+7FFFF, U+8FFFE, U+8FFFF, U+9FFFE, U+9FFFF, U+AFFFE, U+AFFFF, U+BFFFE, U+BFFFF, U+CFFFE, U+CFFFF,
+ * U+DFFFE, U+DFFFF, U+EFFFE, U+EFFFF, U+FFFFE, U+FFFFF, U+10FFFE, and U+10FFFF are parse errors.
+ * (These are all control characters or permanently undefined Unicode characters.)
+ */
+ // Check PCRE is loaded.
+ $count = preg_match_all(
+ '/(?:
+ [\x01-\x08\x0B\x0E-\x1F\x7F] # U+0001 to U+0008, U+000B, U+000E to U+001F and U+007F
+ |
+ \xC2[\x80-\x9F] # U+0080 to U+009F
+ |
+ \xED(?:\xA0[\x80-\xFF]|[\xA1-\xBE][\x00-\xFF]|\xBF[\x00-\xBF]) # U+D800 to U+DFFFF
+ |
+ \xEF\xB7[\x90-\xAF] # U+FDD0 to U+FDEF
+ |
+ \xEF\xBF[\xBE\xBF] # U+FFFE and U+FFFF
+ |
+ [\xF0-\xF4][\x8F-\xBF]\xBF[\xBE\xBF] # U+nFFFE and U+nFFFF (1 <= n <= 10_{16})
+ )/x', $data, $matches);
+ for ($i = 0; $i < $count; ++$i) {
+ $errors[] = 'invalid-codepoint';
+ }
+
+ return $errors;
+ }
+}
diff --git a/masterminds/html5/src/HTML5/Serializer/HTML5Entities.php b/masterminds/html5/src/HTML5/Serializer/HTML5Entities.php
new file mode 100644
index 000000000..e9421a12d
--- /dev/null
+++ b/masterminds/html5/src/HTML5/Serializer/HTML5Entities.php
@@ -0,0 +1,1533 @@
+ '	',
+ "\n" => '
',
+ '!' => '!',
+ '"' => '"',
+ '#' => '#',
+ '$' => '$',
+ '%' => '%',
+ '&' => '&',
+ '\'' => ''',
+ '(' => '(',
+ ')' => ')',
+ '*' => '*',
+ '+' => '+',
+ ',' => ',',
+ '.' => '.',
+ '/' => '/',
+ ':' => ':',
+ ';' => ';',
+ '<' => '<',
+ '<⃒' => '&nvlt',
+ '=' => '=',
+ '=⃥' => '&bne',
+ '>' => '>',
+ '>⃒' => '&nvgt',
+ '?' => '?',
+ '@' => '@',
+ '[' => '[',
+ '\\' => '\',
+ ']' => ']',
+ '^' => '^',
+ '_' => '_',
+ '`' => '`',
+ 'fj' => '&fjlig',
+ '{' => '{',
+ '|' => '|',
+ '}' => '}',
+ ' ' => ' ',
+ '¡' => '¡',
+ '¢' => '¢',
+ '£' => '£',
+ '¤' => '¤',
+ '¥' => '¥',
+ '¦' => '¦',
+ '§' => '§',
+ '¨' => '¨',
+ '©' => '©',
+ 'ª' => 'ª',
+ '«' => '«',
+ '¬' => '¬',
+ '' => '',
+ '®' => '®',
+ '¯' => '¯',
+ '°' => '°',
+ '±' => '±',
+ '²' => '²',
+ '³' => '³',
+ '´' => '´',
+ 'µ' => 'µ',
+ '¶' => '¶',
+ '·' => '·',
+ '¸' => '¸',
+ '¹' => '¹',
+ 'º' => 'º',
+ '»' => '»',
+ '¼' => '¼',
+ '½' => '½',
+ '¾' => '¾',
+ '¿' => '¿',
+ 'À' => 'À',
+ 'Á' => 'Á',
+ 'Â' => 'Â',
+ 'Ã' => 'Ã',
+ 'Ä' => 'Ä',
+ 'Å' => 'Å',
+ 'Æ' => 'Æ',
+ 'Ç' => 'Ç',
+ 'È' => 'È',
+ 'É' => 'É',
+ 'Ê' => 'Ê',
+ 'Ë' => 'Ë',
+ 'Ì' => 'Ì',
+ 'Í' => 'Í',
+ 'Î' => 'Î',
+ 'Ï' => 'Ï',
+ 'Ð' => 'Ð',
+ 'Ñ' => 'Ñ',
+ 'Ò' => 'Ò',
+ 'Ó' => 'Ó',
+ 'Ô' => 'Ô',
+ 'Õ' => 'Õ',
+ 'Ö' => 'Ö',
+ '×' => '×',
+ 'Ø' => 'Ø',
+ 'Ù' => 'Ù',
+ 'Ú' => 'Ú',
+ 'Û' => 'Û',
+ 'Ü' => 'Ü',
+ 'Ý' => 'Ý',
+ 'Þ' => 'Þ',
+ 'ß' => 'ß',
+ 'à' => 'à',
+ 'á' => 'á',
+ 'â' => 'â',
+ 'ã' => 'ã',
+ 'ä' => 'ä',
+ 'å' => 'å',
+ 'æ' => 'æ',
+ 'ç' => 'ç',
+ 'è' => 'è',
+ 'é' => 'é',
+ 'ê' => 'ê',
+ 'ë' => 'ë',
+ 'ì' => 'ì',
+ 'í' => 'í',
+ 'î' => 'î',
+ 'ï' => 'ï',
+ 'ð' => 'ð',
+ 'ñ' => 'ñ',
+ 'ò' => 'ò',
+ 'ó' => 'ó',
+ 'ô' => 'ô',
+ 'õ' => 'õ',
+ 'ö' => 'ö',
+ '÷' => '÷',
+ 'ø' => 'ø',
+ 'ù' => 'ù',
+ 'ú' => 'ú',
+ 'û' => 'û',
+ 'ü' => 'ü',
+ 'ý' => 'ý',
+ 'þ' => 'þ',
+ 'ÿ' => 'ÿ',
+ 'Ā' => 'Ā',
+ 'ā' => 'ā',
+ 'Ă' => 'Ă',
+ 'ă' => 'ă',
+ 'Ą' => 'Ą',
+ 'ą' => 'ą',
+ 'Ć' => 'Ć',
+ 'ć' => 'ć',
+ 'Ĉ' => 'Ĉ',
+ 'ĉ' => 'ĉ',
+ 'Ċ' => 'Ċ',
+ 'ċ' => 'ċ',
+ 'Č' => 'Č',
+ 'č' => 'č',
+ 'Ď' => 'Ď',
+ 'ď' => 'ď',
+ 'Đ' => 'Đ',
+ 'đ' => 'đ',
+ 'Ē' => 'Ē',
+ 'ē' => 'ē',
+ 'Ė' => 'Ė',
+ 'ė' => 'ė',
+ 'Ę' => 'Ę',
+ 'ę' => 'ę',
+ 'Ě' => 'Ě',
+ 'ě' => 'ě',
+ 'Ĝ' => 'Ĝ',
+ 'ĝ' => 'ĝ',
+ 'Ğ' => 'Ğ',
+ 'ğ' => 'ğ',
+ 'Ġ' => 'Ġ',
+ 'ġ' => 'ġ',
+ 'Ģ' => 'Ģ',
+ 'Ĥ' => 'Ĥ',
+ 'ĥ' => 'ĥ',
+ 'Ħ' => 'Ħ',
+ 'ħ' => 'ħ',
+ 'Ĩ' => 'Ĩ',
+ 'ĩ' => 'ĩ',
+ 'Ī' => 'Ī',
+ 'ī' => 'ī',
+ 'Į' => 'Į',
+ 'į' => 'į',
+ 'İ' => 'İ',
+ 'ı' => 'ı',
+ 'IJ' => 'IJ',
+ 'ij' => 'ij',
+ 'Ĵ' => 'Ĵ',
+ 'ĵ' => 'ĵ',
+ 'Ķ' => 'Ķ',
+ 'ķ' => 'ķ',
+ 'ĸ' => 'ĸ',
+ 'Ĺ' => 'Ĺ',
+ 'ĺ' => 'ĺ',
+ 'Ļ' => 'Ļ',
+ 'ļ' => 'ļ',
+ 'Ľ' => 'Ľ',
+ 'ľ' => 'ľ',
+ 'Ŀ' => 'Ŀ',
+ 'ŀ' => 'ŀ',
+ 'Ł' => 'Ł',
+ 'ł' => 'ł',
+ 'Ń' => 'Ń',
+ 'ń' => 'ń',
+ 'Ņ' => 'Ņ',
+ 'ņ' => 'ņ',
+ 'Ň' => 'Ň',
+ 'ň' => 'ň',
+ 'ʼn' => 'ʼn',
+ 'Ŋ' => 'Ŋ',
+ 'ŋ' => 'ŋ',
+ 'Ō' => 'Ō',
+ 'ō' => 'ō',
+ 'Ő' => 'Ő',
+ 'ő' => 'ő',
+ 'Œ' => 'Œ',
+ 'œ' => 'œ',
+ 'Ŕ' => 'Ŕ',
+ 'ŕ' => 'ŕ',
+ 'Ŗ' => 'Ŗ',
+ 'ŗ' => 'ŗ',
+ 'Ř' => 'Ř',
+ 'ř' => 'ř',
+ 'Ś' => 'Ś',
+ 'ś' => 'ś',
+ 'Ŝ' => 'Ŝ',
+ 'ŝ' => 'ŝ',
+ 'Ş' => 'Ş',
+ 'ş' => 'ş',
+ 'Š' => 'Š',
+ 'š' => 'š',
+ 'Ţ' => 'Ţ',
+ 'ţ' => 'ţ',
+ 'Ť' => 'Ť',
+ 'ť' => 'ť',
+ 'Ŧ' => 'Ŧ',
+ 'ŧ' => 'ŧ',
+ 'Ũ' => 'Ũ',
+ 'ũ' => 'ũ',
+ 'Ū' => 'Ū',
+ 'ū' => 'ū',
+ 'Ŭ' => 'Ŭ',
+ 'ŭ' => 'ŭ',
+ 'Ů' => 'Ů',
+ 'ů' => 'ů',
+ 'Ű' => 'Ű',
+ 'ű' => 'ű',
+ 'Ų' => 'Ų',
+ 'ų' => 'ų',
+ 'Ŵ' => 'Ŵ',
+ 'ŵ' => 'ŵ',
+ 'Ŷ' => 'Ŷ',
+ 'ŷ' => 'ŷ',
+ 'Ÿ' => 'Ÿ',
+ 'Ź' => 'Ź',
+ 'ź' => 'ź',
+ 'Ż' => 'Ż',
+ 'ż' => 'ż',
+ 'Ž' => 'Ž',
+ 'ž' => 'ž',
+ 'ƒ' => 'ƒ',
+ 'Ƶ' => 'Ƶ',
+ 'ǵ' => 'ǵ',
+ 'ȷ' => 'ȷ',
+ 'ˆ' => 'ˆ',
+ 'ˇ' => 'ˇ',
+ '˘' => '˘',
+ '˙' => '˙',
+ '˚' => '˚',
+ '˛' => '˛',
+ '˜' => '˜',
+ '˝' => '˝',
+ '̑' => '̑',
+ 'Α' => 'Α',
+ 'Β' => 'Β',
+ 'Γ' => 'Γ',
+ 'Δ' => 'Δ',
+ 'Ε' => 'Ε',
+ 'Ζ' => 'Ζ',
+ 'Η' => 'Η',
+ 'Θ' => 'Θ',
+ 'Ι' => 'Ι',
+ 'Κ' => 'Κ',
+ 'Λ' => 'Λ',
+ 'Μ' => 'Μ',
+ 'Ν' => 'Ν',
+ 'Ξ' => 'Ξ',
+ 'Ο' => 'Ο',
+ 'Π' => 'Π',
+ 'Ρ' => 'Ρ',
+ 'Σ' => 'Σ',
+ 'Τ' => 'Τ',
+ 'Υ' => 'Υ',
+ 'Φ' => 'Φ',
+ 'Χ' => 'Χ',
+ 'Ψ' => 'Ψ',
+ 'Ω' => 'Ω',
+ 'α' => 'α',
+ 'β' => 'β',
+ 'γ' => 'γ',
+ 'δ' => 'δ',
+ 'ε' => 'ε',
+ 'ζ' => 'ζ',
+ 'η' => 'η',
+ 'θ' => 'θ',
+ 'ι' => 'ι',
+ 'κ' => 'κ',
+ 'λ' => 'λ',
+ 'μ' => 'μ',
+ 'ν' => 'ν',
+ 'ξ' => 'ξ',
+ 'ο' => 'ο',
+ 'π' => 'π',
+ 'ρ' => 'ρ',
+ 'ς' => 'ς',
+ 'σ' => 'σ',
+ 'τ' => 'τ',
+ 'υ' => 'υ',
+ 'φ' => 'φ',
+ 'χ' => 'χ',
+ 'ψ' => 'ψ',
+ 'ω' => 'ω',
+ 'ϑ' => 'ϑ',
+ 'ϒ' => 'ϒ',
+ 'ϕ' => 'ϕ',
+ 'ϖ' => 'ϖ',
+ 'Ϝ' => 'Ϝ',
+ 'ϝ' => 'ϝ',
+ 'ϰ' => 'ϰ',
+ 'ϱ' => 'ϱ',
+ 'ϵ' => 'ϵ',
+ '϶' => '϶',
+ 'Ё' => 'Ё',
+ 'Ђ' => 'Ђ',
+ 'Ѓ' => 'Ѓ',
+ 'Є' => 'Є',
+ 'Ѕ' => 'Ѕ',
+ 'І' => 'І',
+ 'Ї' => 'Ї',
+ 'Ј' => 'Ј',
+ 'Љ' => 'Љ',
+ 'Њ' => 'Њ',
+ 'Ћ' => 'Ћ',
+ 'Ќ' => 'Ќ',
+ 'Ў' => 'Ў',
+ 'Џ' => 'Џ',
+ 'А' => 'А',
+ 'Б' => 'Б',
+ 'В' => 'В',
+ 'Г' => 'Г',
+ 'Д' => 'Д',
+ 'Е' => 'Е',
+ 'Ж' => 'Ж',
+ 'З' => 'З',
+ 'И' => 'И',
+ 'Й' => 'Й',
+ 'К' => 'К',
+ 'Л' => 'Л',
+ 'М' => 'М',
+ 'Н' => 'Н',
+ 'О' => 'О',
+ 'П' => 'П',
+ 'Р' => 'Р',
+ 'С' => 'С',
+ 'Т' => 'Т',
+ 'У' => 'У',
+ 'Ф' => 'Ф',
+ 'Х' => 'Х',
+ 'Ц' => 'Ц',
+ 'Ч' => 'Ч',
+ 'Ш' => 'Ш',
+ 'Щ' => 'Щ',
+ 'Ъ' => 'Ъ',
+ 'Ы' => 'Ы',
+ 'Ь' => 'Ь',
+ 'Э' => 'Э',
+ 'Ю' => 'Ю',
+ 'Я' => 'Я',
+ 'а' => 'а',
+ 'б' => 'б',
+ 'в' => 'в',
+ 'г' => 'г',
+ 'д' => 'д',
+ 'е' => 'е',
+ 'ж' => 'ж',
+ 'з' => 'з',
+ 'и' => 'и',
+ 'й' => 'й',
+ 'к' => 'к',
+ 'л' => 'л',
+ 'м' => 'м',
+ 'н' => 'н',
+ 'о' => 'о',
+ 'п' => 'п',
+ 'р' => 'р',
+ 'с' => 'с',
+ 'т' => 'т',
+ 'у' => 'у',
+ 'ф' => 'ф',
+ 'х' => 'х',
+ 'ц' => 'ц',
+ 'ч' => 'ч',
+ 'ш' => 'ш',
+ 'щ' => 'щ',
+ 'ъ' => 'ъ',
+ 'ы' => 'ы',
+ 'ь' => 'ь',
+ 'э' => 'э',
+ 'ю' => 'ю',
+ 'я' => 'я',
+ 'ё' => 'ё',
+ 'ђ' => 'ђ',
+ 'ѓ' => 'ѓ',
+ 'є' => 'є',
+ 'ѕ' => 'ѕ',
+ 'і' => 'і',
+ 'ї' => 'ї',
+ 'ј' => 'ј',
+ 'љ' => 'љ',
+ 'њ' => 'њ',
+ 'ћ' => 'ћ',
+ 'ќ' => 'ќ',
+ 'ў' => 'ў',
+ 'џ' => 'џ',
+ ' ' => ' ',
+ ' ' => ' ',
+ ' ' => ' ',
+ ' ' => ' ',
+ ' ' => ' ',
+ ' ' => ' ',
+ ' ' => ' ',
+ ' ' => ' ',
+ '' => '​',
+ '' => '',
+ '' => '',
+ '' => '',
+ '' => '',
+ '‐' => '‐',
+ '–' => '–',
+ '—' => '—',
+ '―' => '―',
+ '‖' => '‖',
+ '‘' => '‘',
+ '’' => '’',
+ '‚' => '‚',
+ '“' => '“',
+ '”' => '”',
+ '„' => '„',
+ '†' => '†',
+ '‡' => '‡',
+ '•' => '•',
+ '‥' => '‥',
+ '…' => '…',
+ '‰' => '‰',
+ '‱' => '‱',
+ '′' => '′',
+ '″' => '″',
+ '‴' => '‴',
+ '‵' => '‵',
+ '‹' => '‹',
+ '›' => '›',
+ '‾' => '‾',
+ '⁁' => '⁁',
+ '⁃' => '⁃',
+ '⁄' => '⁄',
+ '⁏' => '⁏',
+ '⁗' => '⁗',
+ ' ' => ' ',
+ ' ' => '&ThickSpace',
+ '' => '⁠',
+ '' => '⁡',
+ '' => '⁢',
+ '' => '⁣',
+ '€' => '€',
+ '⃛' => '⃛',
+ '⃜' => '⃜',
+ 'ℂ' => 'ℂ',
+ '℅' => '℅',
+ 'ℊ' => 'ℊ',
+ 'ℋ' => 'ℋ',
+ 'ℌ' => 'ℌ',
+ 'ℍ' => 'ℍ',
+ 'ℎ' => 'ℎ',
+ 'ℏ' => 'ℏ',
+ 'ℐ' => 'ℐ',
+ 'ℑ' => 'ℑ',
+ 'ℒ' => 'ℒ',
+ 'ℓ' => 'ℓ',
+ 'ℕ' => 'ℕ',
+ '№' => '№',
+ '℗' => '℗',
+ '℘' => '℘',
+ 'ℙ' => 'ℙ',
+ 'ℚ' => 'ℚ',
+ 'ℛ' => 'ℛ',
+ 'ℜ' => 'ℜ',
+ 'ℝ' => 'ℝ',
+ '℞' => '℞',
+ '™' => '™',
+ 'ℤ' => 'ℤ',
+ '℧' => '℧',
+ 'ℨ' => 'ℨ',
+ '℩' => '℩',
+ 'ℬ' => 'ℬ',
+ 'ℭ' => 'ℭ',
+ 'ℯ' => 'ℯ',
+ 'ℰ' => 'ℰ',
+ 'ℱ' => 'ℱ',
+ 'ℳ' => 'ℳ',
+ 'ℴ' => 'ℴ',
+ 'ℵ' => 'ℵ',
+ 'ℶ' => 'ℶ',
+ 'ℷ' => 'ℷ',
+ 'ℸ' => 'ℸ',
+ 'ⅅ' => 'ⅅ',
+ 'ⅆ' => 'ⅆ',
+ 'ⅇ' => 'ⅇ',
+ 'ⅈ' => 'ⅈ',
+ '⅓' => '⅓',
+ '⅔' => '⅔',
+ '⅕' => '⅕',
+ '⅖' => '⅖',
+ '⅗' => '⅗',
+ '⅘' => '⅘',
+ '⅙' => '⅙',
+ '⅚' => '⅚',
+ '⅛' => '⅛',
+ '⅜' => '⅜',
+ '⅝' => '⅝',
+ '⅞' => '⅞',
+ '←' => '←',
+ '↑' => '↑',
+ '→' => '→',
+ '↓' => '↓',
+ '↔' => '↔',
+ '↕' => '↕',
+ '↖' => '↖',
+ '↗' => '↗',
+ '↘' => '↘',
+ '↙' => '↙',
+ '↚' => '↚',
+ '↛' => '↛',
+ '↝' => '↝',
+ '↝̸' => '&nrarrw',
+ '↞' => '↞',
+ '↟' => '↟',
+ '↠' => '↠',
+ '↡' => '↡',
+ '↢' => '↢',
+ '↣' => '↣',
+ '↤' => '↤',
+ '↥' => '↥',
+ '↦' => '↦',
+ '↧' => '↧',
+ '↩' => '↩',
+ '↪' => '↪',
+ '↫' => '↫',
+ '↬' => '↬',
+ '↭' => '↭',
+ '↮' => '↮',
+ '↰' => '↰',
+ '↱' => '↱',
+ '↲' => '↲',
+ '↳' => '↳',
+ '↵' => '↵',
+ '↶' => '↶',
+ '↷' => '↷',
+ '↺' => '↺',
+ '↻' => '↻',
+ '↼' => '↼',
+ '↽' => '↽',
+ '↾' => '↾',
+ '↿' => '↿',
+ '⇀' => '⇀',
+ '⇁' => '⇁',
+ '⇂' => '⇂',
+ '⇃' => '⇃',
+ '⇄' => '⇄',
+ '⇅' => '⇅',
+ '⇆' => '⇆',
+ '⇇' => '⇇',
+ '⇈' => '⇈',
+ '⇉' => '⇉',
+ '⇊' => '⇊',
+ '⇋' => '⇋',
+ '⇌' => '⇌',
+ '⇍' => '⇍',
+ '⇎' => '⇎',
+ '⇏' => '⇏',
+ '⇐' => '⇐',
+ '⇑' => '⇑',
+ '⇒' => '⇒',
+ '⇓' => '⇓',
+ '⇔' => '⇔',
+ '⇕' => '⇕',
+ '⇖' => '⇖',
+ '⇗' => '⇗',
+ '⇘' => '⇘',
+ '⇙' => '⇙',
+ '⇚' => '⇚',
+ '⇛' => '⇛',
+ '⇝' => '⇝',
+ '⇤' => '⇤',
+ '⇥' => '⇥',
+ '⇵' => '⇵',
+ '⇽' => '⇽',
+ '⇾' => '⇾',
+ '⇿' => '⇿',
+ '∀' => '∀',
+ '∁' => '∁',
+ '∂' => '∂',
+ '∂̸' => '&npart',
+ '∃' => '∃',
+ '∄' => '∄',
+ '∅' => '∅',
+ '∇' => '∇',
+ '∈' => '∈',
+ '∉' => '∉',
+ '∋' => '∋',
+ '∌' => '∌',
+ '∏' => '∏',
+ '∐' => '∐',
+ '∑' => '∑',
+ '−' => '−',
+ '∓' => '∓',
+ '∔' => '∔',
+ '∖' => '∖',
+ '∗' => '∗',
+ '∘' => '∘',
+ '√' => '√',
+ '∝' => '∝',
+ '∞' => '∞',
+ '∟' => '∟',
+ '∠' => '∠',
+ '∠⃒' => '&nang',
+ '∡' => '∡',
+ '∢' => '∢',
+ '∣' => '∣',
+ '∤' => '∤',
+ '∥' => '∥',
+ '∦' => '∦',
+ '∧' => '∧',
+ '∨' => '∨',
+ '∩' => '∩',
+ '∩︀' => '&caps',
+ '∪' => '∪',
+ '∪︀' => '&cups',
+ '∫' => '∫',
+ '∬' => '∬',
+ '∭' => '∭',
+ '∮' => '∮',
+ '∯' => '∯',
+ '∰' => '∰',
+ '∱' => '∱',
+ '∲' => '∲',
+ '∳' => '∳',
+ '∴' => '∴',
+ '∵' => '∵',
+ '∶' => '∶',
+ '∷' => '∷',
+ '∸' => '∸',
+ '∺' => '∺',
+ '∻' => '∻',
+ '∼' => '∼',
+ '∼⃒' => '&nvsim',
+ '∽' => '∽',
+ '∽̱' => '&race',
+ '∾' => '∾',
+ '∾̳' => '&acE',
+ '∿' => '∿',
+ '≀' => '≀',
+ '≁' => '≁',
+ '≂' => '≂',
+ '≂̸' => '&nesim',
+ '≃' => '≃',
+ '≄' => '≄',
+ '≅' => '≅',
+ '≆' => '≆',
+ '≇' => '≇',
+ '≈' => '≈',
+ '≉' => '≉',
+ '≊' => '≊',
+ '≋' => '≋',
+ '≋̸' => '&napid',
+ '≌' => '≌',
+ '≍' => '≍',
+ '≍⃒' => '&nvap',
+ '≎' => '≎',
+ '≎̸' => '&nbump',
+ '≏' => '≏',
+ '≏̸' => '&nbumpe',
+ '≐' => '≐',
+ '≐̸' => '&nedot',
+ '≑' => '≑',
+ '≒' => '≒',
+ '≓' => '≓',
+ '≔' => '≔',
+ '≕' => '≕',
+ '≖' => '≖',
+ '≗' => '≗',
+ '≙' => '≙',
+ '≚' => '≚',
+ '≜' => '≜',
+ '≟' => '≟',
+ '≠' => '≠',
+ '≡' => '≡',
+ '≡⃥' => '&bnequiv',
+ '≢' => '≢',
+ '≤' => '≤',
+ '≤⃒' => '&nvle',
+ '≥' => '≥',
+ '≥⃒' => '&nvge',
+ '≦' => '≦',
+ '≦̸' => '&nlE',
+ '≧' => '≧',
+ '≧̸' => '&NotGreaterFullEqual',
+ '≨' => '≨',
+ '≨︀' => '&lvertneqq',
+ '≩' => '≩',
+ '≩︀' => '&gvertneqq',
+ '≪' => '≪',
+ '≪̸' => '&nLtv',
+ '≪⃒' => '&nLt',
+ '≫' => '≫',
+ '≫̸' => '&NotGreaterGreater',
+ '≫⃒' => '&nGt',
+ '≬' => '≬',
+ '≭' => '≭',
+ '≮' => '≮',
+ '≯' => '≯',
+ '≰' => '≰',
+ '≱' => '≱',
+ '≲' => '≲',
+ '≳' => '≳',
+ '≴' => '≴',
+ '≵' => '≵',
+ '≶' => '≶',
+ '≷' => '≷',
+ '≸' => '≸',
+ '≹' => '≹',
+ '≺' => '≺',
+ '≻' => '≻',
+ '≼' => '≼',
+ '≽' => '≽',
+ '≾' => '≾',
+ '≿' => '≿',
+ '≿̸' => '&NotSucceedsTilde',
+ '⊀' => '⊀',
+ '⊁' => '⊁',
+ '⊂' => '⊂',
+ '⊂⃒' => '&vnsub',
+ '⊃' => '⊃',
+ '⊃⃒' => '&nsupset',
+ '⊄' => '⊄',
+ '⊅' => '⊅',
+ '⊆' => '⊆',
+ '⊇' => '⊇',
+ '⊈' => '⊈',
+ '⊉' => '⊉',
+ '⊊' => '⊊',
+ '⊊︀' => '&vsubne',
+ '⊋' => '⊋',
+ '⊋︀' => '&vsupne',
+ '⊍' => '⊍',
+ '⊎' => '⊎',
+ '⊏' => '⊏',
+ '⊏̸' => '&NotSquareSubset',
+ '⊐' => '⊐',
+ '⊐̸' => '&NotSquareSuperset',
+ '⊑' => '⊑',
+ '⊒' => '⊒',
+ '⊓' => '⊓',
+ '⊓︀' => '&sqcaps',
+ '⊔' => '⊔',
+ '⊔︀' => '&sqcups',
+ '⊕' => '⊕',
+ '⊖' => '⊖',
+ '⊗' => '⊗',
+ '⊘' => '⊘',
+ '⊙' => '⊙',
+ '⊚' => '⊚',
+ '⊛' => '⊛',
+ '⊝' => '⊝',
+ '⊞' => '⊞',
+ '⊟' => '⊟',
+ '⊠' => '⊠',
+ '⊡' => '⊡',
+ '⊢' => '⊢',
+ '⊣' => '⊣',
+ '⊤' => '⊤',
+ '⊥' => '⊥',
+ '⊧' => '⊧',
+ '⊨' => '⊨',
+ '⊩' => '⊩',
+ '⊪' => '⊪',
+ '⊫' => '⊫',
+ '⊬' => '⊬',
+ '⊭' => '⊭',
+ '⊮' => '⊮',
+ '⊯' => '⊯',
+ '⊰' => '⊰',
+ '⊲' => '⊲',
+ '⊳' => '⊳',
+ '⊴' => '⊴',
+ '⊴⃒' => '&nvltrie',
+ '⊵' => '⊵',
+ '⊵⃒' => '&nvrtrie',
+ '⊶' => '⊶',
+ '⊷' => '⊷',
+ '⊸' => '⊸',
+ '⊹' => '⊹',
+ '⊺' => '⊺',
+ '⊻' => '⊻',
+ '⊽' => '⊽',
+ '⊾' => '⊾',
+ '⊿' => '⊿',
+ '⋀' => '⋀',
+ '⋁' => '⋁',
+ '⋂' => '⋂',
+ '⋃' => '⋃',
+ '⋄' => '⋄',
+ '⋅' => '⋅',
+ '⋆' => '⋆',
+ '⋇' => '⋇',
+ '⋈' => '⋈',
+ '⋉' => '⋉',
+ '⋊' => '⋊',
+ '⋋' => '⋋',
+ '⋌' => '⋌',
+ '⋍' => '⋍',
+ '⋎' => '⋎',
+ '⋏' => '⋏',
+ '⋐' => '⋐',
+ '⋑' => '⋑',
+ '⋒' => '⋒',
+ '⋓' => '⋓',
+ '⋔' => '⋔',
+ '⋕' => '⋕',
+ '⋖' => '⋖',
+ '⋗' => '⋗',
+ '⋘' => '⋘',
+ '⋘̸' => '&nLl',
+ '⋙' => '⋙',
+ '⋙̸' => '&nGg',
+ '⋚' => '⋚',
+ '⋚︀' => '&lesg',
+ '⋛' => '⋛',
+ '⋛︀' => '&gesl',
+ '⋞' => '⋞',
+ '⋟' => '⋟',
+ '⋠' => '⋠',
+ '⋡' => '⋡',
+ '⋢' => '⋢',
+ '⋣' => '⋣',
+ '⋦' => '⋦',
+ '⋧' => '⋧',
+ '⋨' => '⋨',
+ '⋩' => '⋩',
+ '⋪' => '⋪',
+ '⋫' => '⋫',
+ '⋬' => '⋬',
+ '⋭' => '⋭',
+ '⋮' => '⋮',
+ '⋯' => '⋯',
+ '⋰' => '⋰',
+ '⋱' => '⋱',
+ '⋲' => '⋲',
+ '⋳' => '⋳',
+ '⋴' => '⋴',
+ '⋵' => '⋵',
+ '⋵̸' => '¬indot',
+ '⋶' => '⋶',
+ '⋷' => '⋷',
+ '⋹' => '⋹',
+ '⋹̸' => '¬inE',
+ '⋺' => '⋺',
+ '⋻' => '⋻',
+ '⋼' => '⋼',
+ '⋽' => '⋽',
+ '⋾' => '⋾',
+ '⌅' => '⌅',
+ '⌆' => '⌆',
+ '⌈' => '⌈',
+ '⌉' => '⌉',
+ '⌊' => '⌊',
+ '⌋' => '⌋',
+ '⌌' => '⌌',
+ '⌍' => '⌍',
+ '⌎' => '⌎',
+ '⌏' => '⌏',
+ '⌐' => '⌐',
+ '⌒' => '⌒',
+ '⌓' => '⌓',
+ '⌕' => '⌕',
+ '⌖' => '⌖',
+ '⌜' => '⌜',
+ '⌝' => '⌝',
+ '⌞' => '⌞',
+ '⌟' => '⌟',
+ '⌢' => '⌢',
+ '⌣' => '⌣',
+ '⌭' => '⌭',
+ '⌮' => '⌮',
+ '⌶' => '⌶',
+ '⌽' => '⌽',
+ '⌿' => '⌿',
+ '⍼' => '⍼',
+ '⎰' => '⎰',
+ '⎱' => '⎱',
+ '⎴' => '⎴',
+ '⎵' => '⎵',
+ '⎶' => '⎶',
+ '⏜' => '⏜',
+ '⏝' => '⏝',
+ '⏞' => '⏞',
+ '⏟' => '⏟',
+ '⏢' => '⏢',
+ '⏧' => '⏧',
+ '␣' => '␣',
+ 'Ⓢ' => 'Ⓢ',
+ '─' => '─',
+ '│' => '│',
+ '┌' => '┌',
+ '┐' => '┐',
+ '└' => '└',
+ '┘' => '┘',
+ '├' => '├',
+ '┤' => '┤',
+ '┬' => '┬',
+ '┴' => '┴',
+ '┼' => '┼',
+ '═' => '═',
+ '║' => '║',
+ '╒' => '╒',
+ '╓' => '╓',
+ '╔' => '╔',
+ '╕' => '╕',
+ '╖' => '╖',
+ '╗' => '╗',
+ '╘' => '╘',
+ '╙' => '╙',
+ '╚' => '╚',
+ '╛' => '╛',
+ '╜' => '╜',
+ '╝' => '╝',
+ '╞' => '╞',
+ '╟' => '╟',
+ '╠' => '╠',
+ '╡' => '╡',
+ '╢' => '╢',
+ '╣' => '╣',
+ '╤' => '╤',
+ '╥' => '╥',
+ '╦' => '╦',
+ '╧' => '╧',
+ '╨' => '╨',
+ '╩' => '╩',
+ '╪' => '╪',
+ '╫' => '╫',
+ '╬' => '╬',
+ '▀' => '▀',
+ '▄' => '▄',
+ '█' => '█',
+ '░' => '░',
+ '▒' => '▒',
+ '▓' => '▓',
+ '□' => '□',
+ '▪' => '▪',
+ '▫' => '▫',
+ '▭' => '▭',
+ '▮' => '▮',
+ '▱' => '▱',
+ '△' => '△',
+ '▴' => '▴',
+ '▵' => '▵',
+ '▸' => '▸',
+ '▹' => '▹',
+ '▽' => '▽',
+ '▾' => '▾',
+ '▿' => '▿',
+ '◂' => '◂',
+ '◃' => '◃',
+ '◊' => '◊',
+ '○' => '○',
+ '◬' => '◬',
+ '◯' => '◯',
+ '◸' => '◸',
+ '◹' => '◹',
+ '◺' => '◺',
+ '◻' => '◻',
+ '◼' => '◼',
+ '★' => '★',
+ '☆' => '☆',
+ '☎' => '☎',
+ '♀' => '♀',
+ '♂' => '♂',
+ '♠' => '♠',
+ '♣' => '♣',
+ '♥' => '♥',
+ '♦' => '♦',
+ '♪' => '♪',
+ '♭' => '♭',
+ '♮' => '♮',
+ '♯' => '♯',
+ '✓' => '✓',
+ '✗' => '✗',
+ '✠' => '✠',
+ '✶' => '✶',
+ '❘' => '❘',
+ '❲' => '❲',
+ '❳' => '❳',
+ '⟈' => '⟈',
+ '⟉' => '⟉',
+ '⟦' => '⟦',
+ '⟧' => '⟧',
+ '⟨' => '⟨',
+ '⟩' => '⟩',
+ '⟪' => '⟪',
+ '⟫' => '⟫',
+ '⟬' => '⟬',
+ '⟭' => '⟭',
+ '⟵' => '⟵',
+ '⟶' => '⟶',
+ '⟷' => '⟷',
+ '⟸' => '⟸',
+ '⟹' => '⟹',
+ '⟺' => '⟺',
+ '⟼' => '⟼',
+ '⟿' => '⟿',
+ '⤂' => '⤂',
+ '⤃' => '⤃',
+ '⤄' => '⤄',
+ '⤅' => '⤅',
+ '⤌' => '⤌',
+ '⤍' => '⤍',
+ '⤎' => '⤎',
+ '⤏' => '⤏',
+ '⤐' => '⤐',
+ '⤑' => '⤑',
+ '⤒' => '⤒',
+ '⤓' => '⤓',
+ '⤖' => '⤖',
+ '⤙' => '⤙',
+ '⤚' => '⤚',
+ '⤛' => '⤛',
+ '⤜' => '⤜',
+ '⤝' => '⤝',
+ '⤞' => '⤞',
+ '⤟' => '⤟',
+ '⤠' => '⤠',
+ '⤣' => '⤣',
+ '⤤' => '⤤',
+ '⤥' => '⤥',
+ '⤦' => '⤦',
+ '⤧' => '⤧',
+ '⤨' => '⤨',
+ '⤩' => '⤩',
+ '⤪' => '⤪',
+ '⤳' => '⤳',
+ '⤳̸' => '&nrarrc',
+ '⤵' => '⤵',
+ '⤶' => '⤶',
+ '⤷' => '⤷',
+ '⤸' => '⤸',
+ '⤹' => '⤹',
+ '⤼' => '⤼',
+ '⤽' => '⤽',
+ '⥅' => '⥅',
+ '⥈' => '⥈',
+ '⥉' => '⥉',
+ '⥊' => '⥊',
+ '⥋' => '⥋',
+ '⥎' => '⥎',
+ '⥏' => '⥏',
+ '⥐' => '⥐',
+ '⥑' => '⥑',
+ '⥒' => '⥒',
+ '⥓' => '⥓',
+ '⥔' => '⥔',
+ '⥕' => '⥕',
+ '⥖' => '⥖',
+ '⥗' => '⥗',
+ '⥘' => '⥘',
+ '⥙' => '⥙',
+ '⥚' => '⥚',
+ '⥛' => '⥛',
+ '⥜' => '⥜',
+ '⥝' => '⥝',
+ '⥞' => '⥞',
+ '⥟' => '⥟',
+ '⥠' => '⥠',
+ '⥡' => '⥡',
+ '⥢' => '⥢',
+ '⥣' => '⥣',
+ '⥤' => '⥤',
+ '⥥' => '⥥',
+ '⥦' => '⥦',
+ '⥧' => '⥧',
+ '⥨' => '⥨',
+ '⥩' => '⥩',
+ '⥪' => '⥪',
+ '⥫' => '⥫',
+ '⥬' => '⥬',
+ '⥭' => '⥭',
+ '⥮' => '⥮',
+ '⥯' => '⥯',
+ '⥰' => '⥰',
+ '⥱' => '⥱',
+ '⥲' => '⥲',
+ '⥳' => '⥳',
+ '⥴' => '⥴',
+ '⥵' => '⥵',
+ '⥶' => '⥶',
+ '⥸' => '⥸',
+ '⥹' => '⥹',
+ '⥻' => '⥻',
+ '⥼' => '⥼',
+ '⥽' => '⥽',
+ '⥾' => '⥾',
+ '⥿' => '⥿',
+ '⦅' => '⦅',
+ '⦆' => '⦆',
+ '⦋' => '⦋',
+ '⦌' => '⦌',
+ '⦍' => '⦍',
+ '⦎' => '⦎',
+ '⦏' => '⦏',
+ '⦐' => '⦐',
+ '⦑' => '⦑',
+ '⦒' => '⦒',
+ '⦓' => '⦓',
+ '⦔' => '⦔',
+ '⦕' => '⦕',
+ '⦖' => '⦖',
+ '⦚' => '⦚',
+ '⦜' => '⦜',
+ '⦝' => '⦝',
+ '⦤' => '⦤',
+ '⦥' => '⦥',
+ '⦦' => '⦦',
+ '⦧' => '⦧',
+ '⦨' => '⦨',
+ '⦩' => '⦩',
+ '⦪' => '⦪',
+ '⦫' => '⦫',
+ '⦬' => '⦬',
+ '⦭' => '⦭',
+ '⦮' => '⦮',
+ '⦯' => '⦯',
+ '⦰' => '⦰',
+ '⦱' => '⦱',
+ '⦲' => '⦲',
+ '⦳' => '⦳',
+ '⦴' => '⦴',
+ '⦵' => '⦵',
+ '⦶' => '⦶',
+ '⦷' => '⦷',
+ '⦹' => '⦹',
+ '⦻' => '⦻',
+ '⦼' => '⦼',
+ '⦾' => '⦾',
+ '⦿' => '⦿',
+ '⧀' => '⧀',
+ '⧁' => '⧁',
+ '⧂' => '⧂',
+ '⧃' => '⧃',
+ '⧄' => '⧄',
+ '⧅' => '⧅',
+ '⧉' => '⧉',
+ '⧍' => '⧍',
+ '⧎' => '⧎',
+ '⧏' => '⧏',
+ '⧏̸' => '&NotLeftTriangleBar',
+ '⧐' => '⧐',
+ '⧐̸' => '&NotRightTriangleBar',
+ '⧜' => '⧜',
+ '⧝' => '⧝',
+ '⧞' => '⧞',
+ '⧣' => '⧣',
+ '⧤' => '⧤',
+ '⧥' => '⧥',
+ '⧫' => '⧫',
+ '⧴' => '⧴',
+ '⧶' => '⧶',
+ '⨀' => '⨀',
+ '⨁' => '⨁',
+ '⨂' => '⨂',
+ '⨄' => '⨄',
+ '⨆' => '⨆',
+ '⨌' => '⨌',
+ '⨍' => '⨍',
+ '⨐' => '⨐',
+ '⨑' => '⨑',
+ '⨒' => '⨒',
+ '⨓' => '⨓',
+ '⨔' => '⨔',
+ '⨕' => '⨕',
+ '⨖' => '⨖',
+ '⨗' => '⨗',
+ '⨢' => '⨢',
+ '⨣' => '⨣',
+ '⨤' => '⨤',
+ '⨥' => '⨥',
+ '⨦' => '⨦',
+ '⨧' => '⨧',
+ '⨩' => '⨩',
+ '⨪' => '⨪',
+ '⨭' => '⨭',
+ '⨮' => '⨮',
+ '⨯' => '⨯',
+ '⨰' => '⨰',
+ '⨱' => '⨱',
+ '⨳' => '⨳',
+ '⨴' => '⨴',
+ '⨵' => '⨵',
+ '⨶' => '⨶',
+ '⨷' => '⨷',
+ '⨸' => '⨸',
+ '⨹' => '⨹',
+ '⨺' => '⨺',
+ '⨻' => '⨻',
+ '⨼' => '⨼',
+ '⨿' => '⨿',
+ '⩀' => '⩀',
+ '⩂' => '⩂',
+ '⩃' => '⩃',
+ '⩄' => '⩄',
+ '⩅' => '⩅',
+ '⩆' => '⩆',
+ '⩇' => '⩇',
+ '⩈' => '⩈',
+ '⩉' => '⩉',
+ '⩊' => '⩊',
+ '⩋' => '⩋',
+ '⩌' => '⩌',
+ '⩍' => '⩍',
+ '⩐' => '⩐',
+ '⩓' => '⩓',
+ '⩔' => '⩔',
+ '⩕' => '⩕',
+ '⩖' => '⩖',
+ '⩗' => '⩗',
+ '⩘' => '⩘',
+ '⩚' => '⩚',
+ '⩛' => '⩛',
+ '⩜' => '⩜',
+ '⩝' => '⩝',
+ '⩟' => '⩟',
+ '⩦' => '⩦',
+ '⩪' => '⩪',
+ '⩭' => '⩭',
+ '⩭̸' => '&ncongdot',
+ '⩮' => '⩮',
+ '⩯' => '⩯',
+ '⩰' => '⩰',
+ '⩰̸' => '&napE',
+ '⩱' => '⩱',
+ '⩲' => '⩲',
+ '⩳' => '⩳',
+ '⩴' => '⩴',
+ '⩵' => '⩵',
+ '⩷' => '⩷',
+ '⩸' => '⩸',
+ '⩹' => '⩹',
+ '⩺' => '⩺',
+ '⩻' => '⩻',
+ '⩼' => '⩼',
+ '⩽' => '⩽',
+ '⩽̸' => '&nles',
+ '⩾' => '⩾',
+ '⩾̸' => '&nges',
+ '⩿' => '⩿',
+ '⪀' => '⪀',
+ '⪁' => '⪁',
+ '⪂' => '⪂',
+ '⪃' => '⪃',
+ '⪄' => '⪄',
+ '⪅' => '⪅',
+ '⪆' => '⪆',
+ '⪇' => '⪇',
+ '⪈' => '⪈',
+ '⪉' => '⪉',
+ '⪊' => '⪊',
+ '⪋' => '⪋',
+ '⪌' => '⪌',
+ '⪍' => '⪍',
+ '⪎' => '⪎',
+ '⪏' => '⪏',
+ '⪐' => '⪐',
+ '⪑' => '⪑',
+ '⪒' => '⪒',
+ '⪓' => '⪓',
+ '⪔' => '⪔',
+ '⪕' => '⪕',
+ '⪖' => '⪖',
+ '⪗' => '⪗',
+ '⪘' => '⪘',
+ '⪙' => '⪙',
+ '⪚' => '⪚',
+ '⪝' => '⪝',
+ '⪞' => '⪞',
+ '⪟' => '⪟',
+ '⪠' => '⪠',
+ '⪡' => '⪡',
+ '⪡̸' => '&NotNestedLessLess',
+ '⪢' => '⪢',
+ '⪢̸' => '&NotNestedGreaterGreater',
+ '⪤' => '⪤',
+ '⪥' => '⪥',
+ '⪦' => '⪦',
+ '⪧' => '⪧',
+ '⪨' => '⪨',
+ '⪩' => '⪩',
+ '⪪' => '⪪',
+ '⪫' => '⪫',
+ '⪬' => '⪬',
+ '⪬︀' => '&smtes',
+ '⪭' => '⪭',
+ '⪭︀' => '&lates',
+ '⪮' => '⪮',
+ '⪯' => '⪯',
+ '⪯̸' => '&NotPrecedesEqual',
+ '⪰' => '⪰',
+ '⪰̸' => '&NotSucceedsEqual',
+ '⪳' => '⪳',
+ '⪴' => '⪴',
+ '⪵' => '⪵',
+ '⪶' => '⪶',
+ '⪷' => '⪷',
+ '⪸' => '⪸',
+ '⪹' => '⪹',
+ '⪺' => '⪺',
+ '⪻' => '⪻',
+ '⪼' => '⪼',
+ '⪽' => '⪽',
+ '⪾' => '⪾',
+ '⪿' => '⪿',
+ '⫀' => '⫀',
+ '⫁' => '⫁',
+ '⫂' => '⫂',
+ '⫃' => '⫃',
+ '⫄' => '⫄',
+ '⫅' => '⫅',
+ '⫅̸' => '&nsubE',
+ '⫆' => '⫆',
+ '⫆̸' => '&nsupseteqq',
+ '⫇' => '⫇',
+ '⫈' => '⫈',
+ '⫋' => '⫋',
+ '⫋︀' => '&vsubnE',
+ '⫌' => '⫌',
+ '⫌︀' => '&varsupsetneqq',
+ '⫏' => '⫏',
+ '⫐' => '⫐',
+ '⫑' => '⫑',
+ '⫒' => '⫒',
+ '⫓' => '⫓',
+ '⫔' => '⫔',
+ '⫕' => '⫕',
+ '⫖' => '⫖',
+ '⫗' => '⫗',
+ '⫘' => '⫘',
+ '⫙' => '⫙',
+ '⫚' => '⫚',
+ '⫛' => '⫛',
+ '⫤' => '⫤',
+ '⫦' => '⫦',
+ '⫧' => '⫧',
+ '⫨' => '⫨',
+ '⫩' => '⫩',
+ '⫫' => '⫫',
+ '⫬' => '⫬',
+ '⫭' => '⫭',
+ '⫮' => '⫮',
+ '⫯' => '⫯',
+ '⫰' => '⫰',
+ '⫱' => '⫱',
+ '⫲' => '⫲',
+ '⫳' => '⫳',
+ '⫽︀' => '&varsupsetneqq',
+ 'ff' => 'ff',
+ 'fi' => 'fi',
+ 'fl' => 'fl',
+ 'ffi' => 'ffi',
+ 'ffl' => 'ffl',
+ '𝒜' => '𝒜',
+ '𝒞' => '𝒞',
+ '𝒟' => '𝒟',
+ '𝒢' => '𝒢',
+ '𝒥' => '𝒥',
+ '𝒦' => '𝒦',
+ '𝒩' => '𝒩',
+ '𝒪' => '𝒪',
+ '𝒫' => '𝒫',
+ '𝒬' => '𝒬',
+ '𝒮' => '𝒮',
+ '𝒯' => '𝒯',
+ '𝒰' => '𝒰',
+ '𝒱' => '𝒱',
+ '𝒲' => '𝒲',
+ '𝒳' => '𝒳',
+ '𝒴' => '𝒴',
+ '𝒵' => '𝒵',
+ '𝒶' => '𝒶',
+ '𝒷' => '𝒷',
+ '𝒸' => '𝒸',
+ '𝒹' => '𝒹',
+ '𝒻' => '𝒻',
+ '𝒽' => '𝒽',
+ '𝒾' => '𝒾',
+ '𝒿' => '𝒿',
+ '𝓀' => '𝓀',
+ '𝓁' => '𝓁',
+ '𝓂' => '𝓂',
+ '𝓃' => '𝓃',
+ '𝓅' => '𝓅',
+ '𝓆' => '𝓆',
+ '𝓇' => '𝓇',
+ '𝓈' => '𝓈',
+ '𝓉' => '𝓉',
+ '𝓊' => '𝓊',
+ '𝓋' => '𝓋',
+ '𝓌' => '𝓌',
+ '𝓍' => '𝓍',
+ '𝓎' => '𝓎',
+ '𝓏' => '𝓏',
+ '𝔄' => '𝔄',
+ '𝔅' => '𝔅',
+ '𝔇' => '𝔇',
+ '𝔈' => '𝔈',
+ '𝔉' => '𝔉',
+ '𝔊' => '𝔊',
+ '𝔍' => '𝔍',
+ '𝔎' => '𝔎',
+ '𝔏' => '𝔏',
+ '𝔐' => '𝔐',
+ '𝔑' => '𝔑',
+ '𝔒' => '𝔒',
+ '𝔓' => '𝔓',
+ '𝔔' => '𝔔',
+ '𝔖' => '𝔖',
+ '𝔗' => '𝔗',
+ '𝔘' => '𝔘',
+ '𝔙' => '𝔙',
+ '𝔚' => '𝔚',
+ '𝔛' => '𝔛',
+ '𝔜' => '𝔜',
+ '𝔞' => '𝔞',
+ '𝔟' => '𝔟',
+ '𝔠' => '𝔠',
+ '𝔡' => '𝔡',
+ '𝔢' => '𝔢',
+ '𝔣' => '𝔣',
+ '𝔤' => '𝔤',
+ '𝔥' => '𝔥',
+ '𝔦' => '𝔦',
+ '𝔧' => '𝔧',
+ '𝔨' => '𝔨',
+ '𝔩' => '𝔩',
+ '𝔪' => '𝔪',
+ '𝔫' => '𝔫',
+ '𝔬' => '𝔬',
+ '𝔭' => '𝔭',
+ '𝔮' => '𝔮',
+ '𝔯' => '𝔯',
+ '𝔰' => '𝔰',
+ '𝔱' => '𝔱',
+ '𝔲' => '𝔲',
+ '𝔳' => '𝔳',
+ '𝔴' => '𝔴',
+ '𝔵' => '𝔵',
+ '𝔶' => '𝔶',
+ '𝔷' => '𝔷',
+ '𝔸' => '𝔸',
+ '𝔹' => '𝔹',
+ '𝔻' => '𝔻',
+ '𝔼' => '𝔼',
+ '𝔽' => '𝔽',
+ '𝔾' => '𝔾',
+ '𝕀' => '𝕀',
+ '𝕁' => '𝕁',
+ '𝕂' => '𝕂',
+ '𝕃' => '𝕃',
+ '𝕄' => '𝕄',
+ '𝕆' => '𝕆',
+ '𝕊' => '𝕊',
+ '𝕋' => '𝕋',
+ '𝕌' => '𝕌',
+ '𝕍' => '𝕍',
+ '𝕎' => '𝕎',
+ '𝕏' => '𝕏',
+ '𝕐' => '𝕐',
+ '𝕒' => '𝕒',
+ '𝕓' => '𝕓',
+ '𝕔' => '𝕔',
+ '𝕕' => '𝕕',
+ '𝕖' => '𝕖',
+ '𝕗' => '𝕗',
+ '𝕘' => '𝕘',
+ '𝕙' => '𝕙',
+ '𝕚' => '𝕚',
+ '𝕛' => '𝕛',
+ '𝕜' => '𝕜',
+ '𝕝' => '𝕝',
+ '𝕞' => '𝕞',
+ '𝕟' => '𝕟',
+ '𝕠' => '𝕠',
+ '𝕡' => '𝕡',
+ '𝕢' => '𝕢',
+ '𝕣' => '𝕣',
+ '𝕤' => '𝕤',
+ '𝕥' => '𝕥',
+ '𝕦' => '𝕦',
+ '𝕧' => '𝕧',
+ '𝕨' => '𝕨',
+ '𝕩' => '𝕩',
+ '𝕪' => '𝕪',
+ '𝕫' => '𝕫',
+ );
+}
diff --git a/masterminds/html5/src/HTML5/Serializer/OutputRules.php b/masterminds/html5/src/HTML5/Serializer/OutputRules.php
new file mode 100644
index 000000000..ec467f22c
--- /dev/null
+++ b/masterminds/html5/src/HTML5/Serializer/OutputRules.php
@@ -0,0 +1,553 @@
+'http://www.w3.org/1999/xhtml',
+ 'attrNamespace'=>'http://www.w3.org/1999/xhtml',
+
+ 'nodeName'=>'img', 'nodeName'=>array('img', 'a'),
+ 'attrName'=>'alt', 'attrName'=>array('title', 'alt'),
+ ),
+ */
+ array(
+ 'nodeNamespace' => 'http://www.w3.org/1999/xhtml',
+ 'attrName' => array('href',
+ 'hreflang',
+ 'http-equiv',
+ 'icon',
+ 'id',
+ 'keytype',
+ 'kind',
+ 'label',
+ 'lang',
+ 'language',
+ 'list',
+ 'maxlength',
+ 'media',
+ 'method',
+ 'name',
+ 'placeholder',
+ 'rel',
+ 'rows',
+ 'rowspan',
+ 'sandbox',
+ 'spellcheck',
+ 'scope',
+ 'seamless',
+ 'shape',
+ 'size',
+ 'sizes',
+ 'span',
+ 'src',
+ 'srcdoc',
+ 'srclang',
+ 'srcset',
+ 'start',
+ 'step',
+ 'style',
+ 'summary',
+ 'tabindex',
+ 'target',
+ 'title',
+ 'type',
+ 'value',
+ 'width',
+ 'border',
+ 'charset',
+ 'cite',
+ 'class',
+ 'code',
+ 'codebase',
+ 'color',
+ 'cols',
+ 'colspan',
+ 'content',
+ 'coords',
+ 'data',
+ 'datetime',
+ 'default',
+ 'dir',
+ 'dirname',
+ 'enctype',
+ 'for',
+ 'form',
+ 'formaction',
+ 'headers',
+ 'height',
+ 'accept',
+ 'accept-charset',
+ 'accesskey',
+ 'action',
+ 'align',
+ 'alt',
+ 'bgcolor',
+ ),
+ ),
+ array(
+ 'nodeNamespace' => 'http://www.w3.org/1999/xhtml',
+ 'xpath' => 'starts-with(local-name(), \'data-\')',
+ ),
+ );
+
+ const DOCTYPE = '';
+
+ public function __construct($output, $options = array())
+ {
+ if (isset($options['encode_entities'])) {
+ $this->encode = $options['encode_entities'];
+ }
+
+ $this->outputMode = static::IM_IN_HTML;
+ $this->out = $output;
+ $this->hasHTML5 = defined('ENT_HTML5');
+ }
+
+ public function addRule(array $rule)
+ {
+ $this->nonBooleanAttributes[] = $rule;
+ }
+
+ public function setTraverser(Traverser $traverser)
+ {
+ $this->traverser = $traverser;
+
+ return $this;
+ }
+
+ public function unsetTraverser()
+ {
+ $this->traverser = null;
+
+ return $this;
+ }
+
+ public function document($dom)
+ {
+ $this->doctype();
+ if ($dom->documentElement) {
+ foreach ($dom->childNodes as $node) {
+ $this->traverser->node($node);
+ }
+ $this->nl();
+ }
+ }
+
+ protected function doctype()
+ {
+ $this->wr(static::DOCTYPE);
+ $this->nl();
+ }
+
+ public function element($ele)
+ {
+ $name = $ele->tagName;
+
+ // Per spec:
+ // If the element has a declared namespace in the HTML, MathML or
+ // SVG namespaces, we use the lname instead of the tagName.
+ if ($this->traverser->isLocalElement($ele)) {
+ $name = $ele->localName;
+ }
+
+ // If we are in SVG or MathML there is special handling.
+ // Using if/elseif instead of switch because it's faster in PHP.
+ if ('svg' == $name) {
+ $this->outputMode = static::IM_IN_SVG;
+ $name = Elements::normalizeSvgElement($name);
+ } elseif ('math' == $name) {
+ $this->outputMode = static::IM_IN_MATHML;
+ }
+
+ $this->openTag($ele);
+ if (Elements::isA($name, Elements::TEXT_RAW)) {
+ foreach ($ele->childNodes as $child) {
+ if ($child instanceof \DOMCharacterData) {
+ $this->wr($child->data);
+ } elseif ($child instanceof \DOMElement) {
+ $this->element($child);
+ }
+ }
+ } else {
+ // Handle children.
+ if ($ele->hasChildNodes()) {
+ $this->traverser->children($ele->childNodes);
+ }
+
+ // Close out the SVG or MathML special handling.
+ if ('svg' == $name || 'math' == $name) {
+ $this->outputMode = static::IM_IN_HTML;
+ }
+ }
+
+ // If not unary, add a closing tag.
+ if (!Elements::isA($name, Elements::VOID_TAG)) {
+ $this->closeTag($ele);
+ }
+ }
+
+ /**
+ * Write a text node.
+ *
+ * @param \DOMText $ele The text node to write.
+ */
+ public function text($ele)
+ {
+ if (isset($ele->parentNode) && isset($ele->parentNode->tagName) && Elements::isA($ele->parentNode->localName, Elements::TEXT_RAW)) {
+ $this->wr($ele->data);
+
+ return;
+ }
+
+ // FIXME: This probably needs some flags set.
+ $this->wr($this->enc($ele->data));
+ }
+
+ public function cdata($ele)
+ {
+ // This encodes CDATA.
+ $this->wr($ele->ownerDocument->saveXML($ele));
+ }
+
+ public function comment($ele)
+ {
+ // These produce identical output.
+ // $this->wr('');
+ $this->wr($ele->ownerDocument->saveXML($ele));
+ }
+
+ public function processorInstruction($ele)
+ {
+ $this->wr('')
+ ->wr($ele->target)
+ ->wr(' ')
+ ->wr($ele->data)
+ ->wr('?>');
+ }
+
+ /**
+ * Write the namespace attributes.
+ *
+ * @param \DOMNode $ele The element being written.
+ */
+ protected function namespaceAttrs($ele)
+ {
+ if (!$this->xpath || $this->xpath->document !== $ele->ownerDocument) {
+ $this->xpath = new \DOMXPath($ele->ownerDocument);
+ }
+
+ foreach ($this->xpath->query('namespace::*[not(.=../../namespace::*)]', $ele) as $nsNode) {
+ if (!in_array($nsNode->nodeValue, $this->implicitNamespaces)) {
+ $this->wr(' ')->wr($nsNode->nodeName)->wr('="')->wr($nsNode->nodeValue)->wr('"');
+ }
+ }
+ }
+
+ /**
+ * Write the opening tag.
+ *
+ * Tags for HTML, MathML, and SVG are in the local name. Otherwise, use the
+ * qualified name (8.3).
+ *
+ * @param \DOMNode $ele The element being written.
+ */
+ protected function openTag($ele)
+ {
+ $this->wr('<')->wr($this->traverser->isLocalElement($ele) ? $ele->localName : $ele->tagName);
+
+ $this->attrs($ele);
+ $this->namespaceAttrs($ele);
+
+ if ($this->outputMode == static::IM_IN_HTML) {
+ $this->wr('>');
+ } // If we are not in html mode we are in SVG, MathML, or XML embedded content.
+ else {
+ if ($ele->hasChildNodes()) {
+ $this->wr('>');
+ } // If there are no children this is self closing.
+ else {
+ $this->wr(' />');
+ }
+ }
+ }
+
+ protected function attrs($ele)
+ {
+ // FIXME: Needs support for xml, xmlns, xlink, and namespaced elements.
+ if (!$ele->hasAttributes()) {
+ return $this;
+ }
+
+ // TODO: Currently, this always writes name="value", and does not do
+ // value-less attributes.
+ $map = $ele->attributes;
+ $len = $map->length;
+ for ($i = 0; $i < $len; ++$i) {
+ $node = $map->item($i);
+ $val = $this->enc($node->value, true);
+
+ // XXX: The spec says that we need to ensure that anything in
+ // the XML, XMLNS, or XLink NS's should use the canonical
+ // prefix. It seems that DOM does this for us already, but there
+ // may be exceptions.
+ $name = $node->nodeName;
+
+ // Special handling for attributes in SVG and MathML.
+ // Using if/elseif instead of switch because it's faster in PHP.
+ if ($this->outputMode == static::IM_IN_SVG) {
+ $name = Elements::normalizeSvgAttribute($name);
+ } elseif ($this->outputMode == static::IM_IN_MATHML) {
+ $name = Elements::normalizeMathMlAttribute($name);
+ }
+
+ $this->wr(' ')->wr($name);
+
+ if ((isset($val) && '' !== $val) || $this->nonBooleanAttribute($node)) {
+ $this->wr('="')->wr($val)->wr('"');
+ }
+ }
+ }
+
+ protected function nonBooleanAttribute(\DOMAttr $attr)
+ {
+ $ele = $attr->ownerElement;
+ foreach ($this->nonBooleanAttributes as $rule) {
+ if (isset($rule['nodeNamespace']) && $rule['nodeNamespace'] !== $ele->namespaceURI) {
+ continue;
+ }
+ if (isset($rule['attNamespace']) && $rule['attNamespace'] !== $attr->namespaceURI) {
+ continue;
+ }
+ if (isset($rule['nodeName']) && !is_array($rule['nodeName']) && $rule['nodeName'] !== $ele->localName) {
+ continue;
+ }
+ if (isset($rule['nodeName']) && is_array($rule['nodeName']) && !in_array($ele->localName, $rule['nodeName'], true)) {
+ continue;
+ }
+ if (isset($rule['attrName']) && !is_array($rule['attrName']) && $rule['attrName'] !== $attr->localName) {
+ continue;
+ }
+ if (isset($rule['attrName']) && is_array($rule['attrName']) && !in_array($attr->localName, $rule['attrName'], true)) {
+ continue;
+ }
+ if (isset($rule['xpath'])) {
+ $xp = $this->getXPath($attr);
+ if (isset($rule['prefixes'])) {
+ foreach ($rule['prefixes'] as $nsPrefix => $ns) {
+ $xp->registerNamespace($nsPrefix, $ns);
+ }
+ }
+ if (!$xp->evaluate($rule['xpath'], $attr)) {
+ continue;
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private function getXPath(\DOMNode $node)
+ {
+ if (!$this->xpath) {
+ $this->xpath = new \DOMXPath($node->ownerDocument);
+ }
+
+ return $this->xpath;
+ }
+
+ /**
+ * Write the closing tag.
+ *
+ * Tags for HTML, MathML, and SVG are in the local name. Otherwise, use the
+ * qualified name (8.3).
+ *
+ * @param \DOMNode $ele The element being written.
+ */
+ protected function closeTag($ele)
+ {
+ if ($this->outputMode == static::IM_IN_HTML || $ele->hasChildNodes()) {
+ $this->wr('')->wr($this->traverser->isLocalElement($ele) ? $ele->localName : $ele->tagName)->wr('>');
+ }
+ }
+
+ /**
+ * Write to the output.
+ *
+ * @param string $text The string to put into the output
+ *
+ * @return $this
+ */
+ protected function wr($text)
+ {
+ fwrite($this->out, $text);
+
+ return $this;
+ }
+
+ /**
+ * Write a new line character.
+ *
+ * @return $this
+ */
+ protected function nl()
+ {
+ fwrite($this->out, PHP_EOL);
+
+ return $this;
+ }
+
+ /**
+ * Encode text.
+ *
+ * When encode is set to false, the default value, the text passed in is
+ * escaped per section 8.3 of the html5 spec. For details on how text is
+ * escaped see the escape() method.
+ *
+ * When encoding is set to true the text is converted to named character
+ * references where appropriate. Section 8.1.4 Character references of the
+ * html5 spec refers to using named character references. This is useful for
+ * characters that can't otherwise legally be used in the text.
+ *
+ * The named character references are listed in section 8.5.
+ *
+ * @see http://www.w3.org/TR/2013/CR-html5-20130806/syntax.html#named-character-references True encoding will turn all named character references into their entities.
+ * This includes such characters as +.# and many other common ones. By default
+ * encoding here will just escape &'<>".
+ *
+ * Note, PHP 5.4+ has better html5 encoding.
+ *
+ * @todo Use the Entities class in php 5.3 to have html5 entities.
+ *
+ * @param string $text Text to encode.
+ * @param bool $attribute True if we are encoding an attrubute, false otherwise.
+ *
+ * @return string The encoded text.
+ */
+ protected function enc($text, $attribute = false)
+ {
+ // Escape the text rather than convert to named character references.
+ if (!$this->encode) {
+ return $this->escape($text, $attribute);
+ }
+
+ // If we are in PHP 5.4+ we can use the native html5 entity functionality to
+ // convert the named character references.
+
+ if ($this->hasHTML5) {
+ return htmlentities($text, ENT_HTML5 | ENT_SUBSTITUTE | ENT_QUOTES, 'UTF-8', false);
+ } // If a version earlier than 5.4 html5 entities are not entirely handled.
+ // This manually handles them.
+ else {
+ return strtr($text, HTML5Entities::$map);
+ }
+ }
+
+ /**
+ * Escape test.
+ *
+ * According to the html5 spec section 8.3 Serializing HTML fragments, text
+ * within tags that are not style, script, xmp, iframe, noembed, and noframes
+ * need to be properly escaped.
+ *
+ * The & should be converted to &, no breaking space unicode characters
+ * converted to , when in attribute mode the " should be converted to
+ * ", and when not in attribute mode the < and > should be converted to
+ * < and >.
+ *
+ * @see http://www.w3.org/TR/2013/CR-html5-20130806/syntax.html#escapingString
+ *
+ * @param string $text Text to escape.
+ * @param bool $attribute True if we are escaping an attrubute, false otherwise.
+ */
+ protected function escape($text, $attribute = false)
+ {
+ // Not using htmlspecialchars because, while it does escaping, it doesn't
+ // match the requirements of section 8.5. For example, it doesn't handle
+ // non-breaking spaces.
+ if ($attribute) {
+ $replace = array(
+ '"' => '"',
+ '&' => '&',
+ "\xc2\xa0" => ' ',
+ );
+ } else {
+ $replace = array(
+ '<' => '<',
+ '>' => '>',
+ '&' => '&',
+ "\xc2\xa0" => ' ',
+ );
+ }
+
+ return strtr($text, $replace);
+ }
+}
diff --git a/masterminds/html5/src/HTML5/Serializer/RulesInterface.php b/masterminds/html5/src/HTML5/Serializer/RulesInterface.php
new file mode 100644
index 000000000..69a6ecdad
--- /dev/null
+++ b/masterminds/html5/src/HTML5/Serializer/RulesInterface.php
@@ -0,0 +1,99 @@
+ 'html',
+ 'http://www.w3.org/1998/Math/MathML' => 'math',
+ 'http://www.w3.org/2000/svg' => 'svg',
+ );
+
+ protected $dom;
+
+ protected $options;
+
+ protected $encode = false;
+
+ protected $rules;
+
+ protected $out;
+
+ /**
+ * Create a traverser.
+ *
+ * @param \DOMNode|\DOMNodeList $dom The document or node to traverse.
+ * @param resource $out A stream that allows writing. The traverser will output into this
+ * stream.
+ * @param array $options An array of options for the traverser as key/value pairs. These include:
+ * - encode_entities: A bool to specify if full encding should happen for all named
+ * charachter references. Defaults to false which escapes &'<>".
+ * - output_rules: The path to the class handling the output rules.
+ */
+ public function __construct($dom, $out, RulesInterface $rules, $options = array())
+ {
+ $this->dom = $dom;
+ $this->out = $out;
+ $this->rules = $rules;
+ $this->options = $options;
+
+ $this->rules->setTraverser($this);
+ }
+
+ /**
+ * Tell the traverser to walk the DOM.
+ *
+ * @return resource $out Returns the output stream.
+ */
+ public function walk()
+ {
+ if ($this->dom instanceof \DOMDocument) {
+ $this->rules->document($this->dom);
+ } elseif ($this->dom instanceof \DOMDocumentFragment) {
+ // Document fragments are a special case. Only the children need to
+ // be serialized.
+ if ($this->dom->hasChildNodes()) {
+ $this->children($this->dom->childNodes);
+ }
+ } // If NodeList, loop
+ elseif ($this->dom instanceof \DOMNodeList) {
+ // If this is a NodeList of DOMDocuments this will not work.
+ $this->children($this->dom);
+ } // Else assume this is a DOMNode-like datastructure.
+ else {
+ $this->node($this->dom);
+ }
+
+ return $this->out;
+ }
+
+ /**
+ * Process a node in the DOM.
+ *
+ * @param mixed $node A node implementing \DOMNode.
+ */
+ public function node($node)
+ {
+ // A listing of types is at http://php.net/manual/en/dom.constants.php
+ switch ($node->nodeType) {
+ case XML_ELEMENT_NODE:
+ $this->rules->element($node);
+ break;
+ case XML_TEXT_NODE:
+ $this->rules->text($node);
+ break;
+ case XML_CDATA_SECTION_NODE:
+ $this->rules->cdata($node);
+ break;
+ case XML_PI_NODE:
+ $this->rules->processorInstruction($node);
+ break;
+ case XML_COMMENT_NODE:
+ $this->rules->comment($node);
+ break;
+ // Currently we don't support embedding DTDs.
+ default:
+ //print '';
+ break;
+ }
+ }
+
+ /**
+ * Walk through all the nodes on a node list.
+ *
+ * @param \DOMNodeList $nl A list of child elements to walk through.
+ */
+ public function children($nl)
+ {
+ foreach ($nl as $node) {
+ $this->node($node);
+ }
+ }
+
+ /**
+ * Is an element local?
+ *
+ * @param mixed $ele An element that implement \DOMNode.
+ *
+ * @return bool true if local and false otherwise.
+ */
+ public function isLocalElement($ele)
+ {
+ $uri = $ele->namespaceURI;
+ if (empty($uri)) {
+ return false;
+ }
+
+ return isset(static::$local_ns[$uri]);
+ }
+}
diff --git a/mtdowling/jmespath.php/README.rst b/mtdowling/jmespath.php/README.rst
deleted file mode 100644
index b65ee4665..000000000
--- a/mtdowling/jmespath.php/README.rst
+++ /dev/null
@@ -1,123 +0,0 @@
-============
-jmespath.php
-============
-
-JMESPath (pronounced "jaymz path") allows you to declaratively specify how to
-extract elements from a JSON document. *jmespath.php* allows you to use
-JMESPath in PHP applications with PHP data structures. It requires PHP 5.4 or
-greater and can be installed through `Composer `_
-using the ``mtdowling/jmespath.php`` package.
-
-.. code-block:: php
-
- require 'vendor/autoload.php';
-
- $expression = 'foo.*.baz';
-
- $data = [
- 'foo' => [
- 'bar' => ['baz' => 1],
- 'bam' => ['baz' => 2],
- 'boo' => ['baz' => 3]
- ]
- ];
-
- JmesPath\search($expression, $data);
- // Returns: [1, 2, 3]
-
-- `JMESPath Tutorial `_
-- `JMESPath Grammar `_
-- `JMESPath Python library `_
-
-PHP Usage
-=========
-
-The ``JmesPath\search`` function can be used in most cases when using the
-library. This function utilizes a JMESPath runtime based on your environment.
-The runtime utilized can be configured using environment variables and may at
-some point in the future automatically utilize a C extension if available.
-
-.. code-block:: php
-
- $result = JmesPath\search($expression, $data);
-
- // or, if you require PSR-4 compliance.
- $result = JmesPath\Env::search($expression, $data);
-
-Runtimes
---------
-
-jmespath.php utilizes *runtimes*. There are currently two runtimes:
-AstRuntime and CompilerRuntime.
-
-AstRuntime is utilized by ``JmesPath\search()`` and ``JmesPath\Env::search()``
-by default.
-
-AstRuntime
-~~~~~~~~~~
-
-The AstRuntime will parse an expression, cache the resulting AST in memory,
-and interpret the AST using an external tree visitor. AstRuntime provides a
-good general approach for interpreting JMESPath expressions that have a low to
-moderate level of reuse.
-
-.. code-block:: php
-
- $runtime = new JmesPath\AstRuntime();
- $runtime('foo.bar', ['foo' => ['bar' => 'baz']]);
- // > 'baz'
-
-CompilerRuntime
-~~~~~~~~~~~~~~~
-
-``JmesPath\CompilerRuntime`` provides the most performance for
-applications that have a moderate to high level of reuse of JMESPath
-expressions. The CompilerRuntime will walk a JMESPath AST and emit PHP source
-code, resulting in anywhere from 7x to 60x speed improvements.
-
-Compiling JMESPath expressions to source code is a slower process than just
-walking and interpreting a JMESPath AST (via the AstRuntime). However,
-running the compiled JMESPath code results in much better performance than
-walking an AST. This essentially means that there is a warm-up period when
-using the ``CompilerRuntime``, but after the warm-up period, it will provide
-much better performance.
-
-Use the CompilerRuntime if you know that you will be executing JMESPath
-expressions more than once or if you can pre-compile JMESPath expressions
-before executing them (for example, server-side applications).
-
-.. code-block:: php
-
- // Note: The cache directory argument is optional.
- $runtime = new JmesPath\CompilerRuntime('/path/to/compile/folder');
- $runtime('foo.bar', ['foo' => ['bar' => 'baz']]);
- // > 'baz'
-
-Environment Variables
-^^^^^^^^^^^^^^^^^^^^^
-
-You can utilize the CompilerRuntime in ``JmesPath\search()`` by setting
-the ``JP_PHP_COMPILE`` environment variable to "on" or to a directory
-on disk used to store cached expressions.
-
-Testing
-=======
-
-A comprehensive list of test cases can be found at
-https://github.com/jmespath/jmespath.php/tree/master/tests/compliance.
-These compliance tests are utilized by jmespath.php to ensure consistency with
-other implementations, and can serve as examples of the language.
-
-jmespath.php is tested using PHPUnit. In order to run the tests, you need to
-first install the dependencies using Composer as described in the *Installation*
-section. Next you just need to run the tests via make:
-
-.. code-block:: bash
-
- make test
-
-You can run a suite of performance tests as well:
-
-.. code-block:: bash
-
- make perf
diff --git a/symfony/css-selector/CssSelectorConverter.php b/symfony/css-selector/CssSelectorConverter.php
index bbb6afe21..7120a2950 100644
--- a/symfony/css-selector/CssSelectorConverter.php
+++ b/symfony/css-selector/CssSelectorConverter.php
@@ -26,11 +26,11 @@
*/
class CssSelectorConverter
{
- private $translator;
- private $cache;
+ private Translator $translator;
+ private array $cache;
- private static $xmlCache = [];
- private static $htmlCache = [];
+ private static array $xmlCache = [];
+ private static array $htmlCache = [];
/**
* @param bool $html Whether HTML support should be enabled. Disable it for XML documents
@@ -59,11 +59,9 @@ public function __construct(bool $html = true)
*
* Optionally, a prefix can be added to the resulting XPath
* expression with the $prefix parameter.
- *
- * @return string
*/
- public function toXPath(string $cssExpr, string $prefix = 'descendant-or-self::')
+ public function toXPath(string $cssExpr, string $prefix = 'descendant-or-self::'): string
{
- return $this->cache[$prefix][$cssExpr] ?? $this->cache[$prefix][$cssExpr] = $this->translator->cssToXPath($cssExpr, $prefix);
+ return $this->cache[$prefix][$cssExpr] ??= $this->translator->cssToXPath($cssExpr, $prefix);
}
}
diff --git a/symfony/css-selector/Exception/SyntaxErrorException.php b/symfony/css-selector/Exception/SyntaxErrorException.php
index 7deacf9c5..5a9d8078d 100644
--- a/symfony/css-selector/Exception/SyntaxErrorException.php
+++ b/symfony/css-selector/Exception/SyntaxErrorException.php
@@ -23,42 +23,32 @@
*/
class SyntaxErrorException extends ParseException
{
- /**
- * @return self
- */
- public static function unexpectedToken(string $expectedValue, Token $foundToken)
+ public static function unexpectedToken(string $expectedValue, Token $foundToken): self
{
return new self(sprintf('Expected %s, but %s found.', $expectedValue, $foundToken));
}
- /**
- * @return self
- */
- public static function pseudoElementFound(string $pseudoElement, string $unexpectedLocation)
+ public static function pseudoElementFound(string $pseudoElement, string $unexpectedLocation): self
{
return new self(sprintf('Unexpected pseudo-element "::%s" found %s.', $pseudoElement, $unexpectedLocation));
}
- /**
- * @return self
- */
- public static function unclosedString(int $position)
+ public static function unclosedString(int $position): self
{
return new self(sprintf('Unclosed/invalid string at %s.', $position));
}
- /**
- * @return self
- */
- public static function nestedNot()
+ public static function nestedNot(): self
{
return new self('Got nested ::not().');
}
- /**
- * @return self
- */
- public static function stringAsFunctionArgument()
+ public static function notAtTheStartOfASelector(string $pseudoElement): self
+ {
+ return new self(sprintf('Got immediate child pseudo-element ":%s" not at the start of a selector', $pseudoElement));
+ }
+
+ public static function stringAsFunctionArgument(): self
{
return new self('String not allowed as function argument.');
}
diff --git a/symfony/css-selector/LICENSE b/symfony/css-selector/LICENSE
index 88bf75bb4..0138f8f07 100644
--- a/symfony/css-selector/LICENSE
+++ b/symfony/css-selector/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2022 Fabien Potencier
+Copyright (c) 2004-present Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/symfony/css-selector/Node/AbstractNode.php b/symfony/css-selector/Node/AbstractNode.php
index 1306aeacb..d99e80a87 100644
--- a/symfony/css-selector/Node/AbstractNode.php
+++ b/symfony/css-selector/Node/AbstractNode.php
@@ -23,17 +23,10 @@
*/
abstract class AbstractNode implements NodeInterface
{
- /**
- * @var string
- */
- private $nodeName;
+ private string $nodeName;
public function getNodeName(): string
{
- if (null === $this->nodeName) {
- $this->nodeName = preg_replace('~.*\\\\([^\\\\]+)Node$~', '$1', static::class);
- }
-
- return $this->nodeName;
+ return $this->nodeName ??= preg_replace('~.*\\\\([^\\\\]+)Node$~', '$1', static::class);
}
}
diff --git a/symfony/css-selector/Node/AttributeNode.php b/symfony/css-selector/Node/AttributeNode.php
index 0b6e0ee0a..ba4df3100 100644
--- a/symfony/css-selector/Node/AttributeNode.php
+++ b/symfony/css-selector/Node/AttributeNode.php
@@ -23,11 +23,11 @@
*/
class AttributeNode extends AbstractNode
{
- private $selector;
- private $namespace;
- private $attribute;
- private $operator;
- private $value;
+ private NodeInterface $selector;
+ private ?string $namespace;
+ private string $attribute;
+ private string $operator;
+ private ?string $value;
public function __construct(NodeInterface $selector, ?string $namespace, string $attribute, string $operator, ?string $value)
{
@@ -63,9 +63,6 @@ public function getValue(): ?string
return $this->value;
}
- /**
- * {@inheritdoc}
- */
public function getSpecificity(): Specificity
{
return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0));
diff --git a/symfony/css-selector/Node/ClassNode.php b/symfony/css-selector/Node/ClassNode.php
index 1efca808d..717445870 100644
--- a/symfony/css-selector/Node/ClassNode.php
+++ b/symfony/css-selector/Node/ClassNode.php
@@ -23,8 +23,8 @@
*/
class ClassNode extends AbstractNode
{
- private $selector;
- private $name;
+ private NodeInterface $selector;
+ private string $name;
public function __construct(NodeInterface $selector, string $name)
{
@@ -42,9 +42,6 @@ public function getName(): string
return $this->name;
}
- /**
- * {@inheritdoc}
- */
public function getSpecificity(): Specificity
{
return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0));
diff --git a/symfony/css-selector/Node/CombinedSelectorNode.php b/symfony/css-selector/Node/CombinedSelectorNode.php
index a217a45ed..19fecb700 100644
--- a/symfony/css-selector/Node/CombinedSelectorNode.php
+++ b/symfony/css-selector/Node/CombinedSelectorNode.php
@@ -23,9 +23,9 @@
*/
class CombinedSelectorNode extends AbstractNode
{
- private $selector;
- private $combinator;
- private $subSelector;
+ private NodeInterface $selector;
+ private string $combinator;
+ private NodeInterface $subSelector;
public function __construct(NodeInterface $selector, string $combinator, NodeInterface $subSelector)
{
@@ -49,9 +49,6 @@ public function getSubSelector(): NodeInterface
return $this->subSelector;
}
- /**
- * {@inheritdoc}
- */
public function getSpecificity(): Specificity
{
return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity());
diff --git a/symfony/css-selector/Node/ElementNode.php b/symfony/css-selector/Node/ElementNode.php
index fbf8ea0f9..76d2078ea 100644
--- a/symfony/css-selector/Node/ElementNode.php
+++ b/symfony/css-selector/Node/ElementNode.php
@@ -23,10 +23,10 @@
*/
class ElementNode extends AbstractNode
{
- private $namespace;
- private $element;
+ private ?string $namespace;
+ private ?string $element;
- public function __construct(string $namespace = null, string $element = null)
+ public function __construct(?string $namespace = null, ?string $element = null)
{
$this->namespace = $namespace;
$this->element = $element;
@@ -42,9 +42,6 @@ public function getElement(): ?string
return $this->element;
}
- /**
- * {@inheritdoc}
- */
public function getSpecificity(): Specificity
{
return new Specificity(0, 0, $this->element ? 1 : 0);
diff --git a/symfony/css-selector/Node/FunctionNode.php b/symfony/css-selector/Node/FunctionNode.php
index c464cf7c0..938a82b1a 100644
--- a/symfony/css-selector/Node/FunctionNode.php
+++ b/symfony/css-selector/Node/FunctionNode.php
@@ -25,9 +25,9 @@
*/
class FunctionNode extends AbstractNode
{
- private $selector;
- private $name;
- private $arguments;
+ private NodeInterface $selector;
+ private string $name;
+ private array $arguments;
/**
* @param Token[] $arguments
@@ -57,9 +57,6 @@ public function getArguments(): array
return $this->arguments;
}
- /**
- * {@inheritdoc}
- */
public function getSpecificity(): Specificity
{
return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0));
@@ -67,9 +64,7 @@ public function getSpecificity(): Specificity
public function __toString(): string
{
- $arguments = implode(', ', array_map(function (Token $token) {
- return "'".$token->getValue()."'";
- }, $this->arguments));
+ $arguments = implode(', ', array_map(fn (Token $token) => "'".$token->getValue()."'", $this->arguments));
return sprintf('%s[%s:%s(%s)]', $this->getNodeName(), $this->selector, $this->name, $arguments ? '['.$arguments.']' : '');
}
diff --git a/symfony/css-selector/Node/HashNode.php b/symfony/css-selector/Node/HashNode.php
index 94114c095..3af8e8474 100644
--- a/symfony/css-selector/Node/HashNode.php
+++ b/symfony/css-selector/Node/HashNode.php
@@ -23,8 +23,8 @@
*/
class HashNode extends AbstractNode
{
- private $selector;
- private $id;
+ private NodeInterface $selector;
+ private string $id;
public function __construct(NodeInterface $selector, string $id)
{
@@ -42,9 +42,6 @@ public function getId(): string
return $this->id;
}
- /**
- * {@inheritdoc}
- */
public function getSpecificity(): Specificity
{
return $this->selector->getSpecificity()->plus(new Specificity(1, 0, 0));
diff --git a/symfony/css-selector/Node/NegationNode.php b/symfony/css-selector/Node/NegationNode.php
index f00522fb9..f80675838 100644
--- a/symfony/css-selector/Node/NegationNode.php
+++ b/symfony/css-selector/Node/NegationNode.php
@@ -23,8 +23,8 @@
*/
class NegationNode extends AbstractNode
{
- private $selector;
- private $subSelector;
+ private NodeInterface $selector;
+ private NodeInterface $subSelector;
public function __construct(NodeInterface $selector, NodeInterface $subSelector)
{
@@ -42,9 +42,6 @@ public function getSubSelector(): NodeInterface
return $this->subSelector;
}
- /**
- * {@inheritdoc}
- */
public function getSpecificity(): Specificity
{
return $this->selector->getSpecificity()->plus($this->subSelector->getSpecificity());
diff --git a/symfony/css-selector/Node/NodeInterface.php b/symfony/css-selector/Node/NodeInterface.php
index b078d26d4..7d541f9c8 100644
--- a/symfony/css-selector/Node/NodeInterface.php
+++ b/symfony/css-selector/Node/NodeInterface.php
@@ -21,11 +21,9 @@
*
* @internal
*/
-interface NodeInterface
+interface NodeInterface extends \Stringable
{
public function getNodeName(): string;
public function getSpecificity(): Specificity;
-
- public function __toString(): string;
}
diff --git a/symfony/css-selector/Node/PseudoNode.php b/symfony/css-selector/Node/PseudoNode.php
index 12b7bd266..c21cd6e92 100644
--- a/symfony/css-selector/Node/PseudoNode.php
+++ b/symfony/css-selector/Node/PseudoNode.php
@@ -23,8 +23,8 @@
*/
class PseudoNode extends AbstractNode
{
- private $selector;
- private $identifier;
+ private NodeInterface $selector;
+ private string $identifier;
public function __construct(NodeInterface $selector, string $identifier)
{
@@ -42,9 +42,6 @@ public function getIdentifier(): string
return $this->identifier;
}
- /**
- * {@inheritdoc}
- */
public function getSpecificity(): Specificity
{
return $this->selector->getSpecificity()->plus(new Specificity(0, 1, 0));
diff --git a/symfony/css-selector/Node/SelectorNode.php b/symfony/css-selector/Node/SelectorNode.php
index 6e52b2fa7..2318b2bf2 100644
--- a/symfony/css-selector/Node/SelectorNode.php
+++ b/symfony/css-selector/Node/SelectorNode.php
@@ -23,10 +23,10 @@
*/
class SelectorNode extends AbstractNode
{
- private $tree;
- private $pseudoElement;
+ private NodeInterface $tree;
+ private ?string $pseudoElement;
- public function __construct(NodeInterface $tree, string $pseudoElement = null)
+ public function __construct(NodeInterface $tree, ?string $pseudoElement = null)
{
$this->tree = $tree;
$this->pseudoElement = $pseudoElement ? strtolower($pseudoElement) : null;
@@ -42,9 +42,6 @@ public function getPseudoElement(): ?string
return $this->pseudoElement;
}
- /**
- * {@inheritdoc}
- */
public function getSpecificity(): Specificity
{
return $this->tree->getSpecificity()->plus(new Specificity(0, 0, $this->pseudoElement ? 1 : 0));
diff --git a/symfony/css-selector/Node/Specificity.php b/symfony/css-selector/Node/Specificity.php
index b00f6d281..bb8e5e346 100644
--- a/symfony/css-selector/Node/Specificity.php
+++ b/symfony/css-selector/Node/Specificity.php
@@ -29,9 +29,9 @@ class Specificity
public const B_FACTOR = 10;
public const C_FACTOR = 1;
- private $a;
- private $b;
- private $c;
+ private int $a;
+ private int $b;
+ private int $c;
public function __construct(int $a, int $b, int $c)
{
diff --git a/symfony/css-selector/Parser/Handler/CommentHandler.php b/symfony/css-selector/Parser/Handler/CommentHandler.php
index 93f318844..cc01d1e6e 100644
--- a/symfony/css-selector/Parser/Handler/CommentHandler.php
+++ b/symfony/css-selector/Parser/Handler/CommentHandler.php
@@ -26,9 +26,6 @@
*/
class CommentHandler implements HandlerInterface
{
- /**
- * {@inheritdoc}
- */
public function handle(Reader $reader, TokenStream $stream): bool
{
if ('/*' !== $reader->getSubstring(2)) {
diff --git a/symfony/css-selector/Parser/Handler/HashHandler.php b/symfony/css-selector/Parser/Handler/HashHandler.php
index 7ae9b438c..b29042f56 100644
--- a/symfony/css-selector/Parser/Handler/HashHandler.php
+++ b/symfony/css-selector/Parser/Handler/HashHandler.php
@@ -29,8 +29,8 @@
*/
class HashHandler implements HandlerInterface
{
- private $patterns;
- private $escaping;
+ private TokenizerPatterns $patterns;
+ private TokenizerEscaping $escaping;
public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping)
{
@@ -38,9 +38,6 @@ public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $esca
$this->escaping = $escaping;
}
- /**
- * {@inheritdoc}
- */
public function handle(Reader $reader, TokenStream $stream): bool
{
$match = $reader->findPattern($this->patterns->getHashPattern());
diff --git a/symfony/css-selector/Parser/Handler/IdentifierHandler.php b/symfony/css-selector/Parser/Handler/IdentifierHandler.php
index 7b2a14e2c..25c0761e9 100644
--- a/symfony/css-selector/Parser/Handler/IdentifierHandler.php
+++ b/symfony/css-selector/Parser/Handler/IdentifierHandler.php
@@ -29,8 +29,8 @@
*/
class IdentifierHandler implements HandlerInterface
{
- private $patterns;
- private $escaping;
+ private TokenizerPatterns $patterns;
+ private TokenizerEscaping $escaping;
public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping)
{
@@ -38,9 +38,6 @@ public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $esca
$this->escaping = $escaping;
}
- /**
- * {@inheritdoc}
- */
public function handle(Reader $reader, TokenStream $stream): bool
{
$match = $reader->findPattern($this->patterns->getIdentifierPattern());
diff --git a/symfony/css-selector/Parser/Handler/NumberHandler.php b/symfony/css-selector/Parser/Handler/NumberHandler.php
index 8291a68d1..e3eb7afe3 100644
--- a/symfony/css-selector/Parser/Handler/NumberHandler.php
+++ b/symfony/css-selector/Parser/Handler/NumberHandler.php
@@ -28,16 +28,13 @@
*/
class NumberHandler implements HandlerInterface
{
- private $patterns;
+ private TokenizerPatterns $patterns;
public function __construct(TokenizerPatterns $patterns)
{
$this->patterns = $patterns;
}
- /**
- * {@inheritdoc}
- */
public function handle(Reader $reader, TokenStream $stream): bool
{
$match = $reader->findPattern($this->patterns->getNumberPattern());
diff --git a/symfony/css-selector/Parser/Handler/StringHandler.php b/symfony/css-selector/Parser/Handler/StringHandler.php
index 6ce83cdc9..5fd44df26 100644
--- a/symfony/css-selector/Parser/Handler/StringHandler.php
+++ b/symfony/css-selector/Parser/Handler/StringHandler.php
@@ -31,8 +31,8 @@
*/
class StringHandler implements HandlerInterface
{
- private $patterns;
- private $escaping;
+ private TokenizerPatterns $patterns;
+ private TokenizerEscaping $escaping;
public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $escaping)
{
@@ -40,9 +40,6 @@ public function __construct(TokenizerPatterns $patterns, TokenizerEscaping $esca
$this->escaping = $escaping;
}
- /**
- * {@inheritdoc}
- */
public function handle(Reader $reader, TokenStream $stream): bool
{
$quote = $reader->getSubstring(1);
diff --git a/symfony/css-selector/Parser/Handler/WhitespaceHandler.php b/symfony/css-selector/Parser/Handler/WhitespaceHandler.php
index 21345e32c..eb41c3f7f 100644
--- a/symfony/css-selector/Parser/Handler/WhitespaceHandler.php
+++ b/symfony/css-selector/Parser/Handler/WhitespaceHandler.php
@@ -27,9 +27,6 @@
*/
class WhitespaceHandler implements HandlerInterface
{
- /**
- * {@inheritdoc}
- */
public function handle(Reader $reader, TokenStream $stream): bool
{
$match = $reader->findPattern('~^[ \t\r\n\f]+~');
diff --git a/symfony/css-selector/Parser/Parser.php b/symfony/css-selector/Parser/Parser.php
index d73489edf..309c9b521 100644
--- a/symfony/css-selector/Parser/Parser.php
+++ b/symfony/css-selector/Parser/Parser.php
@@ -19,7 +19,7 @@
* CSS selector parser.
*
* This component is a port of the Python cssselect library,
- * which is copyright Ian Bicking, @see https://github.com/SimonSapin/cssselect.
+ * which is copyright Ian Bicking, @see https://github.com/scrapy/cssselect.
*
* @author Jean-François Simon
*
@@ -27,16 +27,13 @@
*/
class Parser implements ParserInterface
{
- private $tokenizer;
+ private Tokenizer $tokenizer;
- public function __construct(Tokenizer $tokenizer = null)
+ public function __construct(?Tokenizer $tokenizer = null)
{
$this->tokenizer = $tokenizer ?? new Tokenizer();
}
- /**
- * {@inheritdoc}
- */
public function parse(string $source): array
{
$reader = new Reader($source);
@@ -60,9 +57,7 @@ public static function parseSeries(array $tokens): array
}
}
- $joined = trim(implode('', array_map(function (Token $token) {
- return $token->getValue();
- }, $tokens)));
+ $joined = trim(implode('', array_map(fn (Token $token) => $token->getValue(), $tokens)));
$int = function ($string) {
if (!is_numeric($string)) {
@@ -197,7 +192,18 @@ private function parseSimpleSelector(TokenStream $stream, bool $insideNegation =
if (!$stream->getPeek()->isDelimiter(['('])) {
$result = new Node\PseudoNode($result, $identifier);
-
+ if ('Pseudo[Element[*]:scope]' === $result->__toString()) {
+ $used = \count($stream->getUsed());
+ if (!(2 === $used
+ || 3 === $used && $stream->getUsed()[0]->isWhiteSpace()
+ || $used >= 3 && $stream->getUsed()[$used - 3]->isDelimiter([','])
+ || $used >= 4
+ && $stream->getUsed()[$used - 3]->isWhiteSpace()
+ && $stream->getUsed()[$used - 4]->isDelimiter([','])
+ )) {
+ throw SyntaxErrorException::notAtTheStartOfASelector('scope');
+ }
+ }
continue;
}
@@ -242,7 +248,7 @@ private function parseSimpleSelector(TokenStream $stream, bool $insideNegation =
}
}
- if (empty($arguments)) {
+ if (!$arguments) {
throw SyntaxErrorException::unexpectedToken('at least one argument', $next);
}
diff --git a/symfony/css-selector/Parser/Reader.php b/symfony/css-selector/Parser/Reader.php
index 4b43effed..7f6ae7a60 100644
--- a/symfony/css-selector/Parser/Reader.php
+++ b/symfony/css-selector/Parser/Reader.php
@@ -23,9 +23,9 @@
*/
class Reader
{
- private $source;
- private $length;
- private $position = 0;
+ private string $source;
+ private int $length;
+ private int $position = 0;
public function __construct(string $source)
{
@@ -53,17 +53,17 @@ public function getSubstring(int $length, int $offset = 0): string
return substr($this->source, $this->position + $offset, $length);
}
- public function getOffset(string $string)
+ /**
+ * @return int|false
+ */
+ public function getOffset(string $string): int|bool
{
$position = strpos($this->source, $string, $this->position);
return false === $position ? false : $position - $this->position;
}
- /**
- * @return array|false
- */
- public function findPattern(string $pattern)
+ public function findPattern(string $pattern): array|false
{
$source = substr($this->source, $this->position);
@@ -74,12 +74,12 @@ public function findPattern(string $pattern)
return false;
}
- public function moveForward(int $length)
+ public function moveForward(int $length): void
{
$this->position += $length;
}
- public function moveToEnd()
+ public function moveToEnd(): void
{
$this->position = $this->length;
}
diff --git a/symfony/css-selector/Parser/Shortcut/ClassParser.php b/symfony/css-selector/Parser/Shortcut/ClassParser.php
index 17fa8c27e..f0ce61184 100644
--- a/symfony/css-selector/Parser/Shortcut/ClassParser.php
+++ b/symfony/css-selector/Parser/Shortcut/ClassParser.php
@@ -28,9 +28,6 @@
*/
class ClassParser implements ParserInterface
{
- /**
- * {@inheritdoc}
- */
public function parse(string $source): array
{
// Matches an optional namespace, optional element, and required class
diff --git a/symfony/css-selector/Parser/Shortcut/ElementParser.php b/symfony/css-selector/Parser/Shortcut/ElementParser.php
index 8b9a86386..a448e4a87 100644
--- a/symfony/css-selector/Parser/Shortcut/ElementParser.php
+++ b/symfony/css-selector/Parser/Shortcut/ElementParser.php
@@ -27,9 +27,6 @@
*/
class ElementParser implements ParserInterface
{
- /**
- * {@inheritdoc}
- */
public function parse(string $source): array
{
// Matches an optional namespace, required element or `*`
diff --git a/symfony/css-selector/Parser/Shortcut/EmptyStringParser.php b/symfony/css-selector/Parser/Shortcut/EmptyStringParser.php
index 222df5cd2..a63919120 100644
--- a/symfony/css-selector/Parser/Shortcut/EmptyStringParser.php
+++ b/symfony/css-selector/Parser/Shortcut/EmptyStringParser.php
@@ -31,9 +31,6 @@
*/
class EmptyStringParser implements ParserInterface
{
- /**
- * {@inheritdoc}
- */
public function parse(string $source): array
{
// Matches an empty string
diff --git a/symfony/css-selector/Parser/Shortcut/HashParser.php b/symfony/css-selector/Parser/Shortcut/HashParser.php
index fb07ee6cf..6683126a3 100644
--- a/symfony/css-selector/Parser/Shortcut/HashParser.php
+++ b/symfony/css-selector/Parser/Shortcut/HashParser.php
@@ -28,9 +28,6 @@
*/
class HashParser implements ParserInterface
{
- /**
- * {@inheritdoc}
- */
public function parse(string $source): array
{
// Matches an optional namespace, optional element, and required id
diff --git a/symfony/css-selector/Parser/Token.php b/symfony/css-selector/Parser/Token.php
index a053203c0..b50441a8e 100644
--- a/symfony/css-selector/Parser/Token.php
+++ b/symfony/css-selector/Parser/Token.php
@@ -31,9 +31,9 @@ class Token
public const TYPE_NUMBER = 'number';
public const TYPE_STRING = 'string';
- private $type;
- private $value;
- private $position;
+ private ?string $type;
+ private ?string $value;
+ private ?int $position;
public function __construct(?string $type, ?string $value, ?int $position)
{
@@ -68,7 +68,7 @@ public function isDelimiter(array $values = []): bool
return false;
}
- if (empty($values)) {
+ if (!$values) {
return true;
}
diff --git a/symfony/css-selector/Parser/TokenStream.php b/symfony/css-selector/Parser/TokenStream.php
index 2085f2dd7..8b72d5dbc 100644
--- a/symfony/css-selector/Parser/TokenStream.php
+++ b/symfony/css-selector/Parser/TokenStream.php
@@ -29,34 +29,23 @@ class TokenStream
/**
* @var Token[]
*/
- private $tokens = [];
+ private array $tokens = [];
/**
* @var Token[]
*/
- private $used = [];
+ private array $used = [];
- /**
- * @var int
- */
- private $cursor = 0;
-
- /**
- * @var Token|null
- */
- private $peeked;
-
- /**
- * @var bool
- */
- private $peeking = false;
+ private int $cursor = 0;
+ private ?Token $peeked;
+ private bool $peeking = false;
/**
* Pushes a token.
*
* @return $this
*/
- public function push(Token $token): self
+ public function push(Token $token): static
{
$this->tokens[] = $token;
@@ -68,7 +57,7 @@ public function push(Token $token): self
*
* @return $this
*/
- public function freeze(): self
+ public function freeze(): static
{
return $this;
}
@@ -156,7 +145,7 @@ public function getNextIdentifierOrStar(): ?string
/**
* Skips next whitespace if any.
*/
- public function skipWhitespace()
+ public function skipWhitespace(): void
{
$peek = $this->getPeek();
diff --git a/symfony/css-selector/Parser/Tokenizer/Tokenizer.php b/symfony/css-selector/Parser/Tokenizer/Tokenizer.php
index e0dcc5bf1..35c96a484 100644
--- a/symfony/css-selector/Parser/Tokenizer/Tokenizer.php
+++ b/symfony/css-selector/Parser/Tokenizer/Tokenizer.php
@@ -31,7 +31,7 @@ class Tokenizer
/**
* @var Handler\HandlerInterface[]
*/
- private $handlers;
+ private array $handlers;
public function __construct()
{
diff --git a/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php b/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php
index 013e827d2..8c4b9f742 100644
--- a/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php
+++ b/symfony/css-selector/Parser/Tokenizer/TokenizerEscaping.php
@@ -23,7 +23,7 @@
*/
class TokenizerEscaping
{
- private $patterns;
+ private TokenizerPatterns $patterns;
public function __construct(TokenizerPatterns $patterns)
{
diff --git a/symfony/css-selector/Parser/Tokenizer/TokenizerPatterns.php b/symfony/css-selector/Parser/Tokenizer/TokenizerPatterns.php
index 5f16ac48f..3c77cf091 100644
--- a/symfony/css-selector/Parser/Tokenizer/TokenizerPatterns.php
+++ b/symfony/css-selector/Parser/Tokenizer/TokenizerPatterns.php
@@ -23,18 +23,18 @@
*/
class TokenizerPatterns
{
- private $unicodeEscapePattern;
- private $simpleEscapePattern;
- private $newLineEscapePattern;
- private $escapePattern;
- private $stringEscapePattern;
- private $nonAsciiPattern;
- private $nmCharPattern;
- private $nmStartPattern;
- private $identifierPattern;
- private $hashPattern;
- private $numberPattern;
- private $quotedStringPattern;
+ private string $unicodeEscapePattern;
+ private string $simpleEscapePattern;
+ private string $newLineEscapePattern;
+ private string $escapePattern;
+ private string $stringEscapePattern;
+ private string $nonAsciiPattern;
+ private string $nmCharPattern;
+ private string $nmStartPattern;
+ private string $identifierPattern;
+ private string $hashPattern;
+ private string $numberPattern;
+ private string $quotedStringPattern;
public function __construct()
{
@@ -49,22 +49,22 @@ public function __construct()
$this->identifierPattern = '-?(?:'.$this->nmStartPattern.')(?:'.$this->nmCharPattern.')*';
$this->hashPattern = '#((?:'.$this->nmCharPattern.')+)';
$this->numberPattern = '[+-]?(?:[0-9]*\.[0-9]+|[0-9]+)';
- $this->quotedStringPattern = '([^\n\r\f%s]|'.$this->stringEscapePattern.')*';
+ $this->quotedStringPattern = '([^\n\r\f\\\\%s]|'.$this->stringEscapePattern.')*';
}
public function getNewLineEscapePattern(): string
{
- return '~^'.$this->newLineEscapePattern.'~';
+ return '~'.$this->newLineEscapePattern.'~';
}
public function getSimpleEscapePattern(): string
{
- return '~^'.$this->simpleEscapePattern.'~';
+ return '~'.$this->simpleEscapePattern.'~';
}
public function getUnicodeEscapePattern(): string
{
- return '~^'.$this->unicodeEscapePattern.'~i';
+ return '~'.$this->unicodeEscapePattern.'~i';
}
public function getIdentifierPattern(): string
diff --git a/symfony/css-selector/XPath/Extension/AbstractExtension.php b/symfony/css-selector/XPath/Extension/AbstractExtension.php
index 44e0035a7..495f88291 100644
--- a/symfony/css-selector/XPath/Extension/AbstractExtension.php
+++ b/symfony/css-selector/XPath/Extension/AbstractExtension.php
@@ -23,41 +23,26 @@
*/
abstract class AbstractExtension implements ExtensionInterface
{
- /**
- * {@inheritdoc}
- */
public function getNodeTranslators(): array
{
return [];
}
- /**
- * {@inheritdoc}
- */
public function getCombinationTranslators(): array
{
return [];
}
- /**
- * {@inheritdoc}
- */
public function getFunctionTranslators(): array
{
return [];
}
- /**
- * {@inheritdoc}
- */
public function getPseudoClassTranslators(): array
{
return [];
}
- /**
- * {@inheritdoc}
- */
public function getAttributeMatchingTranslators(): array
{
return [];
diff --git a/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.php b/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.php
index a9879f1be..3c785e97a 100644
--- a/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.php
+++ b/symfony/css-selector/XPath/Extension/AttributeMatchingExtension.php
@@ -26,20 +26,17 @@
*/
class AttributeMatchingExtension extends AbstractExtension
{
- /**
- * {@inheritdoc}
- */
public function getAttributeMatchingTranslators(): array
{
return [
- 'exists' => [$this, 'translateExists'],
- '=' => [$this, 'translateEquals'],
- '~=' => [$this, 'translateIncludes'],
- '|=' => [$this, 'translateDashMatch'],
- '^=' => [$this, 'translatePrefixMatch'],
- '$=' => [$this, 'translateSuffixMatch'],
- '*=' => [$this, 'translateSubstringMatch'],
- '!=' => [$this, 'translateDifferent'],
+ 'exists' => $this->translateExists(...),
+ '=' => $this->translateEquals(...),
+ '~=' => $this->translateIncludes(...),
+ '|=' => $this->translateDashMatch(...),
+ '^=' => $this->translatePrefixMatch(...),
+ '$=' => $this->translateSuffixMatch(...),
+ '*=' => $this->translateSubstringMatch(...),
+ '!=' => $this->translateDifferent(...),
];
}
@@ -109,9 +106,6 @@ public function translateDifferent(XPathExpr $xpath, string $attribute, ?string
));
}
- /**
- * {@inheritdoc}
- */
public function getName(): string
{
return 'attribute-matching';
diff --git a/symfony/css-selector/XPath/Extension/CombinationExtension.php b/symfony/css-selector/XPath/Extension/CombinationExtension.php
index aee976e94..f78d48883 100644
--- a/symfony/css-selector/XPath/Extension/CombinationExtension.php
+++ b/symfony/css-selector/XPath/Extension/CombinationExtension.php
@@ -25,16 +25,13 @@
*/
class CombinationExtension extends AbstractExtension
{
- /**
- * {@inheritdoc}
- */
public function getCombinationTranslators(): array
{
return [
- ' ' => [$this, 'translateDescendant'],
- '>' => [$this, 'translateChild'],
- '+' => [$this, 'translateDirectAdjacent'],
- '~' => [$this, 'translateIndirectAdjacent'],
+ ' ' => $this->translateDescendant(...),
+ '>' => $this->translateChild(...),
+ '+' => $this->translateDirectAdjacent(...),
+ '~' => $this->translateIndirectAdjacent(...),
];
}
@@ -61,9 +58,6 @@ public function translateIndirectAdjacent(XPathExpr $xpath, XPathExpr $combinedX
return $xpath->join('/following-sibling::', $combinedXpath);
}
- /**
- * {@inheritdoc}
- */
public function getName(): string
{
return 'combination';
diff --git a/symfony/css-selector/XPath/Extension/FunctionExtension.php b/symfony/css-selector/XPath/Extension/FunctionExtension.php
index d3f7222a4..4b9d7bc26 100644
--- a/symfony/css-selector/XPath/Extension/FunctionExtension.php
+++ b/symfony/css-selector/XPath/Extension/FunctionExtension.php
@@ -30,18 +30,15 @@
*/
class FunctionExtension extends AbstractExtension
{
- /**
- * {@inheritdoc}
- */
public function getFunctionTranslators(): array
{
return [
- 'nth-child' => [$this, 'translateNthChild'],
- 'nth-last-child' => [$this, 'translateNthLastChild'],
- 'nth-of-type' => [$this, 'translateNthOfType'],
- 'nth-last-of-type' => [$this, 'translateNthLastOfType'],
- 'contains' => [$this, 'translateContains'],
- 'lang' => [$this, 'translateLang'],
+ 'nth-child' => $this->translateNthChild(...),
+ 'nth-last-child' => $this->translateNthLastChild(...),
+ 'nth-of-type' => $this->translateNthOfType(...),
+ 'nth-last-of-type' => $this->translateNthLastOfType(...),
+ 'contains' => $this->translateContains(...),
+ 'lang' => $this->translateLang(...),
];
}
@@ -161,9 +158,6 @@ public function translateLang(XPathExpr $xpath, FunctionNode $function): XPathEx
));
}
- /**
- * {@inheritdoc}
- */
public function getName(): string
{
return 'function';
diff --git a/symfony/css-selector/XPath/Extension/HtmlExtension.php b/symfony/css-selector/XPath/Extension/HtmlExtension.php
index 6edc08581..4653add5d 100644
--- a/symfony/css-selector/XPath/Extension/HtmlExtension.php
+++ b/symfony/css-selector/XPath/Extension/HtmlExtension.php
@@ -36,30 +36,24 @@ public function __construct(Translator $translator)
->setFlag(NodeExtension::ATTRIBUTE_NAME_IN_LOWER_CASE, true);
}
- /**
- * {@inheritdoc}
- */
public function getPseudoClassTranslators(): array
{
return [
- 'checked' => [$this, 'translateChecked'],
- 'link' => [$this, 'translateLink'],
- 'disabled' => [$this, 'translateDisabled'],
- 'enabled' => [$this, 'translateEnabled'],
- 'selected' => [$this, 'translateSelected'],
- 'invalid' => [$this, 'translateInvalid'],
- 'hover' => [$this, 'translateHover'],
- 'visited' => [$this, 'translateVisited'],
+ 'checked' => $this->translateChecked(...),
+ 'link' => $this->translateLink(...),
+ 'disabled' => $this->translateDisabled(...),
+ 'enabled' => $this->translateEnabled(...),
+ 'selected' => $this->translateSelected(...),
+ 'invalid' => $this->translateInvalid(...),
+ 'hover' => $this->translateHover(...),
+ 'visited' => $this->translateVisited(...),
];
}
- /**
- * {@inheritdoc}
- */
public function getFunctionTranslators(): array
{
return [
- 'lang' => [$this, 'translateLang'],
+ 'lang' => $this->translateLang(...),
];
}
@@ -177,9 +171,6 @@ public function translateVisited(XPathExpr $xpath): XPathExpr
return $xpath->addCondition('0');
}
- /**
- * {@inheritdoc}
- */
public function getName(): string
{
return 'html';
diff --git a/symfony/css-selector/XPath/Extension/NodeExtension.php b/symfony/css-selector/XPath/Extension/NodeExtension.php
index aa6f3f704..49e894ade 100644
--- a/symfony/css-selector/XPath/Extension/NodeExtension.php
+++ b/symfony/css-selector/XPath/Extension/NodeExtension.php
@@ -31,7 +31,7 @@ class NodeExtension extends AbstractExtension
public const ATTRIBUTE_NAME_IN_LOWER_CASE = 2;
public const ATTRIBUTE_VALUE_IN_LOWER_CASE = 4;
- private $flags;
+ private int $flags;
public function __construct(int $flags = 0)
{
@@ -41,7 +41,7 @@ public function __construct(int $flags = 0)
/**
* @return $this
*/
- public function setFlag(int $flag, bool $on): self
+ public function setFlag(int $flag, bool $on): static
{
if ($on && !$this->hasFlag($flag)) {
$this->flags += $flag;
@@ -59,21 +59,18 @@ public function hasFlag(int $flag): bool
return (bool) ($this->flags & $flag);
}
- /**
- * {@inheritdoc}
- */
public function getNodeTranslators(): array
{
return [
- 'Selector' => [$this, 'translateSelector'],
- 'CombinedSelector' => [$this, 'translateCombinedSelector'],
- 'Negation' => [$this, 'translateNegation'],
- 'Function' => [$this, 'translateFunction'],
- 'Pseudo' => [$this, 'translatePseudo'],
- 'Attribute' => [$this, 'translateAttribute'],
- 'Class' => [$this, 'translateClass'],
- 'Hash' => [$this, 'translateHash'],
- 'Element' => [$this, 'translateElement'],
+ 'Selector' => $this->translateSelector(...),
+ 'CombinedSelector' => $this->translateCombinedSelector(...),
+ 'Negation' => $this->translateNegation(...),
+ 'Function' => $this->translateFunction(...),
+ 'Pseudo' => $this->translatePseudo(...),
+ 'Attribute' => $this->translateAttribute(...),
+ 'Class' => $this->translateClass(...),
+ 'Hash' => $this->translateHash(...),
+ 'Element' => $this->translateElement(...),
];
}
@@ -182,9 +179,6 @@ public function translateElement(Node\ElementNode $node): XPathExpr
return $xpath;
}
- /**
- * {@inheritdoc}
- */
public function getName(): string
{
return 'node';
diff --git a/symfony/css-selector/XPath/Extension/PseudoClassExtension.php b/symfony/css-selector/XPath/Extension/PseudoClassExtension.php
index a50b0486a..aada83291 100644
--- a/symfony/css-selector/XPath/Extension/PseudoClassExtension.php
+++ b/symfony/css-selector/XPath/Extension/PseudoClassExtension.php
@@ -26,20 +26,18 @@
*/
class PseudoClassExtension extends AbstractExtension
{
- /**
- * {@inheritdoc}
- */
public function getPseudoClassTranslators(): array
{
return [
- 'root' => [$this, 'translateRoot'],
- 'first-child' => [$this, 'translateFirstChild'],
- 'last-child' => [$this, 'translateLastChild'],
- 'first-of-type' => [$this, 'translateFirstOfType'],
- 'last-of-type' => [$this, 'translateLastOfType'],
- 'only-child' => [$this, 'translateOnlyChild'],
- 'only-of-type' => [$this, 'translateOnlyOfType'],
- 'empty' => [$this, 'translateEmpty'],
+ 'root' => $this->translateRoot(...),
+ 'scope' => $this->translateScopePseudo(...),
+ 'first-child' => $this->translateFirstChild(...),
+ 'last-child' => $this->translateLastChild(...),
+ 'first-of-type' => $this->translateFirstOfType(...),
+ 'last-of-type' => $this->translateLastOfType(...),
+ 'only-child' => $this->translateOnlyChild(...),
+ 'only-of-type' => $this->translateOnlyOfType(...),
+ 'empty' => $this->translateEmpty(...),
];
}
@@ -48,6 +46,11 @@ public function translateRoot(XPathExpr $xpath): XPathExpr
return $xpath->addCondition('not(parent::*)');
}
+ public function translateScopePseudo(XPathExpr $xpath): XPathExpr
+ {
+ return $xpath->addCondition('1');
+ }
+
public function translateFirstChild(XPathExpr $xpath): XPathExpr
{
return $xpath
@@ -112,9 +115,6 @@ public function translateEmpty(XPathExpr $xpath): XPathExpr
return $xpath->addCondition('not(*) and not(string-length())');
}
- /**
- * {@inheritdoc}
- */
public function getName(): string
{
return 'pseudo-class';
diff --git a/symfony/css-selector/XPath/Translator.php b/symfony/css-selector/XPath/Translator.php
index 8ce473036..9e66ce7dd 100644
--- a/symfony/css-selector/XPath/Translator.php
+++ b/symfony/css-selector/XPath/Translator.php
@@ -30,25 +30,25 @@
*/
class Translator implements TranslatorInterface
{
- private $mainParser;
+ private ParserInterface $mainParser;
/**
* @var ParserInterface[]
*/
- private $shortcutParsers = [];
+ private array $shortcutParsers = [];
/**
* @var Extension\ExtensionInterface[]
*/
- private $extensions = [];
+ private array $extensions = [];
- private $nodeTranslators = [];
- private $combinationTranslators = [];
- private $functionTranslators = [];
- private $pseudoClassTranslators = [];
- private $attributeMatchingTranslators = [];
+ private array $nodeTranslators = [];
+ private array $combinationTranslators = [];
+ private array $functionTranslators = [];
+ private array $pseudoClassTranslators = [];
+ private array $attributeMatchingTranslators = [];
- public function __construct(ParserInterface $parser = null)
+ public function __construct(?ParserInterface $parser = null)
{
$this->mainParser = $parser ?? new Parser();
@@ -87,9 +87,6 @@ public static function getXpathLiteral(string $element): string
return sprintf('concat(%s)', implode(', ', $parts));
}
- /**
- * {@inheritdoc}
- */
public function cssToXPath(string $cssExpr, string $prefix = 'descendant-or-self::'): string
{
$selectors = $this->parseSelectors($cssExpr);
@@ -106,9 +103,6 @@ public function cssToXPath(string $cssExpr, string $prefix = 'descendant-or-self
return implode(' | ', $selectors);
}
- /**
- * {@inheritdoc}
- */
public function selectorToXPath(SelectorNode $selector, string $prefix = 'descendant-or-self::'): string
{
return ($prefix ?: '').$this->nodeToXPath($selector);
@@ -117,7 +111,7 @@ public function selectorToXPath(SelectorNode $selector, string $prefix = 'descen
/**
* @return $this
*/
- public function registerExtension(Extension\ExtensionInterface $extension): self
+ public function registerExtension(Extension\ExtensionInterface $extension): static
{
$this->extensions[$extension->getName()] = $extension;
@@ -145,7 +139,7 @@ public function getExtension(string $name): Extension\ExtensionInterface
/**
* @return $this
*/
- public function registerParserShortcut(ParserInterface $shortcut): self
+ public function registerParserShortcut(ParserInterface $shortcut): static
{
$this->shortcutParsers[] = $shortcut;
@@ -220,7 +214,7 @@ private function parseSelectors(string $css): array
foreach ($this->shortcutParsers as $shortcut) {
$tokens = $shortcut->parse($css);
- if (!empty($tokens)) {
+ if ($tokens) {
return $tokens;
}
}
diff --git a/symfony/css-selector/XPath/XPathExpr.php b/symfony/css-selector/XPath/XPathExpr.php
index e45ce7d8c..a76e30bec 100644
--- a/symfony/css-selector/XPath/XPathExpr.php
+++ b/symfony/css-selector/XPath/XPathExpr.php
@@ -23,9 +23,9 @@
*/
class XPathExpr
{
- private $path;
- private $element;
- private $condition;
+ private string $path;
+ private string $element;
+ private string $condition;
public function __construct(string $path = '', string $element = '*', string $condition = '', bool $starPrefix = false)
{
@@ -46,7 +46,7 @@ public function getElement(): string
/**
* @return $this
*/
- public function addCondition(string $condition): self
+ public function addCondition(string $condition): static
{
$this->condition = $this->condition ? sprintf('(%s) and (%s)', $this->condition, $condition) : $condition;
@@ -61,7 +61,7 @@ public function getCondition(): string
/**
* @return $this
*/
- public function addNameTest(): self
+ public function addNameTest(): static
{
if ('*' !== $this->element) {
$this->addCondition('name() = '.Translator::getXpathLiteral($this->element));
@@ -74,7 +74,7 @@ public function addNameTest(): self
/**
* @return $this
*/
- public function addStarPrefix(): self
+ public function addStarPrefix(): static
{
$this->path .= '*/';
@@ -86,7 +86,7 @@ public function addStarPrefix(): self
*
* @return $this
*/
- public function join(string $combiner, self $expr): self
+ public function join(string $combiner, self $expr): static
{
$path = $this->__toString().$combiner;
diff --git a/symfony/dom-crawler/AbstractUriElement.php b/symfony/dom-crawler/AbstractUriElement.php
index 8ff0b992a..e67716f64 100644
--- a/symfony/dom-crawler/AbstractUriElement.php
+++ b/symfony/dom-crawler/AbstractUriElement.php
@@ -40,7 +40,7 @@ abstract class AbstractUriElement
*
* @throws \InvalidArgumentException if the node is not a link
*/
- public function __construct(\DOMElement $node, string $currentUri = null, ?string $method = 'GET')
+ public function __construct(\DOMElement $node, ?string $currentUri = null, ?string $method = 'GET')
{
$this->setNode($node);
$this->method = $method ? strtoupper($method) : null;
@@ -55,49 +55,39 @@ public function __construct(\DOMElement $node, string $currentUri = null, ?strin
/**
* Gets the node associated with this link.
- *
- * @return \DOMElement
*/
- public function getNode()
+ public function getNode(): \DOMElement
{
return $this->node;
}
/**
* Gets the method associated with this link.
- *
- * @return string
*/
- public function getMethod()
+ public function getMethod(): string
{
return $this->method ?? 'GET';
}
/**
* Gets the URI associated with this link.
- *
- * @return string
*/
- public function getUri()
+ public function getUri(): string
{
return UriResolver::resolve($this->getRawUri(), $this->currentUri);
}
/**
* Returns raw URI data.
- *
- * @return string
*/
- abstract protected function getRawUri();
+ abstract protected function getRawUri(): string;
/**
* Returns the canonicalized URI path (see RFC 3986, section 5.2.4).
*
* @param string $path URI path
- *
- * @return string
*/
- protected function canonicalizePath(string $path)
+ protected function canonicalizePath(string $path): string
{
if ('' === $path || '/' === $path) {
return $path;
@@ -125,6 +115,8 @@ protected function canonicalizePath(string $path)
*
* @param \DOMElement $node A \DOMElement instance
*
+ * @return void
+ *
* @throws \LogicException If given node is not an anchor
*/
abstract protected function setNode(\DOMElement $node);
diff --git a/symfony/dom-crawler/Crawler.php b/symfony/dom-crawler/Crawler.php
index a32bc1dd8..8c5e26930 100644
--- a/symfony/dom-crawler/Crawler.php
+++ b/symfony/dom-crawler/Crawler.php
@@ -30,62 +30,44 @@ class Crawler implements \Countable, \IteratorAggregate
/**
* The default namespace prefix to be used with XPath and CSS expressions.
- *
- * @var string
*/
- private $defaultNamespacePrefix = 'default';
+ private string $defaultNamespacePrefix = 'default';
/**
* A map of manually registered namespaces.
*
* @var array
*/
- private $namespaces = [];
+ private array $namespaces = [];
/**
* A map of cached namespaces.
- *
- * @var \ArrayObject
- */
- private $cachedNamespaces;
-
- /**
- * The base href value.
- *
- * @var string|null
*/
- private $baseHref;
+ private \ArrayObject $cachedNamespaces;
- /**
- * @var \DOMDocument|null
- */
- private $document;
+ private ?string $baseHref;
+ private ?\DOMDocument $document = null;
/**
* @var list<\DOMNode>
*/
- private $nodes = [];
+ private array $nodes = [];
/**
* Whether the Crawler contains HTML or XML content (used when converting CSS to XPath).
- *
- * @var bool
*/
- private $isHtml = true;
+ private bool $isHtml = true;
- /**
- * @var HTML5|null
- */
- private $html5Parser;
+ private ?HTML5 $html5Parser = null;
/**
* @param \DOMNodeList|\DOMNode|\DOMNode[]|string|null $node A Node to use as the base for the crawling
*/
- public function __construct($node = null, string $uri = null, string $baseHref = null)
+ public function __construct(\DOMNodeList|\DOMNode|array|string|null $node = null, ?string $uri = null, ?string $baseHref = null, bool $useHtml5Parser = true)
{
$this->uri = $uri;
$this->baseHref = $baseHref ?: $uri;
- $this->html5Parser = class_exists(HTML5::class) ? new HTML5(['disable_html_ns' => true]) : null;
+ $this->html5Parser = $useHtml5Parser ? new HTML5(['disable_html_ns' => true]) : null;
$this->cachedNamespaces = new \ArrayObject();
$this->add($node);
@@ -93,26 +75,24 @@ public function __construct($node = null, string $uri = null, string $baseHref =
/**
* Returns the current URI.
- *
- * @return string|null
*/
- public function getUri()
+ public function getUri(): ?string
{
return $this->uri;
}
/**
* Returns base href.
- *
- * @return string|null
*/
- public function getBaseHref()
+ public function getBaseHref(): ?string
{
return $this->baseHref;
}
/**
* Removes all the nodes.
+ *
+ * @return void
*/
public function clear()
{
@@ -129,9 +109,11 @@ public function clear()
*
* @param \DOMNodeList|\DOMNode|\DOMNode[]|string|null $node A node
*
+ * @return void
+ *
* @throws \InvalidArgumentException when node is not the expected type
*/
- public function add($node)
+ public function add(\DOMNodeList|\DOMNode|array|string|null $node)
{
if ($node instanceof \DOMNodeList) {
$this->addNodeList($node);
@@ -152,8 +134,10 @@ public function add($node)
* If the charset is not set via the content type, it is assumed to be UTF-8,
* or ISO-8859-1 as a fallback, which is the default charset defined by the
* HTTP 1.1 specification.
+ *
+ * @return void
*/
- public function addContent(string $content, string $type = null)
+ public function addContent(string $content, ?string $type = null)
{
if (empty($type)) {
$type = str_starts_with($content, 'validateOnParse = true;
@@ -247,9 +232,6 @@ public function addXmlContent(string $content, string $charset = 'UTF-8', int $o
}
libxml_use_internal_errors($internalErrors);
- if (\LIBXML_VERSION < 20900) {
- libxml_disable_entity_loader($disableEntities);
- }
$this->addDocument($dom);
@@ -260,6 +242,8 @@ public function addXmlContent(string $content, string $charset = 'UTF-8', int $o
* Adds a \DOMDocument to the list of nodes.
*
* @param \DOMDocument $dom A \DOMDocument instance
+ *
+ * @return void
*/
public function addDocument(\DOMDocument $dom)
{
@@ -272,6 +256,8 @@ public function addDocument(\DOMDocument $dom)
* Adds a \DOMNodeList to the list of nodes.
*
* @param \DOMNodeList $nodes A \DOMNodeList instance
+ *
+ * @return void
*/
public function addNodeList(\DOMNodeList $nodes)
{
@@ -286,6 +272,8 @@ public function addNodeList(\DOMNodeList $nodes)
* Adds an array of \DOMNode instances to the list of nodes.
*
* @param \DOMNode[] $nodes An array of \DOMNode instances
+ *
+ * @return void
*/
public function addNodes(array $nodes)
{
@@ -298,6 +286,8 @@ public function addNodes(array $nodes)
* Adds a \DOMNode instance to the list of nodes.
*
* @param \DOMNode $node A \DOMNode instance
+ *
+ * @return void
*/
public function addNode(\DOMNode $node)
{
@@ -309,9 +299,7 @@ public function addNode(\DOMNode $node)
throw new \InvalidArgumentException('Attaching DOM nodes from multiple documents in the same crawler is forbidden.');
}
- if (null === $this->document) {
- $this->document = $node->ownerDocument;
- }
+ $this->document ??= $node->ownerDocument;
// Don't add duplicate nodes in the Crawler
if (\in_array($node, $this->nodes, true)) {
@@ -323,10 +311,8 @@ public function addNode(\DOMNode $node)
/**
* Returns a node given its position in the node list.
- *
- * @return static
*/
- public function eq(int $position)
+ public function eq(int $position): static
{
if (isset($this->nodes[$position])) {
return $this->createSubCrawler($this->nodes[$position]);
@@ -351,7 +337,7 @@ public function eq(int $position)
*
* @return array An array of values returned by the anonymous function
*/
- public function each(\Closure $closure)
+ public function each(\Closure $closure): array
{
$data = [];
foreach ($this->nodes as $i => $node) {
@@ -363,10 +349,8 @@ public function each(\Closure $closure)
/**
* Slices the list of nodes by $offset and $length.
- *
- * @return static
*/
- public function slice(int $offset = 0, int $length = null)
+ public function slice(int $offset = 0, ?int $length = null): static
{
return $this->createSubCrawler(\array_slice($this->nodes, $offset, $length));
}
@@ -377,10 +361,8 @@ public function slice(int $offset = 0, int $length = null)
* To remove a node from the list, the anonymous function must return false.
*
* @param \Closure $closure An anonymous function
- *
- * @return static
*/
- public function reduce(\Closure $closure)
+ public function reduce(\Closure $closure): static
{
$nodes = [];
foreach ($this->nodes as $i => $node) {
@@ -394,20 +376,16 @@ public function reduce(\Closure $closure)
/**
* Returns the first node of the current selection.
- *
- * @return static
*/
- public function first()
+ public function first(): static
{
return $this->eq(0);
}
/**
* Returns the last node of the current selection.
- *
- * @return static
*/
- public function last()
+ public function last(): static
{
return $this->eq(\count($this->nodes) - 1);
}
@@ -415,11 +393,9 @@ public function last()
/**
* Returns the siblings nodes of the current selection.
*
- * @return static
- *
* @throws \InvalidArgumentException When current node is empty
*/
- public function siblings()
+ public function siblings(): static
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
@@ -470,11 +446,9 @@ public function closest(string $selector): ?self
/**
* Returns the next siblings nodes of the current selection.
*
- * @return static
- *
* @throws \InvalidArgumentException When current node is empty
*/
- public function nextAll()
+ public function nextAll(): static
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
@@ -486,11 +460,9 @@ public function nextAll()
/**
* Returns the previous sibling nodes of the current selection.
*
- * @return static
- *
* @throws \InvalidArgumentException
*/
- public function previousAll()
+ public function previousAll(): static
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
@@ -499,28 +471,12 @@ public function previousAll()
return $this->createSubCrawler($this->sibling($this->getNode(0), 'previousSibling'));
}
- /**
- * Returns the parent nodes of the current selection.
- *
- * @return static
- *
- * @throws \InvalidArgumentException When current node is empty
- */
- public function parents()
- {
- trigger_deprecation('symfony/dom-crawler', '5.3', 'The %s() method is deprecated, use ancestors() instead.', __METHOD__);
-
- return $this->ancestors();
- }
-
/**
* Returns the ancestors of the current selection.
*
- * @return static
- *
* @throws \InvalidArgumentException When the current node is empty
*/
- public function ancestors()
+ public function ancestors(): static
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
@@ -541,12 +497,10 @@ public function ancestors()
/**
* Returns the children nodes of the current selection.
*
- * @return static
- *
* @throws \InvalidArgumentException When current node is empty
* @throws \RuntimeException If the CssSelector Component is not available and $selector is provided
*/
- public function children(string $selector = null)
+ public function children(?string $selector = null): static
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
@@ -567,29 +521,32 @@ public function children(string $selector = null)
/**
* Returns the attribute value of the first node of the list.
*
- * @return string|null
+ * @param string|null $default When not null: the value to return when the node or attribute is empty
*
* @throws \InvalidArgumentException When current node is empty
*/
- public function attr(string $attribute)
+ public function attr(string $attribute/* , string $default = null */): ?string
{
+ $default = \func_num_args() > 1 ? func_get_arg(1) : null;
if (!$this->nodes) {
+ if (null !== $default) {
+ return $default;
+ }
+
throw new \InvalidArgumentException('The current node list is empty.');
}
$node = $this->getNode(0);
- return $node->hasAttribute($attribute) ? $node->getAttribute($attribute) : null;
+ return $node->hasAttribute($attribute) ? $node->getAttribute($attribute) : $default;
}
/**
* Returns the node name of the first node of the list.
*
- * @return string
- *
* @throws \InvalidArgumentException When current node is empty
*/
- public function nodeName()
+ public function nodeName(): string
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
@@ -606,11 +563,9 @@ public function nodeName()
* @param string|null $default When not null: the value to return when the current node is empty
* @param bool $normalizeWhitespace Whether whitespaces should be trimmed and normalized to single spaces
*
- * @return string
- *
* @throws \InvalidArgumentException When current node is empty
*/
- public function text(string $default = null, bool $normalizeWhitespace = true)
+ public function text(?string $default = null, bool $normalizeWhitespace = true): string
{
if (!$this->nodes) {
if (null !== $default) {
@@ -623,7 +578,7 @@ public function text(string $default = null, bool $normalizeWhitespace = true)
$text = $this->getNode(0)->nodeValue;
if ($normalizeWhitespace) {
- return trim(preg_replace('/(?:\s{2,}+|[^\S ])/', ' ', $text));
+ return $this->normalizeWhitespace($text);
}
return $text;
@@ -631,10 +586,26 @@ public function text(string $default = null, bool $normalizeWhitespace = true)
/**
* Returns only the inner text that is the direct descendent of the current node, excluding any child nodes.
+ *
+ * @param bool $normalizeWhitespace Whether whitespaces should be trimmed and normalized to single spaces
*/
- public function innerText(): string
+ public function innerText(/* bool $normalizeWhitespace = true */): string
{
- return $this->filterXPath('.//text()')->text();
+ $normalizeWhitespace = 1 <= \func_num_args() ? func_get_arg(0) : true;
+
+ foreach ($this->getNode(0)->childNodes as $childNode) {
+ if (\XML_TEXT_NODE !== $childNode->nodeType && \XML_CDATA_SECTION_NODE !== $childNode->nodeType) {
+ continue;
+ }
+ if (!$normalizeWhitespace) {
+ return $childNode->nodeValue;
+ }
+ if ('' !== trim($childNode->nodeValue)) {
+ return $this->normalizeWhitespace($childNode->nodeValue);
+ }
+ }
+
+ return '';
}
/**
@@ -642,11 +613,9 @@ public function innerText(): string
*
* @param string|null $default When not null: the value to return when the current node is empty
*
- * @return string
- *
* @throws \InvalidArgumentException When current node is empty
*/
- public function html(string $default = null)
+ public function html(?string $default = null): string
{
if (!$this->nodes) {
if (null !== $default) {
@@ -659,7 +628,7 @@ public function html(string $default = null)
$node = $this->getNode(0);
$owner = $node->ownerDocument;
- if (null !== $this->html5Parser && '' === $owner->saveXML($owner->childNodes[0])) {
+ if ($this->html5Parser && '' === $owner->saveXML($owner->childNodes[0])) {
$owner = $this->html5Parser;
}
@@ -680,7 +649,7 @@ public function outerHtml(): string
$node = $this->getNode(0);
$owner = $node->ownerDocument;
- if (null !== $this->html5Parser && '' === $owner->saveXML($owner->childNodes[0])) {
+ if ($this->html5Parser && '' === $owner->saveXML($owner->childNodes[0])) {
$owner = $this->html5Parser;
}
@@ -692,10 +661,8 @@ public function outerHtml(): string
*
* Since an XPath expression might evaluate to either a simple type or a \DOMNodeList,
* this method will return either an array of simple types or a new Crawler instance.
- *
- * @return array|Crawler
*/
- public function evaluate(string $xpath)
+ public function evaluate(string $xpath): array|self
{
if (null === $this->document) {
throw new \LogicException('Cannot evaluate the expression on an uninitialized crawler.');
@@ -723,10 +690,8 @@ public function evaluate(string $xpath)
* Example:
*
* $crawler->filter('h1 a')->extract(['_text', 'href']);
- *
- * @return array
*/
- public function extract(array $attributes)
+ public function extract(array $attributes): array
{
$count = \count($attributes);
@@ -756,10 +721,8 @@ public function extract(array $attributes)
* is considered as a fake parent of the elements inside it.
* This means that a child selector "div" or "./div" will match only
* the div elements of the current crawler, not their children.
- *
- * @return static
*/
- public function filterXPath(string $xpath)
+ public function filterXPath(string $xpath): static
{
$xpath = $this->relativize($xpath);
@@ -776,11 +739,9 @@ public function filterXPath(string $xpath)
*
* This method only works if you have installed the CssSelector Symfony Component.
*
- * @return static
- *
- * @throws \RuntimeException if the CssSelector Component is not available
+ * @throws \LogicException if the CssSelector Component is not available
*/
- public function filter(string $selector)
+ public function filter(string $selector): static
{
$converter = $this->createCssSelectorConverter();
@@ -790,10 +751,8 @@ public function filter(string $selector)
/**
* Selects links by name or alt value for clickable images.
- *
- * @return static
*/
- public function selectLink(string $value)
+ public function selectLink(string $value): static
{
return $this->filterRelativeXPath(
sprintf('descendant-or-self::a[contains(concat(\' \', normalize-space(string(.)), \' \'), %1$s) or ./img[contains(concat(\' \', normalize-space(string(@alt)), \' \'), %1$s)]]', static::xpathLiteral(' '.$value.' '))
@@ -802,10 +761,8 @@ public function selectLink(string $value)
/**
* Selects images by alt value.
- *
- * @return static
*/
- public function selectImage(string $value)
+ public function selectImage(string $value): static
{
$xpath = sprintf('descendant-or-self::img[contains(normalize-space(string(@alt)), %s)]', static::xpathLiteral($value));
@@ -814,10 +771,8 @@ public function selectImage(string $value)
/**
* Selects a button by name or alt value for images.
- *
- * @return static
*/
- public function selectButton(string $value)
+ public function selectButton(string $value): static
{
return $this->filterRelativeXPath(
sprintf('descendant-or-self::input[((contains(%1$s, "submit") or contains(%1$s, "button")) and contains(concat(\' \', normalize-space(string(@value)), \' \'), %2$s)) or (contains(%1$s, "image") and contains(concat(\' \', normalize-space(string(@alt)), \' \'), %2$s)) or @id=%3$s or @name=%3$s] | descendant-or-self::button[contains(concat(\' \', normalize-space(string(.)), \' \'), %2$s) or @id=%3$s or @name=%3$s]', 'translate(@type, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")', static::xpathLiteral(' '.$value.' '), static::xpathLiteral($value))
@@ -827,11 +782,9 @@ public function selectButton(string $value)
/**
* Returns a Link object for the first node in the list.
*
- * @return Link
- *
* @throws \InvalidArgumentException If the current node list is empty or the selected node is not instance of DOMElement
*/
- public function link(string $method = 'get')
+ public function link(string $method = 'get'): Link
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
@@ -853,7 +806,7 @@ public function link(string $method = 'get')
*
* @throws \InvalidArgumentException If the current node list contains non-DOMElement instances
*/
- public function links()
+ public function links(): array
{
$links = [];
foreach ($this->nodes as $node) {
@@ -870,11 +823,9 @@ public function links()
/**
* Returns an Image object for the first node in the list.
*
- * @return Image
- *
* @throws \InvalidArgumentException If the current node list is empty
*/
- public function image()
+ public function image(): Image
{
if (!\count($this)) {
throw new \InvalidArgumentException('The current node list is empty.');
@@ -894,7 +845,7 @@ public function image()
*
* @return Image[]
*/
- public function images()
+ public function images(): array
{
$images = [];
foreach ($this as $node) {
@@ -911,11 +862,9 @@ public function images()
/**
* Returns a Form object for the first node in the list.
*
- * @return Form
- *
* @throws \InvalidArgumentException If the current node list is empty or the selected node is not instance of DOMElement
*/
- public function form(array $values = null, string $method = null)
+ public function form(?array $values = null, ?string $method = null): Form
{
if (!$this->nodes) {
throw new \InvalidArgumentException('The current node list is empty.');
@@ -938,12 +887,17 @@ public function form(array $values = null, string $method = null)
/**
* Overloads a default namespace prefix to be used with XPath and CSS expressions.
+ *
+ * @return void
*/
public function setDefaultNamespacePrefix(string $prefix)
{
$this->defaultNamespacePrefix = $prefix;
}
+ /**
+ * @return void
+ */
public function registerNamespace(string $prefix, string $namespace)
{
$this->namespaces[$prefix] = $namespace;
@@ -964,10 +918,8 @@ public function registerNamespace(string $prefix, string $namespace)
*
* echo Crawler::xpathLiteral('a\'b"c');
* //prints concat('a', "'", 'b"c')
- *
- * @return string
*/
- public static function xpathLiteral(string $s)
+ public static function xpathLiteral(string $s): string
{
if (!str_contains($s, "'")) {
return sprintf("'%s'", $s);
@@ -997,10 +949,8 @@ public static function xpathLiteral(string $s)
* Filters the list of nodes with an XPath expression.
*
* The XPath expression should already be processed to apply it in the context of each node.
- *
- * @return static
*/
- private function filterRelativeXPath(string $xpath): object
+ private function filterRelativeXPath(string $xpath): static
{
$crawler = $this->createSubCrawler(null);
if (null === $this->document) {
@@ -1106,19 +1056,12 @@ private function relativize(string $xpath): string
return $xpath; // The XPath expression is invalid
}
- /**
- * @return \DOMNode|null
- */
- public function getNode(int $position)
+ public function getNode(int $position): ?\DOMNode
{
return $this->nodes[$position] ?? null;
}
- /**
- * @return int
- */
- #[\ReturnTypeWillChange]
- public function count()
+ public function count(): int
{
return \count($this->nodes);
}
@@ -1126,16 +1069,12 @@ public function count()
/**
* @return \ArrayIterator
*/
- #[\ReturnTypeWillChange]
- public function getIterator()
+ public function getIterator(): \ArrayIterator
{
return new \ArrayIterator($this->nodes);
}
- /**
- * @return array
- */
- protected function sibling(\DOMNode $node, string $siblingDir = 'nextSibling')
+ protected function sibling(\DOMNode $node, string $siblingDir = 'nextSibling'): array
{
$nodes = [];
@@ -1159,9 +1098,6 @@ private function parseXhtml(string $htmlContent, string $charset = 'UTF-8'): \DO
$htmlContent = $this->convertToHtmlEntities($htmlContent, $charset);
$internalErrors = libxml_use_internal_errors(true);
- if (\LIBXML_VERSION < 20900) {
- $disableEntities = libxml_disable_entity_loader(true);
- }
$dom = new \DOMDocument('1.0', $charset);
$dom->validateOnParse = true;
@@ -1171,9 +1107,6 @@ private function parseXhtml(string $htmlContent, string $charset = 'UTF-8'): \DO
}
libxml_use_internal_errors($internalErrors);
- if (\LIBXML_VERSION < 20900) {
- libxml_disable_entity_loader($disableEntities);
- }
return $dom;
}
@@ -1183,15 +1116,15 @@ private function parseXhtml(string $htmlContent, string $charset = 'UTF-8'): \DO
*/
private function convertToHtmlEntities(string $htmlContent, string $charset = 'UTF-8'): string
{
- set_error_handler(function () { throw new \Exception(); });
+ set_error_handler(static fn () => throw new \Exception());
try {
return mb_encode_numericentity($htmlContent, [0x80, 0x10FFFF, 0, 0x1FFFFF], $charset);
- } catch (\Exception|\ValueError $e) {
+ } catch (\Exception|\ValueError) {
try {
$htmlContent = iconv($charset, 'UTF-8', $htmlContent);
$htmlContent = mb_encode_numericentity($htmlContent, [0x80, 0x10FFFF, 0, 0x1FFFFF], 'UTF-8');
- } catch (\Exception|\ValueError $e) {
+ } catch (\Exception|\ValueError) {
}
return $htmlContent;
@@ -1249,10 +1182,8 @@ private function findNamespacePrefixes(string $xpath): array
* Creates a crawler for some subnodes.
*
* @param \DOMNodeList|\DOMNode|\DOMNode[]|string|null $nodes
- *
- * @return static
*/
- private function createSubCrawler($nodes): object
+ private function createSubCrawler(\DOMNodeList|\DOMNode|array|string|null $nodes): static
{
$crawler = new static($nodes, $this->uri, $this->baseHref);
$crawler->isHtml = $this->isHtml;
@@ -1291,12 +1222,14 @@ private function parseHtmlString(string $content, string $charset): \DOMDocument
private function canParseHtml5String(string $content): bool
{
- if (null === $this->html5Parser) {
+ if (!$this->html5Parser) {
return false;
}
+
if (false === ($pos = stripos($content, ''))) {
return false;
}
+
$header = substr($content, 0, $pos);
return '' === $header || $this->isValidHtml5Heading($header);
@@ -1306,4 +1239,9 @@ private function isValidHtml5Heading(string $heading): bool
{
return 1 === preg_match('/^\x{FEFF}?\s*(\s*)*$/u', $heading);
}
+
+ private function normalizeWhitespace(string $string): string
+ {
+ return trim(preg_replace("/(?:[ \n\r\t\x0C]{2,}+|[\n\r\t\x0C])/", ' ', $string), " \n\r\t\x0C");
+ }
}
diff --git a/symfony/dom-crawler/Field/ChoiceFormField.php b/symfony/dom-crawler/Field/ChoiceFormField.php
index 9eed27ba4..dcae5490a 100644
--- a/symfony/dom-crawler/Field/ChoiceFormField.php
+++ b/symfony/dom-crawler/Field/ChoiceFormField.php
@@ -20,29 +20,17 @@
*/
class ChoiceFormField extends FormField
{
- /**
- * @var string
- */
- private $type;
- /**
- * @var bool
- */
- private $multiple;
- /**
- * @var array
- */
- private $options;
- /**
- * @var bool
- */
- private $validationDisabled = false;
+ private string $type;
+ private bool $multiple;
+ private array $options;
+ private bool $validationDisabled = false;
/**
* Returns true if the field should be included in the submitted values.
*
* @return bool true if the field should be included in the submitted values, false otherwise
*/
- public function hasValue()
+ public function hasValue(): bool
{
// don't send a value for unchecked checkboxes
if (\in_array($this->type, ['checkbox', 'radio']) && null === $this->value) {
@@ -54,10 +42,8 @@ public function hasValue()
/**
* Check if the current selected option is disabled.
- *
- * @return bool
*/
- public function isDisabled()
+ public function isDisabled(): bool
{
if (parent::isDisabled() && 'select' === $this->type) {
return true;
@@ -75,9 +61,9 @@ public function isDisabled()
/**
* Sets the value of the field.
*
- * @param string|array $value The value of the field
+ * @return void
*/
- public function select($value)
+ public function select(string|array|bool $value)
{
$this->setValue($value);
}
@@ -85,6 +71,8 @@ public function select($value)
/**
* Ticks a checkbox.
*
+ * @return void
+ *
* @throws \LogicException When the type provided is not correct
*/
public function tick()
@@ -99,6 +87,8 @@ public function tick()
/**
* Unticks a checkbox.
*
+ * @return void
+ *
* @throws \LogicException When the type provided is not correct
*/
public function untick()
@@ -113,11 +103,11 @@ public function untick()
/**
* Sets the value of the field.
*
- * @param string|array|bool|null $value The value of the field
+ * @return void
*
* @throws \InvalidArgumentException When value type provided is not correct
*/
- public function setValue($value)
+ public function setValue(string|array|bool|null $value)
{
if ('checkbox' === $this->type && false === $value) {
// uncheck
@@ -159,7 +149,7 @@ public function setValue($value)
*
* @internal
*/
- public function addChoice(\DOMElement $node)
+ public function addChoice(\DOMElement $node): void
{
if (!$this->multiple && 'radio' !== $this->type) {
throw new \LogicException(sprintf('Unable to add a choice for "%s" as it is not multiple or is not a radio button.', $this->name));
@@ -175,20 +165,16 @@ public function addChoice(\DOMElement $node)
/**
* Returns the type of the choice field (radio, select, or checkbox).
- *
- * @return string
*/
- public function getType()
+ public function getType(): string
{
return $this->type;
}
/**
* Returns true if the field accepts multiple values.
- *
- * @return bool
*/
- public function isMultiple()
+ public function isMultiple(): bool
{
return $this->multiple;
}
@@ -196,6 +182,8 @@ public function isMultiple()
/**
* Initializes the form field.
*
+ * @return void
+ *
* @throws \LogicException When node type is incorrect
*/
protected function initialize()
@@ -244,7 +232,7 @@ protected function initialize()
}
// if no option is selected and if it is a simple select box, take the first option as the value
- if (!$found && !$this->multiple && !empty($this->options)) {
+ if (!$found && !$this->multiple && $this->options) {
$this->value = $this->options[0]['value'];
}
}
@@ -268,11 +256,9 @@ private function buildOptionValue(\DOMElement $node): array
/**
* Checks whether given value is in the existing options.
*
- * @internal since Symfony 5.3
- *
- * @return bool
+ * @internal
*/
- public function containsOption(string $optionValue, array $options)
+ public function containsOption(string $optionValue, array $options): bool
{
if ($this->validationDisabled) {
return true;
@@ -290,11 +276,9 @@ public function containsOption(string $optionValue, array $options)
/**
* Returns list of available field options.
*
- * @internal since Symfony 5.3
- *
- * @return array
+ * @internal
*/
- public function availableOptionValues()
+ public function availableOptionValues(): array
{
$values = [];
@@ -308,11 +292,11 @@ public function availableOptionValues()
/**
* Disables the internal validation of the field.
*
- * @internal since Symfony 5.3
+ * @internal
*
* @return $this
*/
- public function disableValidation()
+ public function disableValidation(): static
{
$this->validationDisabled = true;
diff --git a/symfony/dom-crawler/Field/FileFormField.php b/symfony/dom-crawler/Field/FileFormField.php
index bd97e7688..4ebe766f0 100644
--- a/symfony/dom-crawler/Field/FileFormField.php
+++ b/symfony/dom-crawler/Field/FileFormField.php
@@ -23,6 +23,8 @@ class FileFormField extends FormField
*
* @param int $error The error code (one of UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE, UPLOAD_ERR_PARTIAL, UPLOAD_ERR_NO_FILE, UPLOAD_ERR_NO_TMP_DIR, UPLOAD_ERR_CANT_WRITE, or UPLOAD_ERR_EXTENSION)
*
+ * @return void
+ *
* @throws \InvalidArgumentException When error code doesn't exist
*/
public function setErrorCode(int $error)
@@ -37,6 +39,8 @@ public function setErrorCode(int $error)
/**
* Sets the value of the field.
+ *
+ * @return void
*/
public function upload(?string $value)
{
@@ -45,6 +49,8 @@ public function upload(?string $value)
/**
* Sets the value of the field.
+ *
+ * @return void
*/
public function setValue(?string $value)
{
@@ -76,6 +82,8 @@ public function setValue(?string $value)
/**
* Sets path to the file as string for simulating HTTP request.
+ *
+ * @return void
*/
public function setFilePath(string $path)
{
@@ -85,6 +93,8 @@ public function setFilePath(string $path)
/**
* Initializes the form field.
*
+ * @return void
+ *
* @throws \LogicException When node type is incorrect
*/
protected function initialize()
diff --git a/symfony/dom-crawler/Field/FormField.php b/symfony/dom-crawler/Field/FormField.php
index 066af4a16..b97d54dda 100644
--- a/symfony/dom-crawler/Field/FormField.php
+++ b/symfony/dom-crawler/Field/FormField.php
@@ -57,10 +57,8 @@ public function __construct(\DOMElement $node)
/**
* Returns the label tag associated to the field or null if none.
- *
- * @return \DOMElement|null
*/
- public function getLabel()
+ public function getLabel(): ?\DOMElement
{
$xpath = new \DOMXPath($this->node->ownerDocument);
@@ -78,26 +76,24 @@ public function getLabel()
/**
* Returns the name of the field.
- *
- * @return string
*/
- public function getName()
+ public function getName(): string
{
return $this->name;
}
/**
* Gets the value of the field.
- *
- * @return string|array|null
*/
- public function getValue()
+ public function getValue(): string|array|null
{
return $this->value;
}
/**
* Sets the value of the field.
+ *
+ * @return void
*/
public function setValue(?string $value)
{
@@ -106,26 +102,24 @@ public function setValue(?string $value)
/**
* Returns true if the field should be included in the submitted values.
- *
- * @return bool
*/
- public function hasValue()
+ public function hasValue(): bool
{
return true;
}
/**
* Check if the current field is disabled.
- *
- * @return bool
*/
- public function isDisabled()
+ public function isDisabled(): bool
{
return $this->node->hasAttribute('disabled');
}
/**
* Initializes the form field.
+ *
+ * @return void
*/
abstract protected function initialize();
}
diff --git a/symfony/dom-crawler/Field/InputFormField.php b/symfony/dom-crawler/Field/InputFormField.php
index 1c3c84d72..19d77352f 100644
--- a/symfony/dom-crawler/Field/InputFormField.php
+++ b/symfony/dom-crawler/Field/InputFormField.php
@@ -24,6 +24,8 @@ class InputFormField extends FormField
/**
* Initializes the form field.
*
+ * @return void
+ *
* @throws \LogicException When node type is incorrect
*/
protected function initialize()
diff --git a/symfony/dom-crawler/Field/TextareaFormField.php b/symfony/dom-crawler/Field/TextareaFormField.php
index 15526e1c2..5168c5225 100644
--- a/symfony/dom-crawler/Field/TextareaFormField.php
+++ b/symfony/dom-crawler/Field/TextareaFormField.php
@@ -21,6 +21,8 @@ class TextareaFormField extends FormField
/**
* Initializes the form field.
*
+ * @return void
+ *
* @throws \LogicException When node type is incorrect
*/
protected function initialize()
diff --git a/symfony/dom-crawler/Form.php b/symfony/dom-crawler/Form.php
index ebad35b38..fd55546fe 100644
--- a/symfony/dom-crawler/Form.php
+++ b/symfony/dom-crawler/Form.php
@@ -21,20 +21,9 @@
*/
class Form extends Link implements \ArrayAccess
{
- /**
- * @var \DOMElement
- */
- private $button;
-
- /**
- * @var FormFieldRegistry
- */
- private $fields;
-
- /**
- * @var string
- */
- private $baseHref;
+ private \DOMElement $button;
+ private FormFieldRegistry $fields;
+ private ?string $baseHref;
/**
* @param \DOMElement $node A \DOMElement instance
@@ -44,7 +33,7 @@ class Form extends Link implements \ArrayAccess
*
* @throws \LogicException if the node is not a button inside a form tag
*/
- public function __construct(\DOMElement $node, string $currentUri = null, string $method = null, string $baseHref = null)
+ public function __construct(\DOMElement $node, ?string $currentUri = null, ?string $method = null, ?string $baseHref = null)
{
parent::__construct($node, $currentUri, $method);
$this->baseHref = $baseHref;
@@ -54,10 +43,8 @@ public function __construct(\DOMElement $node, string $currentUri = null, string
/**
* Gets the form node associated with this form.
- *
- * @return \DOMElement
*/
- public function getFormNode()
+ public function getFormNode(): \DOMElement
{
return $this->node;
}
@@ -69,7 +56,7 @@ public function getFormNode()
*
* @return $this
*/
- public function setValues(array $values)
+ public function setValues(array $values): static
{
foreach ($values as $name => $value) {
$this->fields->set($name, $value);
@@ -82,10 +69,8 @@ public function setValues(array $values)
* Gets the field values.
*
* The returned array does not include file fields (@see getFiles).
- *
- * @return array
*/
- public function getValues()
+ public function getValues(): array
{
$values = [];
foreach ($this->fields->all() as $name => $field) {
@@ -103,10 +88,8 @@ public function getValues()
/**
* Gets the file field values.
- *
- * @return array
*/
- public function getFiles()
+ public function getFiles(): array
{
if (!\in_array($this->getMethod(), ['POST', 'PUT', 'DELETE', 'PATCH'])) {
return [];
@@ -132,10 +115,8 @@ public function getFiles()
*
* This method converts fields with the array notation
* (like foo[bar] to arrays) like PHP does.
- *
- * @return array
*/
- public function getPhpValues()
+ public function getPhpValues(): array
{
$values = [];
foreach ($this->getValues() as $name => $value) {
@@ -159,10 +140,8 @@ public function getPhpValues()
* (@see getPhpValues), rather than uploaded files found in $_FILES.
* For a compound file field foo[bar] it will create foo[bar][name],
* instead of foo[name][bar] which would be found in $_FILES.
- *
- * @return array
*/
- public function getPhpFiles()
+ public function getPhpFiles(): array
{
$values = [];
foreach ($this->getFiles() as $name => $value) {
@@ -195,10 +174,8 @@ function (&$value, $key) {
* The returned URI is not the same as the form "action" attribute.
* This method merges the value if the method is GET to mimics
* browser behavior.
- *
- * @return string
*/
- public function getUri()
+ public function getUri(): string
{
$uri = parent::getUri();
@@ -219,7 +196,7 @@ public function getUri()
return $uri;
}
- protected function getRawUri()
+ protected function getRawUri(): string
{
// If the form was created from a button rather than the form node, check for HTML5 action overrides
if ($this->button !== $this->node && $this->button->getAttribute('formaction')) {
@@ -233,10 +210,8 @@ protected function getRawUri()
* Gets the form method.
*
* If no method is defined in the form, GET is returned.
- *
- * @return string
*/
- public function getMethod()
+ public function getMethod(): string
{
if (null !== $this->method) {
return $this->method;
@@ -262,16 +237,16 @@ public function getName(): string
/**
* Returns true if the named field exists.
- *
- * @return bool
*/
- public function has(string $name)
+ public function has(string $name): bool
{
return $this->fields->has($name);
}
/**
* Removes a field from the form.
+ *
+ * @return void
*/
public function remove(string $name)
{
@@ -285,13 +260,15 @@ public function remove(string $name)
*
* @throws \InvalidArgumentException When field is not present in this form
*/
- public function get(string $name)
+ public function get(string $name): FormField|array
{
return $this->fields->get($name);
}
/**
* Sets a named field.
+ *
+ * @return void
*/
public function set(FormField $field)
{
@@ -303,7 +280,7 @@ public function set(FormField $field)
*
* @return FormField[]
*/
- public function all()
+ public function all(): array
{
return $this->fields->all();
}
@@ -312,11 +289,8 @@ public function all()
* Returns true if the named field exists.
*
* @param string $name The field name
- *
- * @return bool
*/
- #[\ReturnTypeWillChange]
- public function offsetExists($name)
+ public function offsetExists(mixed $name): bool
{
return $this->has($name);
}
@@ -330,8 +304,7 @@ public function offsetExists($name)
*
* @throws \InvalidArgumentException if the field does not exist
*/
- #[\ReturnTypeWillChange]
- public function offsetGet($name)
+ public function offsetGet(mixed $name): FormField|array
{
return $this->fields->get($name);
}
@@ -342,12 +315,9 @@ public function offsetGet($name)
* @param string $name The field name
* @param string|array $value The value of the field
*
- * @return void
- *
* @throws \InvalidArgumentException if the field does not exist
*/
- #[\ReturnTypeWillChange]
- public function offsetSet($name, $value)
+ public function offsetSet(mixed $name, mixed $value): void
{
$this->fields->set($name, $value);
}
@@ -356,11 +326,8 @@ public function offsetSet($name, $value)
* Removes a field from the form.
*
* @param string $name The field name
- *
- * @return void
*/
- #[\ReturnTypeWillChange]
- public function offsetUnset($name)
+ public function offsetUnset(mixed $name): void
{
$this->fields->remove($name);
}
@@ -370,7 +337,7 @@ public function offsetUnset($name)
*
* @return $this
*/
- public function disableValidation()
+ public function disableValidation(): static
{
foreach ($this->fields->all() as $field) {
if ($field instanceof Field\ChoiceFormField) {
@@ -386,6 +353,8 @@ public function disableValidation()
*
* Expects a 'submit' button \DOMElement and finds the corresponding form element, or the form element itself.
*
+ * @return void
+ *
* @throws \LogicException If given node is not a button or input or does not have a form ancestor
*/
protected function setNode(\DOMElement $node)
@@ -423,7 +392,7 @@ protected function setNode(\DOMElement $node)
* the form node or the entire document depending on whether we need
* to find non-descendant elements through HTML5 'form' attribute.
*/
- private function initialize()
+ private function initialize(): void
{
$this->fields = new FormFieldRegistry();
@@ -455,14 +424,14 @@ private function initialize()
// corresponding elements are either descendants or have a matching HTML5 form attribute
$formId = Crawler::xpathLiteral($this->node->getAttribute('id'));
- $fieldNodes = $xpath->query(sprintf('( descendant::input[@form=%s] | descendant::button[@form=%1$s] | descendant::textarea[@form=%1$s] | descendant::select[@form=%1$s] | //form[@id=%1$s]//input[not(@form)] | //form[@id=%1$s]//button[not(@form)] | //form[@id=%1$s]//textarea[not(@form)] | //form[@id=%1$s]//select[not(@form)] )[not(ancestor::template)]', $formId));
+ $fieldNodes = $xpath->query(sprintf('( descendant::input[@form=%s] | descendant::button[@form=%1$s] | descendant::textarea[@form=%1$s] | descendant::select[@form=%1$s] | //form[@id=%1$s]//input[not(@form)] | //form[@id=%1$s]//button[not(@form)] | //form[@id=%1$s]//textarea[not(@form)] | //form[@id=%1$s]//select[not(@form)] )[( not(ancestor::template) or ancestor::turbo-stream )]', $formId));
foreach ($fieldNodes as $node) {
$this->addField($node);
}
} else {
// do the xpath query with $this->node as the context node, to only find descendant elements
// however, descendant elements with form attribute are not part of this form
- $fieldNodes = $xpath->query('( descendant::input[not(@form)] | descendant::button[not(@form)] | descendant::textarea[not(@form)] | descendant::select[not(@form)] )[not(ancestor::template)]', $this->node);
+ $fieldNodes = $xpath->query('( descendant::input[not(@form)] | descendant::button[not(@form)] | descendant::textarea[not(@form)] | descendant::select[not(@form)] )[( not(ancestor::template) or ancestor::turbo-stream )]', $this->node);
foreach ($fieldNodes as $node) {
$this->addField($node);
}
@@ -473,7 +442,7 @@ private function initialize()
}
}
- private function addField(\DOMElement $node)
+ private function addField(\DOMElement $node): void
{
if (!$node->hasAttribute('name') || !$node->getAttribute('name')) {
return;
diff --git a/symfony/dom-crawler/FormFieldRegistry.php b/symfony/dom-crawler/FormFieldRegistry.php
index 93522adcb..9e1571656 100644
--- a/symfony/dom-crawler/FormFieldRegistry.php
+++ b/symfony/dom-crawler/FormFieldRegistry.php
@@ -20,14 +20,13 @@
*/
class FormFieldRegistry
{
- private $fields = [];
-
- private $base = '';
+ private array $fields = [];
+ private string $base = '';
/**
* Adds a field to the registry.
*/
- public function add(FormField $field)
+ public function add(FormField $field): void
{
$segments = $this->getSegments($field->getName());
@@ -47,9 +46,9 @@ public function add(FormField $field)
}
/**
- * Removes a field based on the fully qualifed name and its children from the registry.
+ * Removes a field based on the fully qualified name and its children from the registry.
*/
- public function remove(string $name)
+ public function remove(string $name): void
{
$segments = $this->getSegments($name);
$target = &$this->fields;
@@ -64,13 +63,13 @@ public function remove(string $name)
}
/**
- * Returns the value of the field based on the fully qualifed name and its children.
+ * Returns the value of the field based on the fully qualified name and its children.
*
* @return FormField|FormField[]|FormField[][]
*
* @throws \InvalidArgumentException if the field does not exist
*/
- public function &get(string $name)
+ public function &get(string $name): FormField|array
{
$segments = $this->getSegments($name);
$target = &$this->fields;
@@ -94,7 +93,7 @@ public function has(string $name): bool
$this->get($name);
return true;
- } catch (\InvalidArgumentException $e) {
+ } catch (\InvalidArgumentException) {
return false;
}
}
@@ -102,11 +101,9 @@ public function has(string $name): bool
/**
* Set the value of a field based on the fully qualified name and its children.
*
- * @param mixed $value The value
- *
* @throws \InvalidArgumentException if the field does not exist
*/
- public function set(string $name, $value)
+ public function set(string $name, mixed $value): void
{
$target = &$this->get($name);
if ((!\is_array($value) && $target instanceof Field\FormField) || $target instanceof Field\ChoiceFormField) {
diff --git a/symfony/dom-crawler/Image.php b/symfony/dom-crawler/Image.php
index b1ac5ca2c..34c8fda6c 100644
--- a/symfony/dom-crawler/Image.php
+++ b/symfony/dom-crawler/Image.php
@@ -16,16 +16,19 @@
*/
class Image extends AbstractUriElement
{
- public function __construct(\DOMElement $node, string $currentUri = null)
+ public function __construct(\DOMElement $node, ?string $currentUri = null)
{
parent::__construct($node, $currentUri, 'GET');
}
- protected function getRawUri()
+ protected function getRawUri(): string
{
return $this->node->getAttribute('src');
}
+ /**
+ * @return void
+ */
protected function setNode(\DOMElement $node)
{
if ('img' !== $node->nodeName) {
diff --git a/symfony/dom-crawler/LICENSE b/symfony/dom-crawler/LICENSE
index 88bf75bb4..0138f8f07 100644
--- a/symfony/dom-crawler/LICENSE
+++ b/symfony/dom-crawler/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2004-2022 Fabien Potencier
+Copyright (c) 2004-present Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/symfony/dom-crawler/Link.php b/symfony/dom-crawler/Link.php
index 80a356e46..681a2f7a2 100644
--- a/symfony/dom-crawler/Link.php
+++ b/symfony/dom-crawler/Link.php
@@ -18,11 +18,14 @@
*/
class Link extends AbstractUriElement
{
- protected function getRawUri()
+ protected function getRawUri(): string
{
return $this->node->getAttribute('href');
}
+ /**
+ * @return void
+ */
protected function setNode(\DOMElement $node)
{
if ('a' !== $node->nodeName && 'area' !== $node->nodeName && 'link' !== $node->nodeName) {
diff --git a/symfony/dom-crawler/UriResolver.php b/symfony/dom-crawler/UriResolver.php
index be64f5257..d3b0c8396 100644
--- a/symfony/dom-crawler/UriResolver.php
+++ b/symfony/dom-crawler/UriResolver.php
@@ -58,7 +58,7 @@ public static function resolve(string $uri, ?string $baseUri): string
}
// absolute URL with relative schema
- if (0 === strpos($uri, '//')) {
+ if (str_starts_with($uri, '//')) {
return preg_replace('#^([^/]*)//.*$#', '$1', $baseUriCleaned).$uri;
}
@@ -70,7 +70,7 @@ public static function resolve(string $uri, ?string $baseUri): string
}
// relative path
- $path = parse_url(substr($baseUri, \strlen($baseUriCleaned)), \PHP_URL_PATH);
+ $path = parse_url(substr($baseUri, \strlen($baseUriCleaned)), \PHP_URL_PATH) ?? '';
$path = self::canonicalizePath(substr($path, 0, strrpos($path, '/')).'/'.$uri);
return $baseUriCleaned.('' === $path || '/' !== $path[0] ? '/' : '').$path;
@@ -85,7 +85,7 @@ private static function canonicalizePath(string $path): string
return $path;
}
- if ('.' === substr($path, -1)) {
+ if (str_ends_with($path, '.')) {
$path .= '/';
}