Update composer dependencies

This commit is contained in:
Kijin Sung 2017-06-29 23:39:23 +09:00
parent 49cc39e507
commit cbd324c35b
428 changed files with 17862 additions and 5885 deletions

View file

@ -21,18 +21,18 @@
"ext-xml": "*",
"coolsms/php-sdk": "2.0.*",
"defuse/php-encryption": "1.2.1",
"ezyang/htmlpurifier": "4.7.*",
"ezyang/htmlpurifier": "4.9.*",
"hautelook/phpass": "0.3.*",
"jbbcode/jbbcode": "1.3.*",
"jmhobbs/swiftmailer-transport-aws-ses": "0.9.*",
"leafo/lessphp": "0.5.*",
"leafo/scssphp": "0.6.*",
"league/html-to-markdown": "4.2.*",
"league/html-to-markdown": "4.4.*",
"matthiasmullie/minify": "1.3.*",
"matthiasmullie/path-converter": "1.*",
"michelf/php-markdown": "1.6.*",
"michelf/php-smartypants": "1.6.0-beta1",
"rmccue/requests": "1.6.*",
"michelf/php-markdown": "1.7.*",
"michelf/php-smartypants": "1.8.*",
"rmccue/requests": "1.7.*",
"sunra/php-simple-html-dom-parser": "1.5.*",
"swiftmailer/swiftmailer": "5.4.*",
"true/punycode": "2.*"

225
composer.lock generated
View file

@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "66af49066c9b92d6708080f3d32860f4",
"content-hash": "51acda30c4d45b4c956743d4bd1ac16e",
"hash": "4a3f88127b2bbff917b4115f2b080e05",
"content-hash": "bb6a8cf92392a44d46e233f4082af61f",
"packages": [
{
"name": "coolsms/php-sdk",
@ -108,21 +108,24 @@
},
{
"name": "ezyang/htmlpurifier",
"version": "v4.7.0",
"version": "v4.9.3",
"source": {
"type": "git",
"url": "https://github.com/ezyang/htmlpurifier.git",
"reference": "ae1828d955112356f7677c465f94f7deb7d27a40"
"reference": "95e1bae3182efc0f3422896a3236e991049dac69"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/ae1828d955112356f7677c465f94f7deb7d27a40",
"reference": "ae1828d955112356f7677c465f94f7deb7d27a40",
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/95e1bae3182efc0f3422896a3236e991049dac69",
"reference": "95e1bae3182efc0f3422896a3236e991049dac69",
"shasum": ""
},
"require": {
"php": ">=5.2"
},
"require-dev": {
"simpletest/simpletest": "^1.1"
},
"type": "library",
"autoload": {
"psr-0": {
@ -148,20 +151,20 @@
"keywords": [
"html"
],
"time": "2015-08-05 01:03:42"
"time": "2017-06-03 02:28:16"
},
{
"name": "hautelook/phpass",
"version": "0.3.4",
"version": "0.3.5",
"source": {
"type": "git",
"url": "https://github.com/hautelook/phpass.git",
"reference": "f0217d804225822f9bdb0d392839029b0fcb0914"
"reference": "b4cbd9b67ed3ef5672ec79d8e0c46d24bd844abd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hautelook/phpass/zipball/f0217d804225822f9bdb0d392839029b0fcb0914",
"reference": "f0217d804225822f9bdb0d392839029b0fcb0914",
"url": "https://api.github.com/repos/hautelook/phpass/zipball/b4cbd9b67ed3ef5672ec79d8e0c46d24bd844abd",
"reference": "b4cbd9b67ed3ef5672ec79d8e0c46d24bd844abd",
"shasum": ""
},
"require": {
@ -326,16 +329,16 @@
},
{
"name": "leafo/scssphp",
"version": "v0.6.6",
"version": "v0.6.7",
"source": {
"type": "git",
"url": "https://github.com/leafo/scssphp.git",
"reference": "6fdfe19d2b13a3f12ba0792227f0718809ce4e4d"
"reference": "562213cd803e42ea53b0735554794c4022d8db89"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/leafo/scssphp/zipball/6fdfe19d2b13a3f12ba0792227f0718809ce4e4d",
"reference": "6fdfe19d2b13a3f12ba0792227f0718809ce4e4d",
"url": "https://api.github.com/repos/leafo/scssphp/zipball/562213cd803e42ea53b0735554794c4022d8db89",
"reference": "562213cd803e42ea53b0735554794c4022d8db89",
"shasum": ""
},
"require": {
@ -375,20 +378,20 @@
"scss",
"stylesheet"
],
"time": "2016-09-11 01:34:11"
"time": "2017-02-23 05:07:33"
},
{
"name": "league/html-to-markdown",
"version": "4.2.2",
"version": "4.4.1",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/html-to-markdown.git",
"reference": "8dfe3b1e6d459b320bec1a4b5499cd9d62796ac0"
"reference": "82ea375b5b2b1da1da222644c0565c695bf88186"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/8dfe3b1e6d459b320bec1a4b5499cd9d62796ac0",
"reference": "8dfe3b1e6d459b320bec1a4b5499cd9d62796ac0",
"url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/82ea375b5b2b1da1da222644c0565c695bf88186",
"reference": "82ea375b5b2b1da1da222644c0565c695bf88186",
"shasum": ""
},
"require": {
@ -407,7 +410,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "4.3-dev"
"dev-master": "4.5-dev"
}
},
"autoload": {
@ -439,31 +442,35 @@
"html",
"markdown"
],
"time": "2016-09-27 12:38:24"
"time": "2017-03-16 00:45:59"
},
{
"name": "matthiasmullie/minify",
"version": "1.3.39",
"version": "1.3.45",
"source": {
"type": "git",
"url": "https://github.com/matthiasmullie/minify.git",
"reference": "1a6cb6b457690034bde461593edb510949bdd6e7"
"reference": "09b83e9dbdc50cf6734c6a9652a54891c0651998"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/matthiasmullie/minify/zipball/1a6cb6b457690034bde461593edb510949bdd6e7",
"reference": "1a6cb6b457690034bde461593edb510949bdd6e7",
"url": "https://api.github.com/repos/matthiasmullie/minify/zipball/09b83e9dbdc50cf6734c6a9652a54891c0651998",
"reference": "09b83e9dbdc50cf6734c6a9652a54891c0651998",
"shasum": ""
},
"require": {
"ext-pcre": "*",
"matthiasmullie/path-converter": "~1.0",
"matthiasmullie/path-converter": "~1.1",
"php": ">=5.3.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "~1.0",
"matthiasmullie/scrapbook": "~1.0",
"phpunit/phpunit": "~4.8"
},
"suggest": {
"psr/cache-implementation": "Cache implementation to use with Minify::cache"
},
"bin": [
"bin/minifycss",
"bin/minifyjs"
@ -495,20 +502,20 @@
"minifier",
"minify"
],
"time": "2016-10-27 22:32:49"
"time": "2017-06-13 15:54:31"
},
{
"name": "matthiasmullie/path-converter",
"version": "1.0.8",
"version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/matthiasmullie/path-converter.git",
"reference": "7c36e5cafa95dd20008d19b153b506cffa8c2848"
"reference": "08551ec1b156e923c242a10ab484bd4d6ead6631"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/7c36e5cafa95dd20008d19b153b506cffa8c2848",
"reference": "7c36e5cafa95dd20008d19b153b506cffa8c2848",
"url": "https://api.github.com/repos/matthiasmullie/path-converter/zipball/08551ec1b156e923c242a10ab484bd4d6ead6631",
"reference": "08551ec1b156e923c242a10ab484bd4d6ead6631",
"shasum": ""
},
"require": {
@ -544,20 +551,20 @@
"paths",
"relative"
],
"time": "2016-04-27 10:38:05"
"time": "2017-01-26 08:54:49"
},
{
"name": "michelf/php-markdown",
"version": "1.6.0",
"version": "1.7.0",
"source": {
"type": "git",
"url": "https://github.com/michelf/php-markdown.git",
"reference": "156e56ee036505ec637d761ee62dc425d807183c"
"reference": "1f51cc520948f66cd2af8cbc45a5ee175e774220"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/michelf/php-markdown/zipball/156e56ee036505ec637d761ee62dc425d807183c",
"reference": "156e56ee036505ec637d761ee62dc425d807183c",
"url": "https://api.github.com/repos/michelf/php-markdown/zipball/1f51cc520948f66cd2af8cbc45a5ee175e774220",
"reference": "1f51cc520948f66cd2af8cbc45a5ee175e774220",
"shasum": ""
},
"require": {
@ -595,31 +602,26 @@
"keywords": [
"markdown"
],
"time": "2015-12-24 01:37:31"
"time": "2016-10-29 18:58:20"
},
{
"name": "michelf/php-smartypants",
"version": "1.6.0-beta1",
"version": "1.8.1",
"source": {
"type": "git",
"url": "https://github.com/michelf/php-smartypants.git",
"reference": "c0465c6d4c5ab853c2fa45df6c10bce7e35cc137"
"reference": "47d17c90a4dfd0ccf1f87e25c65e6c8012415aad"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/michelf/php-smartypants/zipball/171a3a2552f33340bf3636bdb4b05eb4e406fbda",
"reference": "c0465c6d4c5ab853c2fa45df6c10bce7e35cc137",
"url": "https://api.github.com/repos/michelf/php-smartypants/zipball/47d17c90a4dfd0ccf1f87e25c65e6c8012415aad",
"reference": "47d17c90a4dfd0ccf1f87e25c65e6c8012415aad",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-lib": "1.6.x-dev"
}
},
"autoload": {
"psr-0": {
"Michelf": ""
@ -633,16 +635,16 @@
{
"name": "Michel Fortin",
"email": "michel.fortin@michelf.ca",
"homepage": "http://michelf.ca/",
"homepage": "https://michelf.ca/",
"role": "Developer"
},
{
"name": "John Gruber",
"homepage": "http://daringfireball.net/"
"homepage": "https://daringfireball.net/"
}
],
"description": "PHP SmartyPants",
"homepage": "http://michelf.ca/projects/php-smartypants/",
"homepage": "https://michelf.ca/projects/php-smartypants/",
"keywords": [
"dashes",
"quotes",
@ -650,27 +652,27 @@
"typographer",
"typography"
],
"time": "2013-07-31 18:13:10"
"time": "2016-12-13 01:01:17"
},
{
"name": "rmccue/requests",
"version": "v1.6.1",
"version": "v1.7.0",
"source": {
"type": "git",
"url": "https://github.com/rmccue/Requests.git",
"reference": "6aac485666c2955077d77b796bbdd25f0013a4ea"
"reference": "87932f52ffad70504d93f04f15690cf16a089546"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rmccue/Requests/zipball/6aac485666c2955077d77b796bbdd25f0013a4ea",
"reference": "6aac485666c2955077d77b796bbdd25f0013a4ea",
"url": "https://api.github.com/repos/rmccue/Requests/zipball/87932f52ffad70504d93f04f15690cf16a089546",
"reference": "87932f52ffad70504d93f04f15690cf16a089546",
"shasum": ""
},
"require": {
"php": ">=5.2"
},
"require-dev": {
"satooshi/php-coveralls": "dev-master"
"requests/test-server": "dev-master"
},
"type": "library",
"autoload": {
@ -699,23 +701,24 @@
"iri",
"sockets"
],
"time": "2014-05-18 04:59:02"
"time": "2016-10-13 00:11:37"
},
{
"name": "sunra/php-simple-html-dom-parser",
"version": "v1.5.1",
"version": "v1.5.2",
"source": {
"type": "git",
"url": "https://github.com/sunra/php-simple-html-dom-parser.git",
"reference": "f910346ce47513a49ed5b8de197cde26c3f0b193"
"reference": "75b9b1cb64502d8f8c04dc11b5906b969af247c6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sunra/php-simple-html-dom-parser/zipball/f910346ce47513a49ed5b8de197cde26c3f0b193",
"reference": "f910346ce47513a49ed5b8de197cde26c3f0b193",
"url": "https://api.github.com/repos/sunra/php-simple-html-dom-parser/zipball/75b9b1cb64502d8f8c04dc11b5906b969af247c6",
"reference": "75b9b1cb64502d8f8c04dc11b5906b969af247c6",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": ">=5.3.2"
},
"type": "library",
@ -733,6 +736,10 @@
"name": "Sunra",
"email": "sunra@yandex.ru",
"homepage": "https://github.com/sunra"
},
{
"name": "S.C. Chen",
"homepage": "http://sourceforge.net/projects/simplehtmldom/"
}
],
"description": "Composer adaptation of: A HTML DOM parser written in PHP5+ let you manipulate HTML in a very easy way! Require PHP 5+. Supports invalid HTML. Find tags on an HTML page with selectors just like jQuery. Extract contents from HTML in a single line.",
@ -742,27 +749,28 @@
"html",
"parser"
],
"time": "2016-05-20 11:21:15"
"time": "2016-11-22 22:57:47"
},
{
"name": "swiftmailer/swiftmailer",
"version": "v5.4.3",
"version": "v5.4.8",
"source": {
"type": "git",
"url": "https://github.com/swiftmailer/swiftmailer.git",
"reference": "4cc92842069c2bbc1f28daaaf1d2576ec4dfe153"
"reference": "9a06dc570a0367850280eefd3f1dc2da45aef517"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/4cc92842069c2bbc1f28daaaf1d2576ec4dfe153",
"reference": "4cc92842069c2bbc1f28daaaf1d2576ec4dfe153",
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/9a06dc570a0367850280eefd3f1dc2da45aef517",
"reference": "9a06dc570a0367850280eefd3f1dc2da45aef517",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"mockery/mockery": "~0.9.1"
"mockery/mockery": "~0.9.1",
"symfony/phpunit-bridge": "~3.2"
},
"type": "library",
"extra": {
@ -795,25 +803,84 @@
"mail",
"mailer"
],
"time": "2016-07-08 11:51:25"
"time": "2017-05-01 15:54:03"
},
{
"name": "true/punycode",
"version": "v2.1.0",
"name": "symfony/polyfill-mbstring",
"version": "v1.4.0",
"source": {
"type": "git",
"url": "https://github.com/true/php-punycode.git",
"reference": "74033cbe9fdd3eba597f8af501947a125b3b8087"
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "f29dca382a6485c3cbe6379f0c61230167681937"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/true/php-punycode/zipball/74033cbe9fdd3eba597f8af501947a125b3b8087",
"reference": "74033cbe9fdd3eba597f8af501947a125b3b8087",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f29dca382a6485c3cbe6379f0c61230167681937",
"reference": "f29dca382a6485c3cbe6379f0c61230167681937",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": ">=5.3.0"
"php": ">=5.3.3"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4-dev"
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
},
"files": [
"bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"time": "2017-06-09 14:24:12"
},
{
"name": "true/punycode",
"version": "v2.1.1",
"source": {
"type": "git",
"url": "https://github.com/true/php-punycode.git",
"reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/true/php-punycode/zipball/a4d0c11a36dd7f4e7cd7096076cab6d3378a071e",
"reference": "a4d0c11a36dd7f4e7cd7096076cab6d3378a071e",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"symfony/polyfill-mbstring": "^1.3"
},
"require-dev": {
"phpunit/phpunit": "~4.7",
@ -841,15 +908,13 @@
"idna",
"punycode"
],
"time": "2016-08-09 14:50:44"
"time": "2016-11-16 10:37:54"
}
],
"packages-dev": null,
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"michelf/php-smartypants": 10
},
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {

View file

@ -76,6 +76,8 @@ return array(
'HTMLPurifier_AttrTransform_SafeParam' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/SafeParam.php',
'HTMLPurifier_AttrTransform_ScriptRequired' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/ScriptRequired.php',
'HTMLPurifier_AttrTransform_TargetBlank' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetBlank.php',
'HTMLPurifier_AttrTransform_TargetNoopener' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetNoopener.php',
'HTMLPurifier_AttrTransform_TargetNoreferrer' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/TargetNoreferrer.php',
'HTMLPurifier_AttrTransform_Textarea' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/AttrTransform/Textarea.php',
'HTMLPurifier_AttrTypes' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/AttrTypes.php',
'HTMLPurifier_AttrValidator' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/AttrValidator.php',
@ -151,6 +153,8 @@ return array(
'HTMLPurifier_HTMLModule_Tables' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tables.php',
'HTMLPurifier_HTMLModule_Target' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Target.php',
'HTMLPurifier_HTMLModule_TargetBlank' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetBlank.php',
'HTMLPurifier_HTMLModule_TargetNoopener' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetNoopener.php',
'HTMLPurifier_HTMLModule_TargetNoreferrer' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/TargetNoreferrer.php',
'HTMLPurifier_HTMLModule_Text' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Text.php',
'HTMLPurifier_HTMLModule_Tidy' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy.php',
'HTMLPurifier_HTMLModule_Tidy_Name' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/HTMLModule/Tidy/Name.php',
@ -233,6 +237,7 @@ return array(
'HTMLPurifier_URIScheme_mailto' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/mailto.php',
'HTMLPurifier_URIScheme_news' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/news.php',
'HTMLPurifier_URIScheme_nntp' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/nntp.php',
'HTMLPurifier_URIScheme_tel' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/URIScheme/tel.php',
'HTMLPurifier_UnitConverter' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/UnitConverter.php',
'HTMLPurifier_VarParser' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/VarParser.php',
'HTMLPurifier_VarParserException' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier/VarParserException.php',
@ -268,6 +273,7 @@ return array(
'Leafo\\ScssPhp\\Compiler\\Environment' => $vendorDir . '/leafo/scssphp/src/Compiler/Environment.php',
'Leafo\\ScssPhp\\Exception\\CompilerException' => $vendorDir . '/leafo/scssphp/src/Exception/CompilerException.php',
'Leafo\\ScssPhp\\Exception\\ParserException' => $vendorDir . '/leafo/scssphp/src/Exception/ParserException.php',
'Leafo\\ScssPhp\\Exception\\RangeException' => $vendorDir . '/leafo/scssphp/src/Exception/RangeException.php',
'Leafo\\ScssPhp\\Exception\\ServerException' => $vendorDir . '/leafo/scssphp/src/Exception/ServerException.php',
'Leafo\\ScssPhp\\Formatter' => $vendorDir . '/leafo/scssphp/src/Formatter.php',
'Leafo\\ScssPhp\\Formatter\\Compact' => $vendorDir . '/leafo/scssphp/src/Formatter/Compact.php',
@ -287,6 +293,7 @@ return array(
'League\\HTMLToMarkdown\\Configuration' => $vendorDir . '/league/html-to-markdown/src/Configuration.php',
'League\\HTMLToMarkdown\\ConfigurationAwareInterface' => $vendorDir . '/league/html-to-markdown/src/ConfigurationAwareInterface.php',
'League\\HTMLToMarkdown\\Converter\\BlockquoteConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/BlockquoteConverter.php',
'League\\HTMLToMarkdown\\Converter\\CodeConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/CodeConverter.php',
'League\\HTMLToMarkdown\\Converter\\CommentConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/CommentConverter.php',
'League\\HTMLToMarkdown\\Converter\\ConverterInterface' => $vendorDir . '/league/html-to-markdown/src/Converter/ConverterInterface.php',
'League\\HTMLToMarkdown\\Converter\\DefaultConverter' => $vendorDir . '/league/html-to-markdown/src/Converter/DefaultConverter.php',
@ -314,9 +321,13 @@ return array(
'MatthiasMullie\\Minify\\JS' => $vendorDir . '/matthiasmullie/minify/src/JS.php',
'MatthiasMullie\\Minify\\Minify' => $vendorDir . '/matthiasmullie/minify/src/Minify.php',
'MatthiasMullie\\PathConverter\\Converter' => $vendorDir . '/matthiasmullie/path-converter/src/Converter.php',
'MatthiasMullie\\PathConverter\\ConverterInterface' => $vendorDir . '/matthiasmullie/path-converter/src/ConverterInterface.php',
'MatthiasMullie\\PathConverter\\NoConverter' => $vendorDir . '/matthiasmullie/path-converter/src/NoConverter.php',
'Michelf\\Markdown' => $vendorDir . '/michelf/php-markdown/Michelf/Markdown.php',
'Michelf\\MarkdownExtra' => $vendorDir . '/michelf/php-markdown/Michelf/MarkdownExtra.php',
'Michelf\\MarkdownInterface' => $vendorDir . '/michelf/php-markdown/Michelf/MarkdownInterface.php',
'Michelf\\SmartyPants' => $vendorDir . '/michelf/php-smartypants/Michelf/SmartyPants.php',
'Michelf\\SmartyPantsTypographer' => $vendorDir . '/michelf/php-smartypants/Michelf/SmartyPantsTypographer.php',
'Nurigo\\Api\\GroupMessage' => $vendorDir . '/coolsms/php-sdk/app/Nurigo/Api/GroupMessage.php',
'Nurigo\\Api\\Image' => $vendorDir . '/coolsms/php-sdk/app/Nurigo/Api/Image.php',
'Nurigo\\Api\\Message' => $vendorDir . '/coolsms/php-sdk/app/Nurigo/Api/Message.php',
@ -333,6 +344,9 @@ return array(
'Requests_Cookie_Jar' => $vendorDir . '/rmccue/requests/library/Requests/Cookie/Jar.php',
'Requests_Exception' => $vendorDir . '/rmccue/requests/library/Requests/Exception.php',
'Requests_Exception_HTTP' => $vendorDir . '/rmccue/requests/library/Requests/Exception/HTTP.php',
'Requests_Exception_HTTP_304' => $vendorDir . '/rmccue/requests/library/Requests/Exception/HTTP/304.php',
'Requests_Exception_HTTP_305' => $vendorDir . '/rmccue/requests/library/Requests/Exception/HTTP/305.php',
'Requests_Exception_HTTP_306' => $vendorDir . '/rmccue/requests/library/Requests/Exception/HTTP/306.php',
'Requests_Exception_HTTP_400' => $vendorDir . '/rmccue/requests/library/Requests/Exception/HTTP/400.php',
'Requests_Exception_HTTP_401' => $vendorDir . '/rmccue/requests/library/Requests/Exception/HTTP/401.php',
'Requests_Exception_HTTP_402' => $vendorDir . '/rmccue/requests/library/Requests/Exception/HTTP/402.php',
@ -363,6 +377,8 @@ return array(
'Requests_Exception_HTTP_505' => $vendorDir . '/rmccue/requests/library/Requests/Exception/HTTP/505.php',
'Requests_Exception_HTTP_511' => $vendorDir . '/rmccue/requests/library/Requests/Exception/HTTP/511.php',
'Requests_Exception_HTTP_Unknown' => $vendorDir . '/rmccue/requests/library/Requests/Exception/HTTP/Unknown.php',
'Requests_Exception_Transport' => $vendorDir . '/rmccue/requests/library/Requests/Exception/Transport.php',
'Requests_Exception_Transport_cURL' => $vendorDir . '/rmccue/requests/library/Requests/Exception/Transport/cURL.php',
'Requests_Hooker' => $vendorDir . '/rmccue/requests/library/Requests/Hooker.php',
'Requests_Hooks' => $vendorDir . '/rmccue/requests/library/Requests/Hooks.php',
'Requests_IDNAEncoder' => $vendorDir . '/rmccue/requests/library/Requests/IDNAEncoder.php',
@ -385,6 +401,7 @@ return array(
'Swift_Events_ResponseReceivedListener' => $vendorDir . '/jmhobbs/swiftmailer-transport-aws-ses/classes/Swift/Events/ResponseReceivedListener.php',
'Swift_Response_AWSResponse' => $vendorDir . '/jmhobbs/swiftmailer-transport-aws-ses/classes/Swift/Response/AWSResponse.php',
'Swift_Transport_AWSTransport' => $vendorDir . '/jmhobbs/swiftmailer-transport-aws-ses/classes/Swift/Transport/AWSTransport.php',
'Symfony\\Polyfill\\Mbstring\\Mbstring' => $vendorDir . '/symfony/polyfill-mbstring/Mbstring.php',
'TrueBV\\Exception\\DomainOutOfBoundsException' => $vendorDir . '/true/punycode/src/Exception/DomainOutOfBoundsException.php',
'TrueBV\\Exception\\LabelOutOfBoundsException' => $vendorDir . '/true/punycode/src/Exception/LabelOutOfBoundsException.php',
'TrueBV\\Exception\\OutOfBoundsException' => $vendorDir . '/true/punycode/src/Exception/OutOfBoundsException.php',

View file

@ -7,6 +7,7 @@ $baseDir = dirname($vendorDir);
return array(
'2c102faa651ef8ea5874edb585946bce' => $vendorDir . '/swiftmailer/swiftmailer/lib/swift_required.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'8170285c807a9f24f165f37b15bc9a36' => $vendorDir . '/defuse/php-encryption/Crypto.php',
'2cffec82183ee1cea088009cef9a6fc3' => $vendorDir . '/ezyang/htmlpurifier/library/HTMLPurifier.composer.php',
);

View file

@ -7,6 +7,7 @@ $baseDir = dirname($vendorDir);
return array(
'TrueBV\\' => array($vendorDir . '/true/punycode/src'),
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
'Nurigo\\' => array($vendorDir . '/coolsms/php-sdk/app/Nurigo'),
'MatthiasMullie\\PathConverter\\' => array($vendorDir . '/matthiasmullie/path-converter/src'),
'MatthiasMullie\\Minify\\' => array($vendorDir . '/matthiasmullie/minify/src'),

File diff suppressed because it is too large Load diff

View file

@ -15,10 +15,8 @@ with these contents.
---------------------------------------------------------------------------
1. Compatibility
HTML Purifier is PHP 5 only, and is actively tested from PHP 5.0.5 and
up. It has no core dependencies with other libraries. PHP
4 support was deprecated on December 31, 2007 with HTML Purifier 3.0.0.
HTML Purifier is not compatible with zend.ze1_compatibility_mode.
HTML Purifier is PHP 5 and PHP 7, and is actively tested from PHP 5.0.5
and up. It has no core dependencies with other libraries.
These optional extensions can enhance the capabilities of HTML Purifier:
@ -29,7 +27,10 @@ These optional extensions can enhance the capabilities of HTML Purifier:
These optional libraries can enhance the capabilities of HTML Purifier:
* CSSTidy : Clean CSS stylesheets using %Core.ExtractStyleBlocks
Note: You should use the modernized fork of CSSTidy available
at https://github.com/Cerdic/CSSTidy
* Net_IDNA2 (PEAR) : IRI support using %Core.EnableIDNA
Note: This is not necessary for PHP 5.3 or later
---------------------------------------------------------------------------
2. Reconnaissance
@ -305,11 +306,9 @@ appropriate permissions using:
chmod -R 0755 HTMLPurifier/DefinitionCache/Serializer
If the above command doesn't work, you may need to assign write permissions
to all. This may be necessary if your webserver runs as nobody, but is
not recommended since it means any other user can write files in the
directory. Use:
to group:
chmod -R 0777 HTMLPurifier/DefinitionCache/Serializer
chmod -R 0775 HTMLPurifier/DefinitionCache/Serializer
You can also chmod files via your FTP client; this option
is usually accessible by right clicking the corresponding directory and

View file

@ -1,4 +1,4 @@

Installation
Comment installer HTML Purifier

View file

@ -9,6 +9,88 @@ NEWS ( CHANGELOG and HISTORY ) HTMLPurifier
. Internal change
==========================
4.9.3, released 2017-06-02
- Workaround PHP 7.1 infinite loop when opcode cache is enabled.
Thanks @Xiphin (#134, #135)
- Don't use autoloader when testing for DOMDocument. Hypothetically,
this could cause your install to start using DirectLex if you had
previously been monkeypatching in a custom, autoloaded implementation
of DOMDocument. Don't do that. Thanks @Izumi-kun (#130)
4.9.2, released 2017-03-12
- Fixes PHP 5.3 compatibility
- Fix breakage when decoding decimal entities. Thanks @rybakit (#129)
4.9.1, released 2017-03-08
! %URI.DefaultScheme can now be set to null, in which case
all relative paths are removed.
! New CSS properties: min-width, max-width, min-height, max-height (#94)
! Transparency (rgba) and hsl/hsla supported where color CSS is present.
Thanks @fxbt for contributing the patch. (#118)
- When idn_to_ascii is defined, we might accept malformed
hostnames. Apply validation to the result in such cases.
- Close directory when done in Serializer DefinitionCache (#100)
- Deleted some asserts to avoid linters from choking (#97)
- Rework Serializer cache behavior to avoid chmod'ing if possible (#32)
- Embedded semicolons in strings in CSS are now handled correctly!
- We accidentally dropped certain Unicode characters if there was
one or more invalid characters. This has been fixed, thanks
to mpyw <ryosuke_i_628@yahoo.co.jp>
- Fix for "Don't truncate upon encountering </div> when using DOMLex"
caused a regression with HTML 4.01 Strict parsing with libxml 2.9.1
(and maybe later versions, but known OK with libxml 2.9.4). The
fix is to go about handling truncation a bit more cleverly so that
we can wrap with divs (sidestepping the bug) but slurping out the
rest of the text in case it ran off the end. (#78)
- Fix PREG_BACKTRACK_LIMIT_ERROR in HTMLPurifier_Filter_ExtractStyle.
Thanks @breathbath for contributing the report and fix (#120)
- Fix entity decoding algorithm to be more conservative about
decoding entities that are missing trailing semicolon.
To get old behavior, set %Core.LegacyEntityDecoder to true.
(#119)
- Workaround libxml bug when HTML tags are embedded inside
script tags. To disable workaround set %Core.AggressivelyRemoveScript
to false. (#83)
# By default, when a link has a target attribute associated
with it, we now also add rel="noopener" in order to
prevent the new window from being able to overwrite
the original frame. To disable this protection,
set %HTML.TargetNoopener to FALSE.
4.9.0 was cut on Git but never properly released; when we did the
real release we decided to skip this version number.
4.8.0, released 2016-07-16
# By default, when a link has a target attribute associated
with it, we now also add rel="noreferrer" in order to
prevent the new window from being able to overwrite
the original frame. To disable this protection,
set %HTML.TargetNoreferrer to FALSE.
! Full PHP 7 compatibility, the test suite is ALL GO.
! %CSS.AllowDuplicates permits duplicate CSS properties.
! Support for 'tel' URIs.
! Partial support for 'border-radius' properties when %CSS.AllowProprietary is true.
The slash syntax, i.e., 'border-radius: 2em 1em 4em / 0.5em 3em' is not
yet supported.
! %Attr.ID.HTML5 turns on HTML5-style ID handling.
- alt truncation could result in malformed UTF-8 sequence. Don't
truncate. Thanks Brandon Farber for reporting.
- Linkify regex is smarter, based off of Gruber's regex.
- IDNA supported natively on PHP 5.3 and later.
- Non all-numeric top-level names (e.g., foo.1f, 1f) are now
allowed.
- Minor bounds error fix to squash a PHP 7 notice.
- Support non-/tmp temporary directories for data:// validation
- Give a better error message when a user attempts to allow
ul/ol without allowing li.
- On some versions of PHP, the Serializer DefinitionCache could
infinite loop when the directory exists but is not listable. (#49)
- Don't match for <body> inside comments with
%Core.ConvertDocumentToFragment. (#67)
- SafeObject is now less case sensitive. (#57)
- AutoFormat.RemoveEmpty.Predicate now correctly renders in
web form. (#85)
4.7.0, released 2015-08-04
# opacity is now considered a "tricky" CSS property rather than a
proprietary one.

View file

@ -1,6 +1,5 @@
README
All about HTML Purifier
HTML Purifier [![Build Status](https://secure.travis-ci.org/ezyang/htmlpurifier.svg?branch=master)](http://travis-ci.org/ezyang/htmlpurifier)
=============
HTML Purifier is an HTML filtering solution that uses a unique combination
of robust whitelists and agressive parsing to ensure that not only are
@ -19,6 +18,12 @@ Places to go:
an in-depth installation guide.
* See WYSIWYG for information on editors like TinyMCE and FCKeditor
HTML Purifier can be found on the web at: http://htmlpurifier.org/
HTML Purifier can be found on the web at: [http://htmlpurifier.org/](http://htmlpurifier.org/)
vim: et sw=4 sts=4
## Installation
Package available on [Composer](https://packagist.org/packages/ezyang/htmlpurifier).
If you're using Composer to manage dependencies, you can use
$ composer require "ezyang/htmlpurifier": "dev-master"

View file

@ -32,7 +32,7 @@ Things to do as soon as possible:
FUTURE VERSIONS
---------------
4.8 release [OMG CONFIG PONIES]
4.9 release [OMG CONFIG PONIES]
! Fix Printer. It's from the old days when we didn't have decent XML classes
! Factor demo.php into a set of Printer classes, and then create a stub
file for users here (inside the actual HTML Purifier library)

View file

@ -1 +1 @@
4.7.0
4.9.3

View file

@ -1,4 +1,13 @@
HTML Purifier 4.7.0 is a bugfix release, collecting two years
worth of accumulated bug fixes. Highlighted bugfixes are updated
YouTube filter code, corrected rgb() CSS parsing, and one new
configuration option, %AutoFormat.RemoveEmpty.Predicate.
HTML Purifier 4.9.x is a maintenance release, collecting a year
of accumulated bug fixes plus a few new features. New features
include support for min/max-width/height CSS, and rgba/hsl/hsla
in color specifications. Major bugfixes include improvements
in the Serializer cache to avoid chmod'ing directories, better
entity decoding (we won't accidentally encode entities that occur
in URLs) and rel="noopener" on links with target attributes,
to prevent them from overwriting the original frame.
4.9.3 works around an infinite loop bug in PHP 7.1 with the opcode
cache (and has one other, minor bugfix, avoiding using autoloading
when testing for DOMDocument presence). If these bugs do not
affect you, you do not need to upgrade.

View file

@ -15,6 +15,9 @@
"require": {
"php": ">=5.2"
},
"require-dev": {
"simpletest/simpletest": "^1.1"
},
"autoload": {
"psr-0": { "HTMLPurifier": "library/" },
"files": ["library/HTMLPurifier.composer.php"]

View file

@ -7,7 +7,7 @@
* primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
* FILE, changes will be overwritten the next time the script is run.
*
* @version 4.7.0
* @version 4.9.3
*
* @warning
* You must *not* include any other HTML Purifier files before this file,
@ -137,6 +137,8 @@ require 'HTMLPurifier/AttrTransform/SafeObject.php';
require 'HTMLPurifier/AttrTransform/SafeParam.php';
require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
require 'HTMLPurifier/AttrTransform/TargetBlank.php';
require 'HTMLPurifier/AttrTransform/TargetNoopener.php';
require 'HTMLPurifier/AttrTransform/TargetNoreferrer.php';
require 'HTMLPurifier/AttrTransform/Textarea.php';
require 'HTMLPurifier/ChildDef/Chameleon.php';
require 'HTMLPurifier/ChildDef/Custom.php';
@ -175,6 +177,8 @@ require 'HTMLPurifier/HTMLModule/StyleAttribute.php';
require 'HTMLPurifier/HTMLModule/Tables.php';
require 'HTMLPurifier/HTMLModule/Target.php';
require 'HTMLPurifier/HTMLModule/TargetBlank.php';
require 'HTMLPurifier/HTMLModule/TargetNoopener.php';
require 'HTMLPurifier/HTMLModule/TargetNoreferrer.php';
require 'HTMLPurifier/HTMLModule/Text.php';
require 'HTMLPurifier/HTMLModule/Tidy.php';
require 'HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
@ -225,5 +229,6 @@ require 'HTMLPurifier/URIScheme/https.php';
require 'HTMLPurifier/URIScheme/mailto.php';
require 'HTMLPurifier/URIScheme/news.php';
require 'HTMLPurifier/URIScheme/nntp.php';
require 'HTMLPurifier/URIScheme/tel.php';
require 'HTMLPurifier/VarParser/Flexible.php';
require 'HTMLPurifier/VarParser/Native.php';

View file

@ -19,7 +19,7 @@
*/
/*
HTML Purifier 4.7.0 - Standards Compliant HTML Filtering
HTML Purifier 4.9.3 - Standards Compliant HTML Filtering
Copyright (C) 2006-2008 Edward Z. Yang
This library is free software; you can redistribute it and/or
@ -58,12 +58,12 @@ class HTMLPurifier
* Version of HTML Purifier.
* @type string
*/
public $version = '4.7.0';
public $version = '4.9.3';
/**
* Constant with version of HTML Purifier.
*/
const VERSION = '4.7.0';
const VERSION = '4.9.3';
/**
* Global configuration object.
@ -104,7 +104,7 @@ class HTMLPurifier
/**
* Initializes the purifier.
*
* @param HTMLPurifier_Config $config Optional HTMLPurifier_Config object
* @param HTMLPurifier_Config|mixed $config Optional HTMLPurifier_Config object
* for all instances of the purifier, if omitted, a default
* configuration is supplied (which can be overridden on a
* per-use basis).

View file

@ -131,6 +131,8 @@ require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/TargetBlank.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/TargetNoopener.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/TargetNoreferrer.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Textarea.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php';
@ -169,6 +171,8 @@ require_once $__dir . '/HTMLPurifier/HTMLModule/StyleAttribute.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tables.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Target.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/TargetBlank.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/TargetNoopener.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/TargetNoreferrer.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Text.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
@ -219,5 +223,6 @@ require_once $__dir . '/HTMLPurifier/URIScheme/https.php';
require_once $__dir . '/HTMLPurifier/URIScheme/mailto.php';
require_once $__dir . '/HTMLPurifier/URIScheme/news.php';
require_once $__dir . '/HTMLPurifier/URIScheme/nntp.php';
require_once $__dir . '/HTMLPurifier/URIScheme/tel.php';
require_once $__dir . '/HTMLPurifier/VarParser/Flexible.php';
require_once $__dir . '/HTMLPurifier/VarParser/Native.php';

View file

@ -19,8 +19,8 @@ class HTMLPurifier_Arborize
if ($token instanceof HTMLPurifier_Token_End) {
$token->start = null; // [MUT]
$r = array_pop($stack);
assert($r->name === $token->name);
assert(empty($token->attr));
//assert($r->name === $token->name);
//assert(empty($token->attr));
$r->endCol = $token->col;
$r->endLine = $token->line;
$r->endArmor = $token->armor;
@ -32,7 +32,7 @@ class HTMLPurifier_Arborize
$stack[] = $node;
}
}
assert(count($stack) == 1);
//assert(count($stack) == 1);
return $stack[0];
}

View file

@ -21,6 +21,11 @@ class HTMLPurifier_AttrCollections
* @param HTMLPurifier_HTMLModule[] $modules Hash array of HTMLPurifier_HTMLModule members
*/
public function __construct($attr_types, $modules)
{
$this->doConstruct($attr_types, $modules);
}
public function doConstruct($attr_types, $modules)
{
// load extensions from the modules
foreach ($modules as $module) {

View file

@ -86,7 +86,13 @@ abstract class HTMLPurifier_AttrDef
*/
protected function mungeRgb($string)
{
return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string);
$p = '\s*(\d+(\.\d+)?([%]?))\s*';
if (preg_match('/(rgba|hsla)\(/', $string)) {
return preg_replace('/(rgba|hsla)\('.$p.','.$p.','.$p.','.$p.'\)/', '\1(\2,\5,\8,\11)', $string);
}
return preg_replace('/(rgb|hsl)\('.$p.','.$p.','.$p.'\)/', '\1(\2,\5,\8)', $string);
}
/**

View file

@ -25,15 +25,42 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
$css = $this->parseCDATA($css);
$definition = $config->getCSSDefinition();
$allow_duplicates = $config->get("CSS.AllowDuplicates");
// we're going to break the spec and explode by semicolons.
// This is because semicolon rarely appears in escaped form
// Doing this is generally flaky but fast
// IT MIGHT APPEAR IN URIs, see HTMLPurifier_AttrDef_CSSURI
// for details
$declarations = explode(';', $css);
// According to the CSS2.1 spec, the places where a
// non-delimiting semicolon can appear are in strings
// escape sequences. So here is some dumb hack to
// handle quotes.
$len = strlen($css);
$accum = "";
$declarations = array();
$quoted = false;
for ($i = 0; $i < $len; $i++) {
$c = strcspn($css, ";'\"", $i);
$accum .= substr($css, $i, $c);
$i += $c;
if ($i == $len) break;
$d = $css[$i];
if ($quoted) {
$accum .= $d;
if ($d == $quoted) {
$quoted = false;
}
} else {
if ($d == ";") {
$declarations[] = $accum;
$accum = "";
} else {
$accum .= $d;
$quoted = $d;
}
}
}
if ($accum != "") $declarations[] = $accum;
$propvalues = array();
$new_declarations = '';
/**
* Name of the current CSS property being validated.
@ -83,7 +110,11 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
if ($result === false) {
continue;
}
$propvalues[$property] = $result;
if ($allow_duplicates) {
$new_declarations .= "$property:$result;";
} else {
$propvalues[$property] = $result;
}
}
$context->destroy('CurrentCSSProperty');
@ -92,7 +123,6 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
// slightly inefficient, but it's the only way of getting rid of
// duplicates. Perhaps config to optimize it, but not now.
$new_declarations = '';
foreach ($propvalues as $prop => $value) {
$new_declarations .= "$prop:$value;";
}

View file

@ -6,6 +6,16 @@
class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
{
/**
* @type HTMLPurifier_AttrDef_CSS_AlphaValue
*/
protected $alpha;
public function __construct()
{
$this->alpha = new HTMLPurifier_AttrDef_CSS_AlphaValue();
}
/**
* @param string $color
* @param HTMLPurifier_Config $config
@ -29,59 +39,104 @@ class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
return $colors[$lower];
}
if (strpos($color, 'rgb(') !== false) {
// rgb literal handling
if (preg_match('#(rgb|rgba|hsl|hsla)\(#', $color, $matches) === 1) {
$length = strlen($color);
if (strpos($color, ')') !== $length - 1) {
return false;
}
$triad = substr($color, 4, $length - 4 - 1);
$parts = explode(',', $triad);
if (count($parts) !== 3) {
// get used function : rgb, rgba, hsl or hsla
$function = $matches[1];
$parameters_size = 3;
$alpha_channel = false;
if (substr($function, -1) === 'a') {
$parameters_size = 4;
$alpha_channel = true;
}
/*
* Allowed types for values :
* parameter_position => [type => max_value]
*/
$allowed_types = array(
1 => array('percentage' => 100, 'integer' => 255),
2 => array('percentage' => 100, 'integer' => 255),
3 => array('percentage' => 100, 'integer' => 255),
);
$allow_different_types = false;
if (strpos($function, 'hsl') !== false) {
$allowed_types = array(
1 => array('integer' => 360),
2 => array('percentage' => 100),
3 => array('percentage' => 100),
);
$allow_different_types = true;
}
$values = trim(str_replace($function, '', $color), ' ()');
$parts = explode(',', $values);
if (count($parts) !== $parameters_size) {
return false;
}
$type = false; // to ensure that they're all the same type
$type = false;
$new_parts = array();
$i = 0;
foreach ($parts as $part) {
$i++;
$part = trim($part);
if ($part === '') {
return false;
}
$length = strlen($part);
if ($part[$length - 1] === '%') {
// handle percents
if (!$type) {
$type = 'percentage';
} elseif ($type !== 'percentage') {
// different check for alpha channel
if ($alpha_channel === true && $i === count($parts)) {
$result = $this->alpha->validate($part, $config, $context);
if ($result === false) {
return false;
}
$num = (float)substr($part, 0, $length - 1);
if ($num < 0) {
$num = 0;
}
if ($num > 100) {
$num = 100;
}
$new_parts[] = "$num%";
$new_parts[] = (string)$result;
continue;
}
if (substr($part, -1) === '%') {
$current_type = 'percentage';
} else {
// handle integers
if (!$type) {
$type = 'integer';
} elseif ($type !== 'integer') {
return false;
}
$num = (int)$part;
if ($num < 0) {
$num = 0;
}
if ($num > 255) {
$num = 255;
}
$new_parts[] = (string)$num;
$current_type = 'integer';
}
if (!array_key_exists($current_type, $allowed_types[$i])) {
return false;
}
if (!$type) {
$type = $current_type;
}
if ($allow_different_types === false && $type != $current_type) {
return false;
}
$max_value = $allowed_types[$i][$current_type];
if ($current_type == 'integer') {
// Return value between range 0 -> $max_value
$new_parts[] = (int)max(min($part, $max_value), 0);
} elseif ($current_type == 'percentage') {
$new_parts[] = (float)max(min(rtrim($part, '%'), $max_value), 0) . '%';
}
}
$new_triad = implode(',', $new_parts);
$color = "rgb($new_triad)";
$new_values = implode(',', $new_parts);
$color = $function . '(' . $new_values . ')';
} else {
// hexadecimal handling
if ($color[0] === '#') {
@ -100,6 +155,7 @@ class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
}
return $color;
}
}
// vim: et sw=4 sts=4

View file

@ -33,6 +33,9 @@ class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI
return false;
}
$uri_string = substr($uri_string, 4);
if (strlen($uri_string) == 0) {
return false;
}
$new_length = strlen($uri_string) - 1;
if ($uri_string[$new_length] != ')') {
return false;

View file

@ -72,18 +72,26 @@ class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef
// we purposely avoid using regex, hopefully this is faster
if (ctype_alpha($id)) {
$result = true;
} else {
if (!ctype_alpha(@$id[0])) {
if ($config->get('Attr.ID.HTML5') === true) {
if (preg_match('/[\t\n\x0b\x0c ]/', $id)) {
return false;
}
// primitive style of regexps, I suppose
$trim = trim(
$id,
'A..Za..z0..9:-._'
);
$result = ($trim === '');
} else {
if (ctype_alpha($id)) {
// OK
} else {
if (!ctype_alpha(@$id[0])) {
return false;
}
// primitive style of regexps, I suppose
$trim = trim(
$id,
'A..Za..z0..9:-._'
);
if ($trim !== '') {
return false;
}
}
}
$regexp = $config->get('Attr.IDBlacklistRegexp');
@ -91,14 +99,14 @@ class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef
return false;
}
if (!$this->selector && $result) {
if (!$this->selector) {
$id_accumulator->add($id);
}
// if no change was made to the ID, return the result
// else, return the new id if stripping whitespace made it
// valid, or return false.
return $result ? $id : false;
return $id;
}
}

View file

@ -76,24 +76,33 @@ class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
// fairly well supported.
$underscore = $config->get('Core.AllowHostnameUnderscore') ? '_' : '';
// Based off of RFC 1738, but amended so that
// as per RFC 3696, the top label need only not be all numeric.
// The productions describing this are:
$a = '[a-z]'; // alpha
$an = '[a-z0-9]'; // alphanum
$and = "[a-z0-9-$underscore]"; // alphanum | "-"
// domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
$domainlabel = "$an($and*$an)?";
// toplabel = alpha | alpha *( alphanum | "-" ) alphanum
$toplabel = "$a($and*$an)?";
$domainlabel = "$an(?:$and*$an)?";
// AMENDED as per RFC 3696
// toplabel = alphanum | alphanum *( alphanum | "-" ) alphanum
// side condition: not all numeric
$toplabel = "$an(?:$and*$an)?";
// hostname = *( domainlabel "." ) toplabel [ "." ]
if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
return $string;
if (preg_match("/^(?:$domainlabel\.)*($toplabel)\.?$/i", $string, $matches)) {
if (!ctype_digit($matches[1])) {
return $string;
}
}
// PHP 5.3 and later support this functionality natively
if (function_exists('idn_to_ascii')) {
$string = idn_to_ascii($string);
// If we have Net_IDNA2 support, we can support IRIs by
// punycoding them. (This is the most portable thing to do,
// since otherwise we have to assume browsers support
if ($config->get('Core.EnableIDNA')) {
} elseif ($config->get('Core.EnableIDNA')) {
$idna = new Net_IDNA2(array('encoding' => 'utf8', 'overlong' => false, 'strict' => true));
// we need to encode each period separately
$parts = explode('.', $string);
@ -114,13 +123,14 @@ class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
}
}
$string = implode('.', $new_parts);
if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
return $string;
}
} catch (Exception $e) {
// XXX error reporting
}
}
// Try again
if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
return $string;
}
return false;
}
}

View file

@ -32,8 +32,7 @@ class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform
if ($src) {
$alt = $config->get('Attr.DefaultImageAlt');
if ($alt === null) {
// truncate if the alt is too long
$attr['alt'] = substr(basename($attr['src']), 0, 40);
$attr['alt'] = basename($attr['src']);
} else {
$attr['alt'] = $alt;
}

View file

@ -0,0 +1,37 @@
<?php
// must be called POST validation
/**
* Adds rel="noopener" to any links which target a different window
* than the current one. This is used to prevent malicious websites
* from silently replacing the original window, which could be used
* to do phishing.
* This transform is controlled by %HTML.TargetNoopener.
*/
class HTMLPurifier_AttrTransform_TargetNoopener extends HTMLPurifier_AttrTransform
{
/**
* @param array $attr
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
public function transform($attr, $config, $context)
{
if (isset($attr['rel'])) {
$rels = explode(' ', $attr['rel']);
} else {
$rels = array();
}
if (isset($attr['target']) && !in_array('noopener', $rels)) {
$rels[] = 'noopener';
}
if (!empty($rels) || isset($attr['rel'])) {
$attr['rel'] = implode(' ', $rels);
}
return $attr;
}
}

View file

@ -0,0 +1,37 @@
<?php
// must be called POST validation
/**
* Adds rel="noreferrer" to any links which target a different window
* than the current one. This is used to prevent malicious websites
* from silently replacing the original window, which could be used
* to do phishing.
* This transform is controlled by %HTML.TargetNoreferrer.
*/
class HTMLPurifier_AttrTransform_TargetNoreferrer extends HTMLPurifier_AttrTransform
{
/**
* @param array $attr
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return array
*/
public function transform($attr, $config, $context)
{
if (isset($attr['rel'])) {
$rels = explode(' ', $attr['rel']);
} else {
$rels = array();
}
if (isset($attr['target']) && !in_array('noreferrer', $rels)) {
$rels[] = 'noreferrer';
}
if (!empty($rels) || isset($attr['rel'])) {
$attr['rel'] = implode(' ', $rels);
}
return $attr;
}
}

View file

@ -225,6 +225,10 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
);
$max = $config->get('CSS.MaxImgLength');
$this->info['min-width'] =
$this->info['max-width'] =
$this->info['min-height'] =
$this->info['max-height'] =
$this->info['width'] =
$this->info['height'] =
$max === null ?
@ -370,6 +374,19 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
);
$this->info['page-break-inside'] = new HTMLPurifier_AttrDef_Enum(array('auto', 'avoid'));
$border_radius = new HTMLPurifier_AttrDef_CSS_Composite(
array(
new HTMLPurifier_AttrDef_CSS_Percentage(true), // disallow negative
new HTMLPurifier_AttrDef_CSS_Length('0') // disallow negative
));
$this->info['border-top-left-radius'] =
$this->info['border-top-right-radius'] =
$this->info['border-bottom-right-radius'] =
$this->info['border-bottom-left-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 2);
// TODO: support SLASH syntax
$this->info['border-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 4);
}
/**

View file

@ -38,13 +38,19 @@ class HTMLPurifier_ChildDef_List extends HTMLPurifier_ChildDef
return false;
}
// if li is not allowed, delete parent node
if (!isset($config->getHTMLDefinition()->info['li'])) {
trigger_error("Cannot allow ul/ol without allowing li", E_USER_WARNING);
return false;
}
// the new set of children
$result = array();
// a little sanity check to make sure it's not ALL whitespace
$all_whitespace = true;
$current_li = false;
$current_li = null;
foreach ($children as $node) {
if (!empty($node->is_whitespace)) {
@ -65,7 +71,7 @@ class HTMLPurifier_ChildDef_List extends HTMLPurifier_ChildDef
// to handle non-list elements; non-list elements should
// not be appended to an existing li; only li created
// for non-list. This distinction is not currently made.
if ($current_li === false) {
if ($current_li === null) {
$current_li = new HTMLPurifier_Node_Element('li');
$result[] = $current_li;
}

View file

@ -203,7 +203,7 @@ class HTMLPurifier_ChildDef_Table extends HTMLPurifier_ChildDef
$current_tr_tbody->children[] = $node;
break;
case '#PCDATA':
assert($node->is_whitespace);
//assert($node->is_whitespace);
if ($current_tr_tbody === null) {
$ret[] = $node;
} else {

View file

@ -21,7 +21,7 @@ class HTMLPurifier_Config
* HTML Purifier's version
* @type string
*/
public $version = '4.7.0';
public $version = '4.9.3';
/**
* Whether or not to automatically finalize
@ -333,7 +333,7 @@ class HTMLPurifier_Config
}
// Raw type might be negative when using the fully optimized form
// of stdclass, which indicates allow_null == true
// of stdClass, which indicates allow_null == true
$rtype = is_int($def) ? $def : $def->type;
if ($rtype < 0) {
$type = -$rtype;

View file

@ -24,11 +24,11 @@ class HTMLPurifier_ConfigSchema
*
* array(
* 'Namespace' => array(
* 'Directive' => new stdclass(),
* 'Directive' => new stdClass(),
* )
* )
*
* The stdclass may have the following properties:
* The stdClass may have the following properties:
*
* - If isAlias isn't set:
* - type: Integer type of directive, see HTMLPurifier_VarParser for definitions
@ -39,8 +39,8 @@ class HTMLPurifier_ConfigSchema
* - namespace: Namespace this directive aliases to
* - name: Directive name this directive aliases to
*
* In certain degenerate cases, stdclass will actually be an integer. In
* that case, the value is equivalent to an stdclass with the type
* In certain degenerate cases, stdClass will actually be an integer. In
* that case, the value is equivalent to an stdClass with the type
* property set to the integer. If the integer is negative, type is
* equal to the absolute value of integer, and allow_null is true.
*
@ -105,7 +105,7 @@ class HTMLPurifier_ConfigSchema
*/
public function add($key, $default, $type, $allow_null)
{
$obj = new stdclass();
$obj = new stdClass();
$obj->type = is_int($type) ? $type : HTMLPurifier_VarParser::$types[$type];
if ($allow_null) {
$obj->allow_null = true;
@ -152,14 +152,14 @@ class HTMLPurifier_ConfigSchema
*/
public function addAlias($key, $new_key)
{
$obj = new stdclass;
$obj = new stdClass;
$obj->key = $new_key;
$obj->isAlias = true;
$this->info[$key] = $obj;
}
/**
* Replaces any stdclass that only has the type property with type integer.
* Replaces any stdClass that only has the type property with type integer.
*/
public function postProcess()
{

View file

@ -0,0 +1,10 @@
Attr.ID.HTML5
TYPE: bool/null
DEFAULT: null
VERSION: 4.8.0
--DESCRIPTION--
In HTML5, restrictions on the format of the id attribute have been significantly
relaxed, such that any string is valid so long as it contains no spaces and
is at least one character. In lieu of a general HTML5 compatibility flag,
set this configuration directive to true to use the relaxed rules.
--# vim: et sw=4 sts=4

View file

@ -0,0 +1,11 @@
CSS.AllowDuplicates
TYPE: bool
DEFAULT: false
VERSION: 4.8.0
--DESCRIPTION--
<p>
By default, HTML Purifier removes duplicate CSS properties,
like <code>color:red; color:blue</code>. If this is set to
true, duplicate properties are allowed.
</p>
--# vim: et sw=4 sts=4

View file

@ -1,5 +1,5 @@
Cache.SerializerPermissions
TYPE: int
TYPE: int/null
VERSION: 4.3.0
DEFAULT: 0755
--DESCRIPTION--
@ -8,4 +8,9 @@ DEFAULT: 0755
Directory permissions of the files and directories created inside
the DefinitionCache/Serializer or other custom serializer path.
</p>
<p>
In HTML Purifier 4.8.0, this also supports <code>NULL</code>,
which means that no chmod'ing or directory creation shall
occur.
</p>
--# vim: et sw=4 sts=4

View file

@ -0,0 +1,16 @@
Core.AggressivelyRemoveScript
TYPE: bool
VERSION: 4.9.0
DEFAULT: true
--DESCRIPTION--
<p>
This directive enables aggressive pre-filter removal of
script tags. This is not necessary for security,
but it can help work around a bug in libxml where embedded
HTML elements inside script sections cause the parser to
choke. To revert to pre-4.9.0 behavior, set this to false.
This directive has no effect if %Core.Trusted is true,
%Core.RemoveScriptContents is false, or %Core.HiddenElements
does not contain script.
</p>
--# vim: et sw=4 sts=4

View file

@ -0,0 +1,36 @@
Core.LegacyEntityDecoder
TYPE: bool
VERSION: 4.9.0
DEFAULT: false
--DESCRIPTION--
<p>
Prior to HTML Purifier 4.9.0, entities were decoded by performing
a global search replace for all entities whose decoded versions
did not have special meanings under HTML, and replaced them with
their decoded versions. We would match all entities, even if they did
not have a trailing semicolon, but only if there weren't any trailing
alphanumeric characters.
</p>
<table>
<tr><th>Original</th><th>Text</th><th>Attribute</th></tr>
<tr><td>&amp;yen;</td><td>&yen;</td><td>&yen;</td></tr>
<tr><td>&amp;yen</td><td>&yen;</td><td>&yen;</td></tr>
<tr><td>&amp;yena</td><td>&amp;yena</td><td>&amp;yena</td></tr>
<tr><td>&amp;yen=</td><td>&yen;=</td><td>&yen;=</td></tr>
</table>
<p>
In HTML Purifier 4.9.0, we changed the behavior of entity parsing
to match entities that had missing trailing semicolons in less
cases, to more closely match HTML5 parsing behavior:
</p>
<table>
<tr><th>Original</th><th>Text</th><th>Attribute</th></tr>
<tr><td>&amp;yen;</td><td>&yen;</td><td>&yen;</td></tr>
<tr><td>&amp;yen</td><td>&yen;</td><td>&yen;</td></tr>
<tr><td>&amp;yena</td><td>&yen;a</td><td>&amp;yena</td></tr>
<tr><td>&amp;yen=</td><td>&yen;=</td><td>&amp;yen=</td></tr>
</table>
<p>
This flag reverts back to pre-HTML Purifier 4.9.0 behavior.
</p>
--# vim: et sw=4 sts=4

View file

@ -0,0 +1,10 @@
--# vim: et sw=4 sts=4
HTML.TargetNoopener
TYPE: bool
VERSION: 4.8.0
DEFAULT: TRUE
--DESCRIPTION--
If enabled, noopener rel attributes are added to links which have
a target attribute associated with them. This prevents malicious
destinations from overwriting the original window.
--# vim: et sw=4 sts=4

View file

@ -0,0 +1,9 @@
HTML.TargetNoreferrer
TYPE: bool
VERSION: 4.8.0
DEFAULT: TRUE
--DESCRIPTION--
If enabled, noreferrer rel attributes are added to links which have
a target attribute associated with them. This prevents malicious
destinations from overwriting the original window.
--# vim: et sw=4 sts=4

View file

@ -8,6 +8,7 @@ array (
'ftp' => true,
'nntp' => true,
'news' => true,
'tel' => true,
)
--DESCRIPTION--
Whitelist that defines the schemes that a URI is allowed to have. This

View file

@ -1,5 +1,5 @@
URI.DefaultScheme
TYPE: string
TYPE: string/null
DEFAULT: 'http'
--DESCRIPTION--
@ -7,4 +7,9 @@ DEFAULT: 'http'
Defines through what scheme the output will be served, in order to
select the proper object validator when no scheme information is present.
</p>
<p>
Starting with HTML Purifier 4.9.0, the default scheme can be null, in
which case we reject all URIs which do not have explicit schemes.
</p>
--# vim: et sw=4 sts=4

View file

@ -118,7 +118,7 @@ abstract class HTMLPurifier_DefinitionCache
/**
* Clears all expired (older version or revision) objects from cache
* @note Be carefuly implementing this method as flush. Flush must
* @note Be careful implementing this method as flush. Flush must
* not interfere with other Definition types, and cleanup()
* should not be repeatedly called by userland code.
* @param HTMLPurifier_Config $config

View file

@ -97,6 +97,12 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
}
$dir = $this->generateDirectoryPath($config);
$dh = opendir($dir);
// Apparently, on some versions of PHP, readdir will return
// an empty string if you pass an invalid argument to readdir.
// So you need this test. See #49.
if (false === $dh) {
return false;
}
while (false !== ($filename = readdir($dh))) {
if (empty($filename)) {
continue;
@ -106,6 +112,8 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
}
unlink($dir . '/' . $filename);
}
closedir($dh);
return true;
}
/**
@ -119,6 +127,10 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
}
$dir = $this->generateDirectoryPath($config);
$dh = opendir($dir);
// See #49 (and above).
if (false === $dh) {
return false;
}
while (false !== ($filename = readdir($dh))) {
if (empty($filename)) {
continue;
@ -131,6 +143,8 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
unlink($dir . '/' . $filename);
}
}
closedir($dh);
return true;
}
/**
@ -186,11 +200,9 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
if ($result !== false) {
// set permissions of the new file (no execute)
$chmod = $config->get('Cache.SerializerPermissions');
if (!$chmod) {
$chmod = 0644; // invalid config or simpletest
if ($chmod !== null) {
chmod($file, $chmod & 0666);
}
$chmod = $chmod & 0666;
chmod($file, $chmod);
}
return $result;
}
@ -204,8 +216,10 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
{
$directory = $this->generateDirectoryPath($config);
$chmod = $config->get('Cache.SerializerPermissions');
if (!$chmod) {
$chmod = 0755; // invalid config or simpletest
if ($chmod === null) {
// TODO: This races
if (is_dir($directory)) return true;
return mkdir($directory);
}
if (!is_dir($directory)) {
$base = $this->generateBaseDirectoryPath($config);
@ -219,15 +233,16 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
} elseif (!$this->_testPermissions($base, $chmod)) {
return false;
}
mkdir($directory, $chmod);
if (!$this->_testPermissions($directory, $chmod)) {
if (!mkdir($directory, $chmod)) {
trigger_error(
'Base directory ' . $base . ' does not exist,
please create or change using %Cache.SerializerPath',
'Could not create directory ' . $directory . '',
E_USER_WARNING
);
return false;
}
if (!$this->_testPermissions($directory, $chmod)) {
return false;
}
} elseif (!$this->_testPermissions($directory, $chmod)) {
return false;
}
@ -256,7 +271,7 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
);
return false;
}
if (function_exists('posix_getuid')) {
if (function_exists('posix_getuid') && $chmod !== null) {
// POSIX system, we can give more specific advice
if (fileowner($dir) === posix_getuid()) {
// we can chmod it ourselves

View file

@ -101,6 +101,14 @@ class HTMLPurifier_Encoder
* It will parse according to UTF-8 and return a valid UTF8 string, with
* non-SGML codepoints excluded.
*
* Specifically, it will permit:
* \x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}
* Source: https://www.w3.org/TR/REC-xml/#NT-Char
* Arguably this function should be modernized to the HTML5 set
* of allowed characters:
* https://www.w3.org/TR/html5/syntax.html#preprocessing-the-input-stream
* which simultaneously expand and restrict the set of allowed characters.
*
* @param string $str The string to clean
* @param bool $force_php
* @return string
@ -122,15 +130,12 @@ class HTMLPurifier_Encoder
* function that needs to be able to understand UTF-8 characters.
* As of right now, only smart lossless character encoding converters
* would need that, and I'm probably not going to implement them.
* Once again, PHP 6 should solve all our problems.
*/
public static function cleanUTF8($str, $force_php = false)
{
// UTF-8 validity is checked since PHP 4.3.5
// This is an optimization: if the string is already valid UTF-8, no
// need to do PHP stuff. 99% of the time, this will be the case.
// The regexp matches the XML char production, as well as well as excluding
// non-SGML codepoints U+007F to U+009F
if (preg_match(
'/^[\x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]*$/Du',
$str
@ -255,6 +260,7 @@ class HTMLPurifier_Encoder
// 7F-9F is not strictly prohibited by XML,
// but it is non-SGML, and thus we don't allow it
(0xA0 <= $mUcs4 && 0xD7FF >= $mUcs4) ||
(0xE000 <= $mUcs4 && 0xFFFD >= $mUcs4) ||
(0x10000 <= $mUcs4 && 0x10FFFF >= $mUcs4)
)
) {

View file

@ -16,6 +16,138 @@ class HTMLPurifier_EntityParser
*/
protected $_entity_lookup;
/**
* Callback regex string for entities in text.
* @type string
*/
protected $_textEntitiesRegex;
/**
* Callback regex string for entities in attributes.
* @type string
*/
protected $_attrEntitiesRegex;
/**
* Tests if the beginning of a string is a semi-optional regex
*/
protected $_semiOptionalPrefixRegex;
public function __construct() {
// From
// http://stackoverflow.com/questions/15532252/why-is-reg-being-rendered-as-without-the-bounding-semicolon
$semi_optional = "quot|QUOT|lt|LT|gt|GT|amp|AMP|AElig|Aacute|Acirc|Agrave|Aring|Atilde|Auml|COPY|Ccedil|ETH|Eacute|Ecirc|Egrave|Euml|Iacute|Icirc|Igrave|Iuml|Ntilde|Oacute|Ocirc|Ograve|Oslash|Otilde|Ouml|REG|THORN|Uacute|Ucirc|Ugrave|Uuml|Yacute|aacute|acirc|acute|aelig|agrave|aring|atilde|auml|brvbar|ccedil|cedil|cent|copy|curren|deg|divide|eacute|ecirc|egrave|eth|euml|frac12|frac14|frac34|iacute|icirc|iexcl|igrave|iquest|iuml|laquo|macr|micro|middot|nbsp|not|ntilde|oacute|ocirc|ograve|ordf|ordm|oslash|otilde|ouml|para|plusmn|pound|raquo|reg|sect|shy|sup1|sup2|sup3|szlig|thorn|times|uacute|ucirc|ugrave|uml|uuml|yacute|yen|yuml";
// NB: three empty captures to put the fourth match in the right
// place
$this->_semiOptionalPrefixRegex = "/&()()()($semi_optional)/";
$this->_textEntitiesRegex =
'/&(?:'.
// hex
'[#]x([a-fA-F0-9]+);?|'.
// dec
'[#]0*(\d+);?|'.
// string (mandatory semicolon)
// NB: order matters: match semicolon preferentially
'([A-Za-z_:][A-Za-z0-9.\-_:]*);|'.
// string (optional semicolon)
"($semi_optional)".
')/';
$this->_attrEntitiesRegex =
'/&(?:'.
// hex
'[#]x([a-fA-F0-9]+);?|'.
// dec
'[#]0*(\d+);?|'.
// string (mandatory semicolon)
// NB: order matters: match semicolon preferentially
'([A-Za-z_:][A-Za-z0-9.\-_:]*);|'.
// string (optional semicolon)
// don't match if trailing is equals or alphanumeric (URL
// like)
"($semi_optional)(?![=;A-Za-z0-9])".
')/';
}
/**
* Substitute entities with the parsed equivalents. Use this on
* textual data in an HTML document (as opposed to attributes.)
*
* @param string $string String to have entities parsed.
* @return string Parsed string.
*/
public function substituteTextEntities($string)
{
return preg_replace_callback(
$this->_textEntitiesRegex,
array($this, 'entityCallback'),
$string
);
}
/**
* Substitute entities with the parsed equivalents. Use this on
* attribute contents in documents.
*
* @param string $string String to have entities parsed.
* @return string Parsed string.
*/
public function substituteAttrEntities($string)
{
return preg_replace_callback(
$this->_attrEntitiesRegex,
array($this, 'entityCallback'),
$string
);
}
/**
* Callback function for substituteNonSpecialEntities() that does the work.
*
* @param array $matches PCRE matches array, with 0 the entire match, and
* either index 1, 2 or 3 set with a hex value, dec value,
* or string (respectively).
* @return string Replacement string.
*/
protected function entityCallback($matches)
{
$entity = $matches[0];
$hex_part = @$matches[1];
$dec_part = @$matches[2];
$named_part = empty($matches[3]) ? @$matches[4] : $matches[3];
if ($hex_part !== NULL && $hex_part !== "") {
return HTMLPurifier_Encoder::unichr(hexdec($hex_part));
} elseif ($dec_part !== NULL && $dec_part !== "") {
return HTMLPurifier_Encoder::unichr((int) $dec_part);
} else {
if (!$this->_entity_lookup) {
$this->_entity_lookup = HTMLPurifier_EntityLookup::instance();
}
if (isset($this->_entity_lookup->table[$named_part])) {
return $this->_entity_lookup->table[$named_part];
} else {
// exact match didn't match anything, so test if
// any of the semicolon optional match the prefix.
// Test that this is an EXACT match is important to
// prevent infinite loop
if (!empty($matches[3])) {
return preg_replace_callback(
$this->_semiOptionalPrefixRegex,
array($this, 'entityCallback'),
$entity
);
}
return $entity;
}
}
}
// LEGACY CODE BELOW
/**
* Callback regex string for parsing entities.
* @type string
@ -144,7 +276,7 @@ class HTMLPurifier_EntityParser
$entity;
} else {
return isset($this->_special_ent2dec[$matches[3]]) ?
$this->_special_ent2dec[$matches[3]] :
$this->_special_dec2str[$this->_special_ent2dec[$matches[3]]] :
$entity;
}
}

View file

@ -95,7 +95,10 @@ class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
if ($tidy !== null) {
$this->_tidy = $tidy;
}
$html = preg_replace_callback('#<style(?:\s.*)?>(.+)</style>#isU', array($this, 'styleCallback'), $html);
// NB: this must be NON-greedy because if we have
// <style>foo</style> <style>bar</style>
// we must not grab foo</style> <style>bar
$html = preg_replace_callback('#<style(?:\s.*)?>(.*)<\/style>#isU', array($this, 'styleCallback'), $html);
$style_blocks = $this->_styleMatches;
$this->_styleMatches = array(); // reset
$context->register('StyleBlocks', $style_blocks); // $context must not be reused

View file

@ -146,7 +146,7 @@ class HTMLPurifier_Generator
$attr = $this->generateAttributes($token->attr, $token->name);
if ($this->_flashCompat) {
if ($token->name == "object") {
$flash = new stdclass();
$flash = new stdClass();
$flash->attr = $token->attr;
$flash->param = array();
$this->_flashStack[] = $flash;

View file

@ -0,0 +1,21 @@
<?php
/**
* Module adds the target-based noopener attribute transformation to a tags. It
* is enabled by HTML.TargetNoopener
*/
class HTMLPurifier_HTMLModule_TargetNoopener extends HTMLPurifier_HTMLModule
{
/**
* @type string
*/
public $name = 'TargetNoopener';
/**
* @param HTMLPurifier_Config $config
*/
public function setup($config) {
$a = $this->addBlankElement('a');
$a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetNoopener();
}
}

View file

@ -0,0 +1,21 @@
<?php
/**
* Module adds the target-based noreferrer attribute transformation to a tags. It
* is enabled by HTML.TargetNoreferrer
*/
class HTMLPurifier_HTMLModule_TargetNoreferrer extends HTMLPurifier_HTMLModule
{
/**
* @type string
*/
public $name = 'TargetNoreferrer';
/**
* @param HTMLPurifier_Config $config
*/
public function setup($config) {
$a = $this->addBlankElement('a');
$a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetNoreferrer();
}
}

View file

@ -271,6 +271,14 @@ class HTMLPurifier_HTMLModuleManager
if ($config->get('HTML.TargetBlank')) {
$modules[] = 'TargetBlank';
}
// NB: HTML.TargetNoreferrer and HTML.TargetNoopener must be AFTER HTML.TargetBlank
// so that its post-attr-transform gets run afterwards.
if ($config->get('HTML.TargetNoreferrer')) {
$modules[] = 'TargetNoreferrer';
}
if ($config->get('HTML.TargetNoopener')) {
$modules[] = 'TargetNoopener';
}
// merge in custom modules
$modules = array_merge($modules, $this->userModules);

View file

@ -31,9 +31,14 @@ class HTMLPurifier_Injector_Linkify extends HTMLPurifier_Injector
return;
}
// there is/are URL(s). Let's split the string:
// Note: this regex is extremely permissive
$bits = preg_split('#((?:https?|ftp)://[^\s\'",<>()]+)#Su', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
// there is/are URL(s). Let's split the string.
// We use this regex:
// https://gist.github.com/gruber/249502
// but with @cscott's backtracking fix and also
// the Unicode characters un-Unicodified.
$bits = preg_split(
'/\\b((?:[a-z][\\w\\-]+:(?:\\/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}\\/)(?:[^\\s()<>]|\\((?:[^\\s()<>]|(?:\\([^\\s()<>]+\\)))*\\))+(?:\\((?:[^\\s()<>]|(?:\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:\'".,<>?\x{00ab}\x{00bb}\x{201c}\x{201d}\x{2018}\x{2019}]))/iu',
$token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
$token = array();

View file

@ -46,6 +46,12 @@ class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
$this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp');
$this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions');
$this->exclude = $config->get('AutoFormat.RemoveEmpty.Predicate');
foreach ($this->exclude as $key => $attrs) {
if (!is_array($attrs)) {
// HACK, see HTMLPurifier/Printer/ConfigForm.php
$this->exclude[$key] = explode(';', $attrs);
}
}
$this->attrValidator = new HTMLPurifier_AttrValidator();
}

View file

@ -36,6 +36,7 @@ class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector
);
/**
* These are all lower-case keys.
* @type array
*/
protected $allowedParam = array(
@ -43,7 +44,7 @@ class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector
'movie' => true,
'flashvars' => true,
'src' => true,
'allowFullScreen' => true, // if omitted, assume to be 'false'
'allowfullscreen' => true, // if omitted, assume to be 'false'
);
/**
@ -93,9 +94,11 @@ class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector
$token->attr['name'] === $this->addParam[$n]) {
// keep token, and add to param stack
$this->paramStack[$i][$n] = true;
} elseif (isset($this->allowedParam[$n])) {
} elseif (isset($this->allowedParam[strtolower($n)])) {
// keep token, don't do anything to it
// (could possibly check for duplicates here)
// Note: In principle, parameters should be case sensitive.
// But it seems they are not really; so accept any case.
} else {
$token = false;
}

View file

@ -96,7 +96,7 @@ class HTMLPurifier_Lexer
break;
}
if (class_exists('DOMDocument') &&
if (class_exists('DOMDocument', false) &&
method_exists('DOMDocument', 'loadHTML') &&
!extension_loaded('domxml')
) {
@ -169,21 +169,24 @@ class HTMLPurifier_Lexer
'&#x27;' => "'"
);
public function parseText($string, $config) {
return $this->parseData($string, false, $config);
}
public function parseAttr($string, $config) {
return $this->parseData($string, true, $config);
}
/**
* Parses special entities into the proper characters.
*
* This string will translate escaped versions of the special characters
* into the correct ones.
*
* @warning
* You should be able to treat the output of this function as
* completely parsed, but that's only because all other entities should
* have been handled previously in substituteNonSpecialEntities()
*
* @param string $string String character data to be parsed.
* @return string Parsed character data.
*/
public function parseData($string)
public function parseData($string, $is_attr, $config)
{
// following functions require at least one character
if ($string === '') {
@ -209,7 +212,15 @@ class HTMLPurifier_Lexer
}
// hmm... now we have some uncommon entities. Use the callback.
$string = $this->_entity_parser->substituteSpecialEntities($string);
if ($config->get('Core.LegacyEntityDecoder')) {
$string = $this->_entity_parser->substituteSpecialEntities($string);
} else {
if ($is_attr) {
$string = $this->_entity_parser->substituteAttrEntities($string);
} else {
$string = $this->_entity_parser->substituteTextEntities($string);
}
}
return $string;
}
@ -323,7 +334,9 @@ class HTMLPurifier_Lexer
}
// expand entities that aren't the big five
$html = $this->_entity_parser->substituteNonSpecialEntities($html);
if ($config->get('Core.LegacyEntityDecoder')) {
$html = $this->_entity_parser->substituteNonSpecialEntities($html);
}
// clean into wellformed UTF-8 string for an SGML context: this has
// to be done after entity expansion because the entities sometimes
@ -335,6 +348,13 @@ class HTMLPurifier_Lexer
$html = preg_replace('#<\?.+?\?>#s', '', $html);
}
$hidden_elements = $config->get('Core.HiddenElements');
if ($config->get('Core.AggressivelyRemoveScript') &&
!($config->get('HTML.Trusted') || !$config->get('Core.RemoveScriptContents')
|| empty($hidden_elements["script"]))) {
$html = preg_replace('#<script[^>]*>.*?</script>#i', '', $html);
}
return $html;
}
@ -345,12 +365,17 @@ class HTMLPurifier_Lexer
public function extractBody($html)
{
$matches = array();
$result = preg_match('!<body[^>]*>(.*)</body>!is', $html, $matches);
$result = preg_match('|(.*?)<body[^>]*>(.*)</body>|is', $html, $matches);
if ($result) {
return $matches[1];
} else {
return $html;
// Make sure it's not in a comment
$comment_start = strrpos($matches[1], '<!--');
$comment_end = strrpos($matches[1], '-->');
if ($comment_start === false ||
($comment_end !== false && $comment_end > $comment_start)) {
return $matches[2];
}
}
return $html;
}
}

View file

@ -72,12 +72,20 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
$doc->loadHTML($html);
restore_error_handler();
$body = $doc->getElementsByTagName('html')->item(0)-> // <html>
getElementsByTagName('body')->item(0); // <body>
$div = $body->getElementsByTagName('div')->item(0); // <div>
$tokens = array();
$this->tokenizeDOM(
$doc->getElementsByTagName('html')->item(0)-> // <html>
getElementsByTagName('body')->item(0), // <body>
$tokens
);
$this->tokenizeDOM($div, $tokens, $config);
// If the div has a sibling, that means we tripped across
// a premature </div> tag. So remove the div we parsed,
// and then tokenize the rest of body. We can't tokenize
// the sibling directly as we'll lose the tags in that case.
if ($div->nextSibling) {
$body->removeChild($div);
$this->tokenizeDOM($body, $tokens, $config);
}
return $tokens;
}
@ -88,7 +96,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
* @param HTMLPurifier_Token[] $tokens Array-list of already tokenized tokens.
* @return HTMLPurifier_Token of node appended to previously passed tokens.
*/
protected function tokenizeDOM($node, &$tokens)
protected function tokenizeDOM($node, &$tokens, $config)
{
$level = 0;
$nodes = array($level => new HTMLPurifier_Queue(array($node)));
@ -97,7 +105,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
while (!$nodes[$level]->isEmpty()) {
$node = $nodes[$level]->shift(); // FIFO
$collect = $level > 0 ? true : false;
$needEndingTag = $this->createStartNode($node, $tokens, $collect);
$needEndingTag = $this->createStartNode($node, $tokens, $collect, $config);
if ($needEndingTag) {
$closingNodes[$level][] = $node;
}
@ -127,7 +135,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
* @return bool if the token needs an endtoken
* @todo data and tagName properties don't seem to exist in DOMNode?
*/
protected function createStartNode($node, &$tokens, $collect)
protected function createStartNode($node, &$tokens, $collect, $config)
{
// intercept non element nodes. WE MUST catch all of them,
// but we're not getting the character reference nodes because
@ -151,7 +159,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
}
}
}
$tokens[] = $this->factory->createText($this->parseData($data));
$tokens[] = $this->factory->createText($this->parseText($data, $config));
return false;
} elseif ($node->nodeType === XML_COMMENT_NODE) {
// this is code is only invoked for comments in script/style in versions
@ -252,7 +260,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
* @param HTMLPurifier_Context $context
* @return string
*/
protected function wrapHTML($html, $config, $context)
protected function wrapHTML($html, $config, $context, $use_div = true)
{
$def = $config->getDefinition('HTML');
$ret = '';
@ -271,7 +279,11 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
$ret .= '<html><head>';
$ret .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
// No protection if $html contains a stray </div>!
$ret .= '</head><body>' . $html . '</body></html>';
$ret .= '</head><body>';
if ($use_div) $ret .= '<div>';
$ret .= $html;
if ($use_div) $ret .= '</div>';
$ret .= '</body></html>';
return $ret;
}
}

View file

@ -129,12 +129,12 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
// We are not inside tag and there still is another tag to parse
$token = new
HTMLPurifier_Token_Text(
$this->parseData(
$this->parseText(
substr(
$html,
$cursor,
$position_next_lt - $cursor
)
), $config
)
);
if ($maintain_line_numbers) {
@ -154,11 +154,11 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
// Create Text of rest of string
$token = new
HTMLPurifier_Token_Text(
$this->parseData(
$this->parseText(
substr(
$html,
$cursor
)
), $config
)
);
if ($maintain_line_numbers) {
@ -324,8 +324,8 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
$token = new
HTMLPurifier_Token_Text(
'<' .
$this->parseData(
substr($html, $cursor)
$this->parseText(
substr($html, $cursor), $config
)
);
if ($maintain_line_numbers) {
@ -429,7 +429,7 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
if ($value === false) {
$value = '';
}
return array($key => $this->parseData($value));
return array($key => $this->parseAttr($value, $config));
}
// setup loop environment
@ -518,7 +518,7 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
if ($value === false) {
$value = '';
}
$array[$key] = $this->parseData($value);
$array[$key] = $this->parseAttr($value, $config);
$cursor++;
} else {
// boolattr

View file

@ -21,7 +21,7 @@ class HTMLPurifier_Lexer_PH5P extends HTMLPurifier_Lexer_DOMLex
public function tokenizeHTML($html, $config, $context)
{
$new_html = $this->normalize($html, $config, $context);
$new_html = $this->wrapHTML($new_html, $config, $context);
$new_html = $this->wrapHTML($new_html, $config, $context, false /* no div */);
try {
$parser = new HTML5($new_html);
$doc = $parser->save();
@ -34,9 +34,9 @@ class HTMLPurifier_Lexer_PH5P extends HTMLPurifier_Lexer_DOMLex
$tokens = array();
$this->tokenizeDOM(
$doc->getElementsByTagName('html')->item(0)-> // <html>
getElementsByTagName('body')->item(0) // <body>
getElementsByTagName('body')->item(0) // <body>
,
$tokens
$tokens, $config
);
return $tokens;
}
@ -1515,6 +1515,7 @@ class HTML5
// Consume the maximum number of characters possible, with the
// consumed characters case-sensitively matching one of the
// identifiers in the first column of the entities table.
$e_name = $this->characters('0-9A-Za-z;', $this->char + 1);
$len = strlen($e_name);
@ -1547,7 +1548,7 @@ class HTML5
// Return a character token for the character corresponding to the
// entity name (as given by the second column of the entities table).
return html_entity_decode('&' . $entity . ';', ENT_QUOTES, 'UTF-8');
return html_entity_decode('&' . rtrim($entity, ';') . ';', ENT_QUOTES, 'UTF-8');
}
private function emitToken($token)

View file

@ -327,6 +327,10 @@ class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer
case HTMLPurifier_VarParser::HASH:
$nvalue = '';
foreach ($value as $i => $v) {
if (is_array($v)) {
// HACK
$v = implode(";", $v);
}
$nvalue .= "$i:$v" . PHP_EOL;
}
$value = $nvalue;

View file

@ -165,7 +165,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
if (empty($zipper->front)) break;
$token = $zipper->prev($token);
// indicate that other injectors should not process this token,
// but we need to reprocess it
// but we need to reprocess it. See Note [Injector skips]
unset($token->skip[$i]);
$token->rewind = $i;
if ($token instanceof HTMLPurifier_Token_Start) {
@ -210,6 +210,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
if ($token instanceof HTMLPurifier_Token_Text) {
foreach ($this->injectors as $i => $injector) {
if (isset($token->skip[$i])) {
// See Note [Injector skips]
continue;
}
if ($token->rewind !== null && $token->rewind !== $i) {
@ -367,6 +368,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
if ($ok) {
foreach ($this->injectors as $i => $injector) {
if (isset($token->skip[$i])) {
// See Note [Injector skips]
continue;
}
if ($token->rewind !== null && $token->rewind !== $i) {
@ -422,6 +424,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
$token->start = $current_parent;
foreach ($this->injectors as $i => $injector) {
if (isset($token->skip[$i])) {
// See Note [Injector skips]
continue;
}
if ($token->rewind !== null && $token->rewind !== $i) {
@ -534,12 +537,17 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
*/
protected function processToken($token, $injector = -1)
{
// Zend OpCache miscompiles $token = array($token), so
// avoid this pattern. See: https://github.com/ezyang/htmlpurifier/issues/108
// normalize forms of token
if (is_object($token)) {
$token = array(1, $token);
$tmp = $token;
$token = array(1, $tmp);
}
if (is_int($token)) {
$token = array($token);
$tmp = $token;
$token = array($tmp);
}
if ($token === false) {
$token = array(1);
@ -561,7 +569,12 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
list($old, $r) = $this->zipper->splice($this->token, $delete, $token);
if ($injector > -1) {
// determine appropriate skips
// See Note [Injector skips]
// Determine appropriate skips. Here's what the code does:
// *If* we deleted one or more tokens, copy the skips
// of those tokens into the skips of the new tokens (in $token).
// Also, mark the newly inserted tokens as having come from
// $injector.
$oldskip = isset($old[0]) ? $old[0]->skip : array();
foreach ($token as $object) {
$object->skip = $oldskip;
@ -597,4 +610,50 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
}
}
// Note [Injector skips]
// ~~~~~~~~~~~~~~~~~~~~~
// When I originally designed this class, the idea behind the 'skip'
// property of HTMLPurifier_Token was to help avoid infinite loops
// in injector processing. For example, suppose you wrote an injector
// that bolded swear words. Naively, you might write it so that
// whenever you saw ****, you replaced it with <strong>****</strong>.
//
// When this happens, we will reprocess all of the tokens with the
// other injectors. Now there is an opportunity for infinite loop:
// if we rerun the swear-word injector on these tokens, we might
// see **** and then reprocess again to get
// <strong><strong>****</strong></strong> ad infinitum.
//
// Thus, the idea of a skip is that once we process a token with
// an injector, we mark all of those tokens as having "come from"
// the injector, and we never run the injector again on these
// tokens.
//
// There were two more complications, however:
//
// - With HTMLPurifier_Injector_RemoveEmpty, we noticed that if
// you had <b><i></i></b>, after you removed the <i></i>, you
// really would like this injector to go back and reprocess
// the <b> tag, discovering that it is now empty and can be
// removed. So we reintroduced the possibility of infinite looping
// by adding a "rewind" function, which let you go back to an
// earlier point in the token stream and reprocess it with injectors.
// Needless to say, we need to UN-skip the token so it gets
// reprocessed.
//
// - Suppose that you successfuly process a token, replace it with
// one with your skip mark, but now another injector wants to
// process the skipped token with another token. Should you continue
// to skip that new token, or reprocess it? If you reprocess,
// you can end up with an infinite loop where one injector converts
// <a> to <b>, and then another injector converts it back. So
// we inherit the skips, but for some reason, I thought that we
// should inherit the skip from the first token of the token
// that we deleted. Why? Well, it seems to work OK.
//
// If I were to redesign this functionality, I would absolutely not
// go about doing it this way: the semantics are just not very well
// defined, and in any case you probably wanted to operate on trees,
// not token streams.
// vim: et sw=4 sts=4

View file

@ -26,7 +26,7 @@ abstract class HTMLPurifier_Token
public $armor = array();
/**
* Used during MakeWellFormed.
* Used during MakeWellFormed. See Note [Injector skips]
* @type
*/
public $skip;

View file

@ -85,11 +85,13 @@ class HTMLPurifier_URI
$def = $config->getDefinition('URI');
$scheme_obj = $def->getDefaultScheme($config, $context);
if (!$scheme_obj) {
// something funky happened to the default scheme object
trigger_error(
'Default scheme object "' . $def->defaultScheme . '" was not readable',
E_USER_WARNING
);
if ($def->defaultScheme !== null) {
// something funky happened to the default scheme object
trigger_error(
'Default scheme object "' . $def->defaultScheme . '" was not readable',
E_USER_WARNING
);
} // suppress error if it's null
return false;
}
}

View file

@ -79,9 +79,18 @@ class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme
} else {
$raw_data = $data;
}
if ( strlen($raw_data) < 12 ) {
// error; exif_imagetype throws exception with small files,
// and this likely indicates a corrupt URI/failed parse anyway
return false;
}
// XXX probably want to refactor this into a general mechanism
// for filtering arbitrary content types
$file = tempnam("/tmp", "");
if (function_exists('sys_get_temp_dir')) {
$file = tempnam(sys_get_temp_dir(), "");
} else {
$file = tempnam("/tmp", "");
}
file_put_contents($file, $raw_data);
if (function_exists('exif_imagetype')) {
$image_code = exif_imagetype($file);

View file

@ -0,0 +1,46 @@
<?php
/**
* Validates tel (for phone numbers).
*
* The relevant specifications for this protocol are RFC 3966 and RFC 5341,
* but this class takes a much simpler approach: we normalize phone
* numbers so that they only include (possibly) a leading plus,
* and then any number of digits and x'es.
*/
class HTMLPurifier_URIScheme_tel extends HTMLPurifier_URIScheme
{
/**
* @type bool
*/
public $browsable = false;
/**
* @type bool
*/
public $may_omit_host = true;
/**
* @param HTMLPurifier_URI $uri
* @param HTMLPurifier_Config $config
* @param HTMLPurifier_Context $context
* @return bool
*/
public function doValidate(&$uri, $config, $context)
{
$uri->userinfo = null;
$uri->host = null;
$uri->port = null;
// Delete all non-numeric characters, non-x characters
// from phone number, EXCEPT for a leading plus sign.
$uri->path = preg_replace('/(?!^\+)[^\dx]/', '',
// Normalize e(x)tension to lower-case
str_replace('X', 'x', $uri->path));
return true;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1 @@
Deny from all

View file

@ -0,0 +1,102 @@
--- C:\Users\Edward\Webs\htmlpurifier\maintenance\PH5P.php 2008-07-07 09:12:12.000000000 -0400
+++ C:\Users\Edward\Webs\htmlpurifier\maintenance/PH5P.new.php 2008-12-06 02:29:34.988800000 -0500
@@ -65,7 +65,7 @@
public function __construct($data) {
$data = str_replace("\r\n", "\n", $data);
- $date = str_replace("\r", null, $data);
+ $data = str_replace("\r", null, $data);
$this->data = $data;
$this->char = -1;
@@ -211,7 +211,10 @@
// If nothing is returned, emit a U+0026 AMPERSAND character token.
// Otherwise, emit the character token that was returned.
$char = (!$entity) ? '&' : $entity;
- $this->emitToken($char);
+ $this->emitToken(array(
+ 'type' => self::CHARACTR,
+ 'data' => $char
+ ));
// Finally, switch to the data state.
$this->state = 'data';
@@ -708,7 +711,7 @@
} elseif($char === '&') {
/* U+0026 AMPERSAND (&)
Switch to the entity in attribute value state. */
- $this->entityInAttributeValueState('non');
+ $this->entityInAttributeValueState();
} elseif($char === '>') {
/* U+003E GREATER-THAN SIGN (>)
@@ -738,7 +741,8 @@
? '&'
: $entity;
- $this->emitToken($char);
+ $last = count($this->token['attr']) - 1;
+ $this->token['attr'][$last]['value'] .= $char;
}
private function bogusCommentState() {
@@ -1066,6 +1070,11 @@
$this->char++;
if(in_array($id, $this->entities)) {
+ if ($e_name[$c-1] !== ';') {
+ if ($c < $len && $e_name[$c] == ';') {
+ $this->char++; // consume extra semicolon
+ }
+ }
$entity = $id;
break;
}
@@ -2084,7 +2093,7 @@
/* Reconstruct the active formatting elements, if any. */
$this->reconstructActiveFormattingElements();
- $this->insertElement($token);
+ $this->insertElement($token, true, true);
break;
}
break;
@@ -3465,7 +3474,18 @@
}
}
- private function insertElement($token, $append = true) {
+ private function insertElement($token, $append = true, $check = false) {
+ // Proprietary workaround for libxml2's limitations with tag names
+ if ($check) {
+ // Slightly modified HTML5 tag-name modification,
+ // removing anything that's not an ASCII letter, digit, or hyphen
+ $token['name'] = preg_replace('/[^a-z0-9-]/i', '', $token['name']);
+ // Remove leading hyphens and numbers
+ $token['name'] = ltrim($token['name'], '-0..9');
+ // In theory, this should ever be needed, but just in case
+ if ($token['name'] === '') $token['name'] = 'span'; // arbitrary generic choice
+ }
+
$el = $this->dom->createElement($token['name']);
foreach($token['attr'] as $attr) {
@@ -3659,7 +3679,7 @@
}
}
- private function generateImpliedEndTags(array $exclude = array()) {
+ private function generateImpliedEndTags($exclude = array()) {
/* When the steps below require the UA to generate implied end tags,
then, if the current node is a dd element, a dt element, an li element,
a p element, a td element, a th element, or a tr element, the UA must
@@ -3673,7 +3693,8 @@
}
}
- private function getElementCategory($name) {
+ private function getElementCategory($node) {
+ $name = $node->tagName;
if(in_array($name, $this->special))
return self::SPECIAL;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,130 @@
#!/usr/bin/php
<?php
chdir(dirname(__FILE__));
require_once 'common.php';
assertCli();
/**
* @file
* Adds vimline to files
*/
chdir(dirname(__FILE__) . '/..');
$FS = new FSTools();
$vimline = 'vim: et sw=4 sts=4';
$files = $FS->globr('.', '*');
foreach ($files as $file) {
if (
!is_file($file) ||
prefix_is('./docs/doxygen', $file) ||
prefix_is('./library/standalone', $file) ||
prefix_is('./docs/specimens', $file) ||
postfix_is('.ser', $file) ||
postfix_is('.tgz', $file) ||
postfix_is('.patch', $file) ||
postfix_is('.dtd', $file) ||
postfix_is('.ent', $file) ||
postfix_is('.png', $file) ||
postfix_is('.ico', $file) ||
// wontfix
postfix_is('.vtest', $file) ||
postfix_is('.svg', $file) ||
postfix_is('.phpt', $file) ||
postfix_is('VERSION', $file) ||
postfix_is('WHATSNEW', $file) ||
postfix_is('configdoc/usage.xml', $file) ||
postfix_is('library/HTMLPurifier.includes.php', $file) ||
postfix_is('library/HTMLPurifier.safe-includes.php', $file) ||
postfix_is('smoketests/xssAttacks.xml', $file) ||
// phpt files
postfix_is('.diff', $file) ||
postfix_is('.exp', $file) ||
postfix_is('.log', $file) ||
postfix_is('.out', $file) ||
$file == './library/HTMLPurifier/Lexer/PH5P.php' ||
$file == './maintenance/PH5P.php'
) continue;
$ext = strrchr($file, '.');
if (
postfix_is('README', $file) ||
postfix_is('LICENSE', $file) ||
postfix_is('CREDITS', $file) ||
postfix_is('INSTALL', $file) ||
postfix_is('NEWS', $file) ||
postfix_is('TODO', $file) ||
postfix_is('WYSIWYG', $file) ||
postfix_is('Changelog', $file)
) $ext = '.txt';
if (postfix_is('Doxyfile', $file)) $ext = 'Doxyfile';
if (postfix_is('.php.in', $file)) $ext = '.php';
$no_nl = false;
switch ($ext) {
case '.php':
case '.inc':
case '.js':
$line = '// %s';
break;
case '.html':
case '.xsl':
case '.xml':
case '.htc':
$line = "<!-- %s\n-->";
break;
case '.htmlt':
$no_nl = true;
$line = '--# %s';
break;
case '.ini':
$line = '; %s';
break;
case '.css':
$line = '/* %s */';
break;
case '.bat':
$line = 'rem %s';
break;
case '.txt':
case '.utf8':
if (
prefix_is('./library/HTMLPurifier/ConfigSchema', $file) ||
prefix_is('./smoketests/test-schema', $file) ||
prefix_is('./tests/HTMLPurifier/StringHashParser', $file)
) {
$no_nl = true;
$line = '--# %s';
} else {
$line = ' %s';
}
break;
case 'Doxyfile':
$line = '# %s';
break;
default:
throw new Exception('Unknown file: ' . $file);
}
echo "$file\n";
$contents = file_get_contents($file);
$regex = '~' . str_replace('%s', 'vim: .+', preg_quote($line, '~')) . '~m';
$contents = preg_replace($regex, '', $contents);
$contents = rtrim($contents);
if (strpos($contents, "\r\n") !== false) $nl = "\r\n";
elseif (strpos($contents, "\n") !== false) $nl = "\n";
elseif (strpos($contents, "\r") !== false) $nl = "\r";
else $nl = PHP_EOL;
if (!$no_nl) $contents .= $nl;
$contents .= $nl . str_replace('%s', $vimline, $line) . $nl;
file_put_contents($file, $contents);
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,25 @@
<?php
function assertCli()
{
if (php_sapi_name() != 'cli' && !getenv('PHP_IS_CLI')) {
echo 'Script cannot be called from web-browser (if you are indeed calling via cli,
set environment variable PHP_IS_CLI to work around this).';
exit(1);
}
}
function prefix_is($comp, $subject)
{
return strncmp($comp, $subject, strlen($comp)) === 0;
}
function postfix_is($comp, $subject)
{
return strlen($subject) < $comp ? false : substr($subject, -strlen($comp)) === $comp;
}
// Load useful stuff like FSTools
require_once dirname(__FILE__) . '/../extras/HTMLPurifierExtras.auto.php';
// vim: et sw=4 sts=4

View file

@ -0,0 +1,11 @@
#!/bin/bash
cd ..
mkdir docs/doxygen
rm -Rf docs/doxygen/*
doxygen 1>docs/doxygen/info.log 2>docs/doxygen/errors.log
if [ "$?" != 0 ]; then
cat docs/doxygen/errors.log
exit
fi
cd docs
tar czf doxygen.tgz doxygen

View file

@ -0,0 +1,155 @@
#!/usr/bin/php
<?php
chdir(dirname(__FILE__));
require_once 'common.php';
require_once '../library/HTMLPurifier.auto.php';
assertCli();
if (version_compare(PHP_VERSION, '5.2.2', '<')) {
echo "This script requires PHP 5.2.2 or later, for tokenizer line numbers.";
exit(1);
}
/**
* @file
* Scans HTML Purifier source code for $config tokens and records the
* directive being used; configdoc can use this info later.
*
* Currently, this just dumps all the info onto the console. Eventually, it
* will create an XML file that our XSLT transform can use.
*/
$FS = new FSTools();
chdir(dirname(__FILE__) . '/../library/');
$raw_files = $FS->globr('.', '*.php');
$files = array();
foreach ($raw_files as $file) {
$file = substr($file, 2); // rm leading './'
if (strncmp('standalone/', $file, 11) === 0) continue; // rm generated files
if (substr_count($file, '.') > 1) continue; // rm meta files
$files[] = $file;
}
/**
* Moves the $i cursor to the next non-whitespace token
*/
function consumeWhitespace($tokens, &$i)
{
do {$i++;} while (is_array($tokens[$i]) && $tokens[$i][0] === T_WHITESPACE);
}
/**
* Tests whether or not a token is a particular type. There are three run-cases:
* - ($token, $expect_token): tests if the token is $expect_token type;
* - ($token, $expect_value): tests if the token is the string $expect_value;
* - ($token, $expect_token, $expect_value): tests if token is $expect_token type, and
* its string representation is $expect_value
*/
function testToken($token, $value_or_token, $value = null)
{
if (is_null($value)) {
if (is_int($value_or_token)) return is_array($token) && $token[0] === $value_or_token;
else return $token === $value_or_token;
} else {
return is_array($token) && $token[0] === $value_or_token && $token[1] === $value;
}
}
$counter = 0;
$full_counter = 0;
$tracker = array();
foreach ($files as $file) {
$tokens = token_get_all(file_get_contents($file));
$file = str_replace('\\', '/', $file);
for ($i = 0, $c = count($tokens); $i < $c; $i++) {
$ok = false;
// Match $config
if (!$ok && testToken($tokens[$i], T_VARIABLE, '$config')) $ok = true;
// Match $this->config
while (!$ok && testToken($tokens[$i], T_VARIABLE, '$this')) {
consumeWhitespace($tokens, $i);
if (!testToken($tokens[$i], T_OBJECT_OPERATOR)) break;
consumeWhitespace($tokens, $i);
if (testToken($tokens[$i], T_STRING, 'config')) $ok = true;
break;
}
if (!$ok) continue;
$ok = false;
for($i++; $i < $c; $i++) {
if ($tokens[$i] === ',' || $tokens[$i] === ')' || $tokens[$i] === ';') {
break;
}
if (is_string($tokens[$i])) continue;
if ($tokens[$i][0] === T_OBJECT_OPERATOR) {
$ok = true;
break;
}
}
if (!$ok) continue;
$line = $tokens[$i][2];
consumeWhitespace($tokens, $i);
if (!testToken($tokens[$i], T_STRING, 'get')) continue;
consumeWhitespace($tokens, $i);
if (!testToken($tokens[$i], '(')) continue;
$full_counter++;
$matched = false;
do {
// What we currently don't match are batch retrievals, and
// wildcard retrievals. This data might be useful in the future,
// which is why we have a do {} while loop that doesn't actually
// do anything.
consumeWhitespace($tokens, $i);
if (!testToken($tokens[$i], T_CONSTANT_ENCAPSED_STRING)) continue;
$id = substr($tokens[$i][1], 1, -1);
$counter++;
$matched = true;
if (!isset($tracker[$id])) $tracker[$id] = array();
if (!isset($tracker[$id][$file])) $tracker[$id][$file] = array();
$tracker[$id][$file][] = $line;
} while (0);
//echo "$file:$line uses $namespace.$directive\n";
}
}
echo "\n$counter/$full_counter instances of \$config or \$this->config found in source code.\n";
echo "Generating XML... ";
$xw = new XMLWriter();
$xw->openURI('../configdoc/usage.xml');
$xw->setIndent(true);
$xw->startDocument('1.0', 'UTF-8');
$xw->startElement('usage');
foreach ($tracker as $id => $files) {
$xw->startElement('directive');
$xw->writeAttribute('id', $id);
foreach ($files as $file => $lines) {
$xw->startElement('file');
$xw->writeAttribute('name', $file);
foreach ($lines as $line) {
$xw->writeElement('line', $line);
}
$xw->endElement();
}
$xw->endElement();
}
$xw->endElement();
$xw->flush();
echo "done!\n";
// vim: et sw=4 sts=4

View file

@ -0,0 +1,42 @@
#!/usr/bin/php
<?php
chdir(dirname(__FILE__));
require_once 'common.php';
assertCli();
/**
* @file
* Flushes the definition serial cache. This file should be
* called if changes to any subclasses of HTMLPurifier_Definition
* or related classes (such as HTMLPurifier_HTMLModule) are made. This
* may also be necessary if you've modified a customized version.
*
* @param Accepts one argument, cache type to flush; otherwise flushes all
* the caches.
*/
echo "Flushing cache... \n";
require_once(dirname(__FILE__) . '/../library/HTMLPurifier.auto.php');
$config = HTMLPurifier_Config::createDefault();
$names = array('HTML', 'CSS', 'URI', 'Test');
if (isset($argv[1])) {
if (in_array($argv[1], $names)) {
$names = array($argv[1]);
} else {
throw new Exception("Cache parameter {$argv[1]} is not a valid cache");
}
}
foreach ($names as $name) {
echo " - Flushing $name\n";
$cache = new HTMLPurifier_DefinitionCache_Serializer($name);
$cache->flush($config);
}
echo "Cache flushed successfully.\n";
// vim: et sw=4 sts=4

View file

@ -0,0 +1,30 @@
#!/usr/bin/php
<?php
chdir(dirname(__FILE__));
require_once 'common.php';
assertCli();
/**
* @file
* Runs all generation/flush cache scripts to ensure that somewhat volatile
* generated files are up-to-date.
*/
function e($cmd)
{
echo "\$ $cmd\n";
passthru($cmd, $status);
echo "\n";
if ($status) exit($status);
}
$php = empty($_SERVER['argv'][1]) ? 'php' : $_SERVER['argv'][1];
e($php . ' generate-includes.php');
e($php . ' generate-schema-cache.php');
e($php . ' flush-definition-cache.php');
e($php . ' generate-standalone.php');
e($php . ' config-scanner.php');
// vim: et sw=4 sts=4

View file

@ -0,0 +1,75 @@
#!/usr/bin/php
<?php
chdir(dirname(__FILE__));
require_once 'common.php';
assertCli();
/**
* @file
* Parses *.ent files into an entity lookup table, and then serializes and
* writes the whole kaboodle to a file. The resulting file is cached so
* that this script does not need to be run. This script should rarely,
* if ever, be run, since HTML's entities are fairly immutable.
*/
// here's where the entity files are located, assuming working directory
// is the same as the location of this PHP file. Needs trailing slash.
$entity_dir = '../docs/entities/';
// defines the output file for the serialized content.
$output_file = '../library/HTMLPurifier/EntityLookup/entities.ser';
// courtesy of a PHP manual comment
function unichr($dec)
{
if ($dec < 128) {
$utf = chr($dec);
} elseif ($dec < 2048) {
$utf = chr(192 + (($dec - ($dec % 64)) / 64));
$utf .= chr(128 + ($dec % 64));
} else {
$utf = chr(224 + (($dec - ($dec % 4096)) / 4096));
$utf .= chr(128 + ((($dec % 4096) - ($dec % 64)) / 64));
$utf .= chr(128 + ($dec % 64));
}
return $utf;
}
if ( !is_dir($entity_dir) ) exit("Fatal Error: Can't find entity directory.\n");
if ( file_exists($output_file) ) exit("Fatal Error: output file already exists.\n");
$dh = @opendir($entity_dir);
if ( !$dh ) exit("Fatal Error: Cannot read entity directory.\n");
$entity_files = array();
while (($file = readdir($dh)) !== false) {
if (@$file[0] === '.') continue;
if (substr(strrchr($file, "."), 1) !== 'ent') continue;
$entity_files[] = $file;
}
closedir($dh);
if ( !$entity_files ) exit("Fatal Error: No entity files to parse.\n");
$entity_table = array();
$regexp = '/<!ENTITY\s+([A-Za-z0-9]+)\s+"&#(?:38;#)?([0-9]+);">/';
foreach ( $entity_files as $file ) {
$contents = file_get_contents($entity_dir . $file);
$matches = array();
preg_match_all($regexp, $contents, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$entity_table[$match[1]] = unichr($match[2]);
}
}
$output = serialize($entity_table);
$fh = fopen($output_file, 'w');
fwrite($fh, $output);
fclose($fh);
echo "Completed successfully.";
// vim: et sw=4 sts=4

View file

@ -0,0 +1,192 @@
#!/usr/bin/php
<?php
chdir(dirname(__FILE__));
require_once 'common.php';
require_once '../tests/path2class.func.php';
require_once '../library/HTMLPurifier/Bootstrap.php';
assertCli();
/**
* @file
* Generates an include stub for users who do not want to use the autoloader.
* When new files are added to HTML Purifier's main codebase, this file should
* be called.
*/
chdir(dirname(__FILE__) . '/../library/');
$FS = new FSTools();
$exclude_dirs = array(
'HTMLPurifier/Language/',
'HTMLPurifier/ConfigSchema/',
'HTMLPurifier/Filter/',
'HTMLPurifier/Printer/',
/* These should be excluded, but need to have ConfigSchema support first
*/
);
$exclude_files = array(
'HTMLPurifier/Lexer/PEARSax3.php',
'HTMLPurifier/Lexer/PH5P.php',
'HTMLPurifier/Printer.php',
);
// Determine what files need to be included:
echo 'Scanning for files... ';
$raw_files = $FS->globr('.', '*.php');
if (!$raw_files) throw new Exception('Did not find any PHP source files');
$files = array();
foreach ($raw_files as $file) {
$file = substr($file, 2); // rm leading './'
if (strncmp('standalone/', $file, 11) === 0) continue; // rm generated files
if (substr_count($file, '.') > 1) continue; // rm meta files
$ok = true;
foreach ($exclude_dirs as $dir) {
if (strncmp($dir, $file, strlen($dir)) === 0) {
$ok = false;
break;
}
}
if (!$ok) continue; // rm excluded directories
if (in_array($file, $exclude_files)) continue; // rm excluded files
$files[] = $file;
}
echo "done!\n";
// Reorder list so that dependencies are included first:
/**
* Returns a lookup array of dependencies for a file.
*
* @note This function expects that format $name extends $parent on one line
*
* @param string $file
* File to check dependencies of.
* @return array
* Lookup array of files the file is dependent on, sorted accordingly.
*/
function get_dependency_lookup($file)
{
static $cache = array();
if (isset($cache[$file])) return $cache[$file];
if (!file_exists($file)) {
echo "File doesn't exist: $file\n";
return array();
}
$fh = fopen($file, 'r');
$deps = array();
while (!feof($fh)) {
$line = fgets($fh);
if (strncmp('class', $line, 5) === 0) {
// The implementation here is fragile and will break if we attempt
// to use interfaces. Beware!
$arr = explode(' extends ', trim($line, ' {'."\n\r"), 2);
if (count($arr) < 2) break;
$parent = $arr[1];
$dep_file = HTMLPurifier_Bootstrap::getPath($parent);
if (!$dep_file) break;
$deps[$dep_file] = true;
break;
}
}
fclose($fh);
foreach (array_keys($deps) as $file) {
// Extra dependencies must come *before* base dependencies
$deps = get_dependency_lookup($file) + $deps;
}
$cache[$file] = $deps;
return $deps;
}
/**
* Sorts files based on dependencies. This function is lazy and will not
* group files with dependencies together; it will merely ensure that a file
* is never included before its dependencies are.
*
* @param $files
* Files array to sort.
* @return
* Sorted array ($files is not modified by reference!)
*/
function dep_sort($files)
{
$ret = array();
$cache = array();
foreach ($files as $file) {
if (isset($cache[$file])) continue;
$deps = get_dependency_lookup($file);
foreach (array_keys($deps) as $dep) {
if (!isset($cache[$dep])) {
$ret[] = $dep;
$cache[$dep] = true;
}
}
$cache[$file] = true;
$ret[] = $file;
}
return $ret;
}
$files = dep_sort($files);
// Build the actual include stub:
$version = trim(file_get_contents('../VERSION'));
// stub
$php = "<?php
/**
* @file
* This file was auto-generated by generate-includes.php and includes all of
* the core files required by HTML Purifier. Use this if performance is a
* primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
* FILE, changes will be overwritten the next time the script is run.
*
* @version $version
*
* @warning
* You must *not* include any other HTML Purifier files before this file,
* because 'require' not 'require_once' is used.
*
* @warning
* This file requires that the include path contains the HTML Purifier
* library directory; this is not auto-set.
*/
";
foreach ($files as $file) {
$php .= "require '$file';" . PHP_EOL;
}
echo "Writing HTMLPurifier.includes.php... ";
file_put_contents('HTMLPurifier.includes.php', $php);
echo "done!\n";
$php = "<?php
/**
* @file
* This file was auto-generated by generate-includes.php and includes all of
* the core files required by HTML Purifier. This is a convenience stub that
* includes all files using dirname(__FILE__) and require_once. PLEASE DO NOT
* EDIT THIS FILE, changes will be overwritten the next time the script is run.
*
* Changes to include_path are not necessary.
*/
\$__dir = dirname(__FILE__);
";
foreach ($files as $file) {
$php .= "require_once \$__dir . '/$file';" . PHP_EOL;
}
echo "Writing HTMLPurifier.safe-includes.php... ";
file_put_contents('HTMLPurifier.safe-includes.php', $php);
echo "done!\n";
// vim: et sw=4 sts=4

View file

@ -0,0 +1,22 @@
<?php
/**
* @file
* This file compares our version of PH5P with Jero's original version, and
* generates a patch of the differences. This script should be run whenever
* library/HTMLPurifier/Lexer/PH5P.php is modified.
*/
$orig = realpath(dirname(__FILE__) . '/PH5P.php');
$new = realpath(dirname(__FILE__) . '/../library/HTMLPurifier/Lexer/PH5P.php');
$newt = dirname(__FILE__) . '/PH5P.new.php'; // temporary file
// minor text-processing of new file to get into same format as original
$new_src = file_get_contents($new);
$new_src = '<?php' . PHP_EOL . substr($new_src, strpos($new_src, 'class HTML5 {'));
file_put_contents($newt, $new_src);
shell_exec("diff -u \"$orig\" \"$newt\" > PH5P.patch");
unlink($newt);
// vim: et sw=4 sts=4

View file

@ -0,0 +1,45 @@
#!/usr/bin/php
<?php
require_once dirname(__FILE__) . '/common.php';
require_once dirname(__FILE__) . '/../library/HTMLPurifier.auto.php';
assertCli();
/**
* @file
* Generates a schema cache file, saving it to
* library/HTMLPurifier/ConfigSchema/schema.ser.
*
* This should be run when new configuration options are added to
* HTML Purifier. A cached version is available via the repository
* so this does not normally have to be regenerated.
*
* If you have a directory containing custom configuration schema files,
* you can simple add a path to that directory as a parameter to
* this, and they will get included.
*/
$target = dirname(__FILE__) . '/../library/HTMLPurifier/ConfigSchema/schema.ser';
$builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder();
$interchange = new HTMLPurifier_ConfigSchema_Interchange();
$builder->buildDir($interchange);
$loader = dirname(__FILE__) . '/../config-schema.php';
if (file_exists($loader)) include $loader;
foreach ($_SERVER['argv'] as $i => $dir) {
if ($i === 0) continue;
$builder->buildDir($interchange, realpath($dir));
}
$interchange->validate();
$schema_builder = new HTMLPurifier_ConfigSchema_Builder_ConfigSchema();
$schema = $schema_builder->build($interchange);
echo "Saving schema... ";
file_put_contents($target, serialize($schema));
echo "done!\n";
// vim: et sw=4 sts=4

View file

@ -0,0 +1,159 @@
#!/usr/bin/php
<?php
chdir(dirname(__FILE__));
require_once 'common.php';
assertCli();
/**
* @file
* Compiles all of HTML Purifier's library files into one big file
* named HTMLPurifier.standalone.php. This is usually called during the
* release process.
*/
/**
* Global hash that tracks already loaded includes
*/
$GLOBALS['loaded'] = array();
/**
* Custom FSTools for this script that overloads some behavior
* @warning The overloading of copy() is not necessarily global for
* this script. Watch out!
*/
class MergeLibraryFSTools extends FSTools
{
public function copyable($entry)
{
// Skip hidden files
if ($entry[0] == '.') {
return false;
}
return true;
}
public function copy($source, $dest)
{
copy_and_remove_includes($source, $dest);
}
}
$FS = new MergeLibraryFSTools();
/**
* Replaces the includes inside PHP source code with the corresponding
* source.
* @param string $text PHP source code to replace includes from
*/
function replace_includes($text)
{
// also remove vim modelines
return preg_replace_callback(
"/require(?:_once)? ['\"]([^'\"]+)['\"];/",
'replace_includes_callback',
$text
);
}
/**
* Removes leading PHP tags from included files. Assumes that there is
* no trailing tag. Also removes vim modelines.
* @note This is safe for files that have internal <?php
* @param string $text Text to have leading PHP tag from
*/
function remove_php_tags($text)
{
$text = preg_replace('#// vim:.+#', '', $text);
return substr($text, 5);
}
/**
* Copies the contents of a directory to the standalone directory
* @param string $dir Directory to copy
*/
function make_dir_standalone($dir)
{
global $FS;
return $FS->copyr($dir, 'standalone/' . $dir);
}
/**
* Copies the contents of a file to the standalone directory
* @param string $file File to copy
*/
function make_file_standalone($file)
{
global $FS;
$FS->mkdirr('standalone/' . dirname($file));
copy_and_remove_includes($file, 'standalone/' . $file);
return true;
}
/**
* Copies a file to another location recursively, if it is a PHP file
* remove includes
* @param string $file Original file
* @param string $sfile New location of file
*/
function copy_and_remove_includes($file, $sfile)
{
$contents = file_get_contents($file);
if (strrchr($file, '.') === '.php') $contents = replace_includes($contents);
return file_put_contents($sfile, $contents);
}
/**
* @param $matches preg_replace_callback matches array, where index 1
* is the filename to include
*/
function replace_includes_callback($matches)
{
$file = $matches[1];
$preserve = array(
// PEAR (external)
'XML/HTMLSax3.php' => 1
);
if (isset($preserve[$file])) {
return $matches[0];
}
if (isset($GLOBALS['loaded'][$file])) return '';
$GLOBALS['loaded'][$file] = true;
return replace_includes(remove_php_tags(file_get_contents($file)));
}
echo 'Generating includes file... ';
shell_exec('php generate-includes.php');
echo "done!\n";
chdir(dirname(__FILE__) . '/../library/');
echo 'Creating full file...';
$contents = replace_includes(file_get_contents('HTMLPurifier.includes.php'));
$contents = str_replace(
// Note that bootstrap is now inside the standalone file
"define('HTMLPURIFIER_PREFIX', realpath(dirname(__FILE__) . '/..'));",
"define('HTMLPURIFIER_PREFIX', dirname(__FILE__) . '/standalone');
set_include_path(HTMLPURIFIER_PREFIX . PATH_SEPARATOR . get_include_path());",
$contents
);
file_put_contents('HTMLPurifier.standalone.php', $contents);
echo ' done!' . PHP_EOL;
echo 'Creating standalone directory...';
$FS->rmdirr('standalone'); // ensure a clean copy
// data files
$FS->mkdirr('standalone/HTMLPurifier/DefinitionCache/Serializer');
make_file_standalone('HTMLPurifier/EntityLookup/entities.ser');
make_file_standalone('HTMLPurifier/ConfigSchema/schema.ser');
// non-standard inclusion setup
make_dir_standalone('HTMLPurifier/ConfigSchema');
make_dir_standalone('HTMLPurifier/Language');
make_dir_standalone('HTMLPurifier/Filter');
make_dir_standalone('HTMLPurifier/Printer');
make_file_standalone('HTMLPurifier/Printer.php');
make_file_standalone('HTMLPurifier/Lexer/PH5P.php');
echo ' done!' . PHP_EOL;
// vim: et sw=4 sts=4

View file

@ -0,0 +1,11 @@
#!/usr/bin/php
<?php
/**
* @file
* Deprecated in favor of generate-standalone.php.
*/
require dirname(__FILE__) . '/generate-standalone.php';
// vim: et sw=4 sts=4

View file

@ -0,0 +1,71 @@
#!/usr/bin/php
<?php
chdir(dirname(__FILE__));
require_once 'common.php';
assertCli();
echo "Please do not run this script. It is here for historical purposes only.";
exit;
/**
* @file
* Extracts all definitions inside a configuration schema
* (HTMLPurifier_ConfigSchema) and exports them as plain text files.
*
* @todo Extract version numbers.
*/
define('HTMLPURIFIER_SCHEMA_STRICT', true); // description data needs to be collected
require_once dirname(__FILE__) . '/../library/HTMLPurifier.auto.php';
// We need includes to ensure all HTMLPurifier_ConfigSchema calls are
// performed.
require_once 'HTMLPurifier.includes.php';
// Also, these extra files will be necessary.
require_once 'HTMLPurifier/Filter/ExtractStyleBlocks.php';
/**
* Takes a hash and saves its contents to library/HTMLPurifier/ConfigSchema/
*/
function saveHash($hash)
{
if ($hash === false) return;
$dir = realpath(dirname(__FILE__) . '/../library/HTMLPurifier/ConfigSchema');
$name = $hash['ID'] . '.txt';
$file = $dir . '/' . $name;
if (file_exists($file)) {
trigger_error("File already exists; skipped $name");
return;
}
$file = new FSTools_File($file);
$file->open('w');
$multiline = false;
foreach ($hash as $key => $value) {
$multiline = $multiline || (strpos($value, "\n") !== false);
if ($multiline) {
$file->put("--$key--" . PHP_EOL);
$file->put(str_replace("\n", PHP_EOL, $value) . PHP_EOL);
} else {
if ($key == 'ID') {
$file->put("$value" . PHP_EOL);
} else {
$file->put("$key: $value" . PHP_EOL);
}
}
}
$file->close();
}
$schema = HTMLPurifier_ConfigSchema::instance();
$adapter = new HTMLPurifier_ConfigSchema_StringHashReverseAdapter($schema);
foreach ($schema->info as $ns => $ns_array) {
saveHash($adapter->get($ns));
foreach ($ns_array as $dir => $x) {
saveHash($adapter->get($ns, $dir));
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,32 @@
#!/usr/bin/php
<?php
chdir(dirname(__FILE__));
require_once 'common.php';
assertCli();
echo "Please do not run this script. It is here for historical purposes only.";
exit;
/**
* @file
* Removes leading includes from files.
*
* @note
* This does not remove inline includes; those must be handled manually.
*/
chdir(dirname(__FILE__) . '/../tests/HTMLPurifier');
$FS = new FSTools();
$files = $FS->globr('.', '*.php');
foreach ($files as $file) {
if (substr_count(basename($file), '.') > 1) continue;
$old_code = file_get_contents($file);
$new_code = preg_replace("#^require_once .+[\n\r]*#m", '', $old_code);
if ($old_code !== $new_code) {
file_put_contents($file, $new_code);
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,32 @@
#!/usr/bin/php
<?php
chdir(dirname(__FILE__));
require_once 'common.php';
assertCli();
echo "Please do not run this script. It is here for historical purposes only.";
exit;
/**
* @file
* Removes ConfigSchema function calls from source files.
*/
chdir(dirname(__FILE__) . '/../library/');
$FS = new FSTools();
$files = $FS->globr('.', '*.php');
foreach ($files as $file) {
if (substr_count(basename($file), '.') > 1) continue;
$old_code = file_get_contents($file);
$new_code = preg_replace("#^HTMLPurifier_ConfigSchema::.+?\);[\n\r]*#ms", '', $old_code);
if ($old_code !== $new_code) {
file_put_contents($file, $new_code);
}
if (preg_match('#^\s+HTMLPurifier_ConfigSchema::#m', $new_code)) {
echo "Indented ConfigSchema call in $file\n";
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,5 @@
#!/bin/bash -e
./compile-doxygen.sh
cd ../docs
scp doxygen.tgz htmlpurifier.org:/home/ezyang/htmlpurifier.org
ssh htmlpurifier.org "cd /home/ezyang/htmlpurifier.org && ./reload-docs.sh"

View file

@ -0,0 +1,37 @@
#!/usr/bin/php
<?php
chdir(dirname(__FILE__));
require_once 'common.php';
assertCli();
/**
* @file
* Removes trailing whitespace from files.
*/
chdir(dirname(__FILE__) . '/..');
$FS = new FSTools();
$files = $FS->globr('.', '{,.}*', GLOB_BRACE);
foreach ($files as $file) {
if (
!is_file($file) ||
prefix_is('./.git', $file) ||
prefix_is('./docs/doxygen', $file) ||
postfix_is('.ser', $file) ||
postfix_is('.tgz', $file) ||
postfix_is('.patch', $file) ||
postfix_is('.dtd', $file) ||
postfix_is('.ent', $file) ||
$file == './library/HTMLPurifier/Lexer/PH5P.php' ||
$file == './maintenance/PH5P.php'
) continue;
$contents = file_get_contents($file);
$result = preg_replace('/^(.*?)[ \t]+(\r?)$/m', '\1\2', $contents, -1, $count);
if (!$count) continue;
echo "$file\n";
file_put_contents($file, $result);
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,84 @@
#!/usr/bin/php
<?php
chdir(dirname(__FILE__));
require_once 'common.php';
require_once '../library/HTMLPurifier.auto.php';
assertCli();
/**
* @file
* Renames a configuration directive. This involves renaming the file,
* adding an alias, and then regenerating the cache. You still have to
* manually go through and fix any calls to the directive.
* @warning This script doesn't handle multi-stringhash files.
*/
$argv = $_SERVER['argv'];
if (count($argv) < 3) {
echo "Usage: {$argv[0]} OldName NewName\n";
exit(1);
}
chdir('../library/HTMLPurifier/ConfigSchema/schema');
$old = $argv[1];
$new = $argv[2];
if (!file_exists("$old.txt")) {
echo "Cannot move undefined configuration directive $old\n";
exit(1);
}
if ($old === $new) {
echo "Attempting to move to self, aborting\n";
exit(1);
}
if (file_exists("$new.txt")) {
echo "Cannot move to already defined directive $new\n";
exit(1);
}
$file = "$old.txt";
$builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder();
$interchange = new HTMLPurifier_ConfigSchema_Interchange();
$builder->buildFile($interchange, $file);
$contents = file_get_contents($file);
if (strpos($contents, "\r\n") !== false) {
$nl = "\r\n";
} elseif (strpos($contents, "\r") !== false) {
$nl = "\r";
} else {
$nl = "\n";
}
// replace name with new name
$contents = str_replace($old, $new, $contents);
if ($interchange->directives[$old]->aliases) {
$pos_alias = strpos($contents, 'ALIASES:');
$pos_ins = strpos($contents, $nl, $pos_alias);
if ($pos_ins === false) $pos_ins = strlen($contents);
$contents =
substr($contents, 0, $pos_ins) . ", $old" . substr($contents, $pos_ins);
file_put_contents($file, $contents);
} else {
$lines = explode($nl, $contents);
$insert = false;
foreach ($lines as $n => $line) {
if (strncmp($line, '--', 2) === 0) {
$insert = $n;
break;
}
}
if (!$insert) {
$lines[] = "ALIASES: $old";
} else {
array_splice($lines, $insert, 0, "ALIASES: $old");
}
file_put_contents($file, implode($nl, $lines));
}
rename("$old.txt", "$new.txt") || exit(1);

View file

@ -0,0 +1,34 @@
#!/usr/bin/php
<?php
chdir(dirname(__FILE__));
require_once 'common.php';
assertCli();
/**
* @file
* Converts all instances of $config->set and $config->get to the new
* format, as described by docs/dev-config-bcbreaks.txt
*/
$FS = new FSTools();
chdir(dirname(__FILE__) . '/..');
$raw_files = $FS->globr('.', '*.php');
foreach ($raw_files as $file) {
$file = substr($file, 2); // rm leading './'
if (strpos($file, 'library/standalone/') === 0) continue;
if (strpos($file, 'maintenance/update-config.php') === 0) continue;
if (strpos($file, 'test-settings.php') === 0) continue;
if (substr_count($file, '.') > 1) continue; // rm meta files
// process the file
$contents = file_get_contents($file);
$contents = preg_replace(
"#config->(set|get)\('(.+?)', '(.+?)'#",
"config->\\1('\\2.\\3'",
$contents
);
if ($contents === '') continue;
file_put_contents($file, $contents);
}
// vim: et sw=4 sts=4

View file

@ -21,10 +21,8 @@ if ($data !== false && $data !== '') {
// REQUIRED SETTINGS
// Note on running SimpleTest:
// Because HTML Purifier is PHP5-only and E_STRICT compliant, SimpleTest
// 1.0.1 will not work; you need to run SimpleTest off its trunk using:
//
// $ svn co https://simpletest.svn.sourceforge.net/svnroot/simpletest/simpletest/trunk simpletest
// You want the Git copy of SimpleTest, found here:
// https://github.com/simpletest/simpletest/
//
// If SimpleTest is borked with HTML Purifier, please contact me or
// the SimpleTest devs; I am a developer for SimpleTest so I should be

View file

@ -0,0 +1,72 @@
<?php
// This file is the configuration for Travis testing.
// Note: The only external library you *need* is SimpleTest; everything else
// is optional.
// We've got a lot of tests, so we recommend turning the limit off.
set_time_limit(0);
// Turning off output buffering will prevent mysterious errors from core dumps.
$data = @ob_get_clean();
if ($data !== false && $data !== '') {
echo "Output buffer contains data [".urlencode($data)."]\n";
exit;
}
// -----------------------------------------------------------------------------
// REQUIRED SETTINGS
// Note on running SimpleTest:
// You want the Git copy of SimpleTest, found here:
// https://github.com/simpletest/simpletest/
//
// If SimpleTest is borked with HTML Purifier, please contact me or
// the SimpleTest devs; I am a developer for SimpleTest so I should be
// able to quickly assess a fix. SimpleTest's problem is my problem!
// Where is SimpleTest located? Remember to include a trailing slash!
$simpletest_location = dirname(__FILE__) . '/simpletest/';
// -----------------------------------------------------------------------------
// OPTIONAL SETTINGS
// Note on running PHPT:
// Vanilla PHPT from https://github.com/tswicegood/PHPT_Core should
// work fine on Linux w/o multitest.
//
// To do multitest or Windows testing, you'll need some more
// patches at https://github.com/ezyang/PHPT_Core
//
// I haven't tested the Windows setup in a while so I don't know if
// it still works.
// Should PHPT tests be enabled?
$GLOBALS['HTMLPurifierTest']['PHPT'] = false;
// If PHPT isn't in your Path via PEAR, set that here:
// set_include_path('/path/to/phpt/Core/src' . PATH_SEPARATOR . get_include_path());
// Where is CSSTidy located? (Include trailing slash. Leave false to disable.)
$csstidy_location = false;
// For tests/multitest.php, which versions to test?
$versions_to_test = array();
// Stable PHP binary to use when invoking maintenance scripts.
$php = 'php';
// For tests/multitest.php, what is the multi-version executable? It must
// accept an extra parameter (version number) before all other arguments
$phpv = false;
// Should PEAR tests be run? If you've got a valid PEAR installation, set this
// to true (or, if it's not in the include path, to its install directory).
$GLOBALS['HTMLPurifierTest']['PEAR'] = false;
// If PEAR is enabled, what PEAR tests should be run? (Note: you will
// need to ensure these libraries are installed)
$GLOBALS['HTMLPurifierTest']['Net_IDNA2'] = true;
// vim: et sw=4 sts=4

View file

@ -0,0 +1,15 @@
<?php
function path2class($path)
{
$temp = $path;
$temp = str_replace('./', '', $temp); // remove leading './'
$temp = str_replace('.\\', '', $temp); // remove leading '.\'
$temp = str_replace('\\', '_', $temp); // normalize \ to _
$temp = str_replace('/', '_', $temp); // normalize / to _
while(strpos($temp, '__') !== false) $temp = str_replace('__', '_', $temp);
$temp = str_replace('.php', '', $temp);
return $temp;
}
// vim: et sw=4 sts=4

View file

@ -66,6 +66,11 @@ class PasswordHash
public function get_random_bytes($count)
{
$output = '';
if (is_callable('random_bytes')) {
return random_bytes($count);
}
if (@is_readable('/dev/urandom') &&
($fh = @fopen('/dev/urandom', 'rb'))) {
$output = fread($fh, $count);

View file

@ -169,11 +169,11 @@ if ($dumpTree) {
$scss = new Compiler();
if ($debugInfo && $inputFile) {
if ($debugInfo) {
$scss->setLineNumberStyle(Compiler::DEBUG_INFO);
}
if ($lineNumbers && $inputFile) {
if ($lineNumbers) {
$scss->setLineNumberStyle(Compiler::LINE_COMMENTS);
}

View file

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2015 Leaf Corcoran
* @copyright 2017 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*

View file

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2015 Leaf Corcoran
* @copyright 2012-2017 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*

View file

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2015 Leaf Corcoran
* @copyright 2012-2017 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*

View file

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2015 Leaf Corcoran
* @copyright 2012-2017 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
@ -729,7 +729,7 @@ class Compiler
{
$env = $this->pushEnv($block);
$envs = $this->compactEnv($env);
$without = isset($block->with) ? $this->compileWith($block->with) : self::WITH_RULE;
$without = isset($block->with) ? $this->compileWith($block->with) : static::WITH_RULE;
// wrap inline selector
if ($block->selector) {
@ -854,12 +854,12 @@ class Compiler
];
// exclude selectors by default
$without = self::WITH_RULE;
$without = static::WITH_RULE;
if ($this->libMapHasKey([$with, self::$with])) {
$without = self::WITH_ALL;
if ($this->libMapHasKey([$with, static::$with])) {
$without = static::WITH_ALL;
$list = $this->coerceList($this->libMapGet([$with, self::$with]));
$list = $this->coerceList($this->libMapGet([$with, static::$with]));
foreach ($list[2] as $item) {
$keyword = $this->compileStringContent($this->coerceString($item));
@ -870,10 +870,10 @@ class Compiler
}
}
if ($this->libMapHasKey([$with, self::$without])) {
if ($this->libMapHasKey([$with, static::$without])) {
$without = 0;
$list = $this->coerceList($this->libMapGet([$with, self::$without]));
$list = $this->coerceList($this->libMapGet([$with, static::$without]));
foreach ($list[2] as $item) {
$keyword = $this->compileStringContent($this->coerceString($item));
@ -920,10 +920,10 @@ class Compiler
*/
private function isWithout($without, Block $block)
{
if ((($without & self::WITH_RULE) && isset($block->selectors)) ||
(($without & self::WITH_MEDIA) &&
if ((($without & static::WITH_RULE) && isset($block->selectors)) ||
(($without & static::WITH_MEDIA) &&
isset($block->type) && $block->type === Type::T_MEDIA) ||
(($without & self::WITH_SUPPORTS) &&
(($without & static::WITH_SUPPORTS) &&
isset($block->type) && $block->type === Type::T_DIRECTIVE &&
isset($block->name) && $block->name === 'supports')
) {
@ -1014,13 +1014,16 @@ class Compiler
$line = $block->sourceLine;
switch ($this->lineNumberStyle) {
case self::LINE_COMMENTS:
$annotation->lines[] = '/* line ' . $line . ', ' . $file . ' */';
case static::LINE_COMMENTS:
$annotation->lines[] = '/* line ' . $line
. ($file ? ', ' . $file : '')
. ' */';
break;
case self::DEBUG_INFO:
$annotation->lines[] = '@media -sass-debug-info{filename{font-family:"' . $file
. '"}line{font-family:' . $line . '}}';
case static::DEBUG_INFO:
$annotation->lines[] = '@media -sass-debug-info{'
. ($file ? 'filename{font-family:"' . $file . '"}' : '')
. 'line{font-family:' . $line . '}}';
break;
}
@ -1580,7 +1583,7 @@ class Compiler
$shouldSet = $isDefault &&
(($result = $this->get($name[1], false)) === null
|| $result === self::$null);
|| $result === static::$null);
if (! $isDefault || $shouldSet) {
$this->set($name[1], $this->reduce($value));
@ -1608,7 +1611,7 @@ class Compiler
if ($value[0] !== Type::T_NULL) {
$value = $this->reduce($value);
if ($value[0] === Type::T_NULL || $value === self::$nullString) {
if ($value[0] === Type::T_NULL || $value === static::$nullString) {
break;
}
}
@ -1634,7 +1637,7 @@ class Compiler
case Type::T_FUNCTION:
list(, $block) = $child;
$this->set(self::$namespaces[$block->type] . $block->name, $block);
$this->set(static::$namespaces[$block->type] . $block->name, $block);
break;
case Type::T_EXTEND:
@ -1682,7 +1685,7 @@ class Compiler
list(,, $values) = $this->coerceList($item);
foreach ($each->vars as $i => $var) {
$this->set($var, isset($values[$i]) ? $values[$i] : self::$null, true);
$this->set($var, isset($values[$i]) ? $values[$i] : static::$null, true);
}
}
@ -1791,7 +1794,7 @@ class Compiler
// including a mixin
list(, $name, $argValues, $content) = $child;
$mixin = $this->get(self::$namespaces['mixin'] . $name, false);
$mixin = $this->get(static::$namespaces['mixin'] . $name, false);
if (! $mixin) {
$this->throwError("Undefined mixin $name");
@ -1810,7 +1813,7 @@ class Compiler
if (isset($content)) {
$content->scope = $callingScope;
$this->setRaw(self::$namespaces['special'] . 'content', $content, $this->env);
$this->setRaw(static::$namespaces['special'] . 'content', $content, $this->env);
}
if (isset($mixin->args)) {
@ -1827,8 +1830,8 @@ class Compiler
break;
case Type::T_MIXIN_CONTENT:
$content = $this->get(self::$namespaces['special'] . 'content', false, $this->getStoreEnv())
?: $this->get(self::$namespaces['special'] . 'content', false, $this->env);
$content = $this->get(static::$namespaces['special'] . 'content', false, $this->getStoreEnv())
?: $this->get(static::$namespaces['special'] . 'content', false, $this->env);
if (! $content) {
$content = new \stdClass();
@ -1915,7 +1918,7 @@ class Compiler
*/
protected function isTruthy($value)
{
return $value !== self::$false && $value !== self::$null;
return $value !== static::$false && $value !== static::$null;
}
/**
@ -1970,7 +1973,7 @@ class Compiler
case Type::T_EXPRESSION:
list(, $op, $left, $right, $inParens) = $value;
$opName = isset(self::$operatorNames[$op]) ? self::$operatorNames[$op] : $op;
$opName = isset(static::$operatorNames[$op]) ? static::$operatorNames[$op] : $op;
$inExp = $inExp || $this->shouldEval($left) || $this->shouldEval($right);
$left = $this->reduce($left, true);
@ -2086,11 +2089,11 @@ class Compiler
if ($op === 'not') {
if ($inExp || $inParens) {
if ($exp === self::$false || $exp === self::$null) {
return self::$true;
if ($exp === static::$false || $exp === static::$null) {
return static::$true;
}
return self::$false;
return static::$false;
}
$op = $op . ' ';
@ -2344,7 +2347,7 @@ class Compiler
return;
}
if ($left !== self::$false and $left !== self::$null) {
if ($left !== static::$false and $left !== static::$null) {
return $this->reduce($right, true);
}
@ -2366,7 +2369,7 @@ class Compiler
return;
}
if ($left !== self::$false and $left !== self::$null) {
if ($left !== static::$false and $left !== static::$null) {
return $left;
}
@ -2597,7 +2600,7 @@ class Compiler
*/
public function toBool($thing)
{
return $thing ? self::$true : self::$false;
return $thing ? static::$true : static::$false;
}
/**
@ -2723,6 +2726,39 @@ class Compiler
$reduced = $this->reduce($exp);
switch ($reduced[0]) {
case Type::T_LIST:
$reduced = $this->extractInterpolation($reduced);
if ($reduced[0] !== Type::T_LIST) {
break;
}
list(, $delim, $items) = $reduced;
if ($delim !== ' ') {
$delim .= ' ';
}
$filtered = [];
foreach ($items as $item) {
if ($item[0] === Type::T_NULL) {
continue;
}
$temp = $this->compileValue([Type::T_KEYWORD, $item]);
if ($temp[0] === Type::T_STRING) {
$filtered[] = $this->compileStringContent($temp);
} elseif ($temp[0] === Type::T_KEYWORD) {
$filtered[] = $temp[1];
} else {
$filtered[] = $this->compileValue($temp);
}
}
$reduced = [Type::T_KEYWORD, implode("$delim", $filtered)];
break;
case Type::T_STRING:
$reduced = [Type::T_KEYWORD, $this->compileStringContent($reduced)];
break;
@ -2847,7 +2883,7 @@ class Compiler
$newPart = [];
foreach ($part as $p) {
if ($p === self::$selfSelector) {
if ($p === static::$selfSelector) {
$setSelf = true;
foreach ($parent as $i => $parentPart) {
@ -3066,7 +3102,7 @@ class Compiler
public function get($name, $shouldThrow = true, Environment $env = null)
{
$normalizedName = $this->normalizeName($name);
$specialContentKey = self::$namespaces['special'] . 'content';
$specialContentKey = static::$namespaces['special'] . 'content';
if (! isset($env)) {
$env = $this->getStoreEnv();
@ -3354,6 +3390,8 @@ class Compiler
$urls = [$url, preg_replace('/[^\/]+$/', '_\0', $url)];
}
$hasExtension = preg_match('/[.]s?css$/', $url);
foreach ($this->importPaths as $dir) {
if (is_string($dir)) {
// check urls for normal import paths
@ -3363,7 +3401,7 @@ class Compiler
. $full;
if ($this->fileExists($file = $full . '.scss') ||
$this->fileExists($file = $full)
($hasExtension && $this->fileExists($file = $full))
) {
return $file;
}
@ -3460,7 +3498,7 @@ class Compiler
*/
protected function fileExists($name)
{
return is_file($name);
return file_exists($name) && is_file($name);
}
/**
@ -3474,7 +3512,7 @@ class Compiler
*/
protected function callScssFunction($name, $argValues, &$returnValue)
{
$func = $this->get(self::$namespaces['function'] . $name, false);
$func = $this->get(static::$namespaces['function'] . $name, false);
if (! $func) {
return false;
@ -3503,7 +3541,7 @@ class Compiler
$this->popEnv();
$returnValue = ! isset($ret) ? self::$defaultValue : $ret;
$returnValue = ! isset($ret) ? static::$defaultValue : $ret;
return true;
}
@ -3527,7 +3565,7 @@ class Compiler
list($f, $prototype) = $this->userFunctions[$name];
} elseif (($f = $this->getBuiltinFunction($name)) && is_callable($f)) {
$libName = $f[1];
$prototype = isset(self::$$libName) ? self::$$libName : null;
$prototype = isset(static::$$libName) ? static::$$libName : null;
} else {
return false;
}
@ -3752,7 +3790,7 @@ class Compiler
}
if ($value === null) {
return self::$null;
return static::$null;
}
if (is_numeric($value)) {
@ -3760,7 +3798,7 @@ class Compiler
}
if ($value === '') {
return self::$emptyString;
return static::$emptyString;
}
if (preg_match('/^(#([0-9a-f]{6})|#([0-9a-f]{3}))$/i', $value, $m)) {
@ -3802,11 +3840,11 @@ class Compiler
return $item;
}
if ($item === self::$emptyList) {
return self::$emptyMap;
if ($item === static::$emptyList) {
return static::$emptyMap;
}
return [Type::T_MAP, [$item], [self::$null]];
return [Type::T_MAP, [$item], [static::$null]];
}
/**
@ -4176,7 +4214,7 @@ class Compiler
list($list, $value) = $args;
if ($value[0] === Type::T_MAP) {
return self::$null;
return static::$null;
}
if ($list[0] === Type::T_MAP ||
@ -4188,7 +4226,7 @@ class Compiler
}
if ($list[0] !== Type::T_LIST) {
return self::$null;
return static::$null;
}
$values = [];
@ -4199,7 +4237,7 @@ class Compiler
$key = array_search($this->normalizeValue($value), $values);
return false === $key ? self::$null : $key + 1;
return false === $key ? static::$null : $key + 1;
}
protected static $libRgb = ['red', 'green', 'blue'];
@ -4769,7 +4807,7 @@ class Compiler
$n += count($list[2]);
}
return isset($list[2][$n]) ? $list[2][$n] : self::$defaultValue;
return isset($list[2][$n]) ? $list[2][$n] : static::$defaultValue;
}
protected static $libSetNth = ['list', 'n', 'value'];
@ -4807,7 +4845,7 @@ class Compiler
}
}
return self::$null;
return static::$null;
}
protected static $libMapKeys = ['map'];
@ -4958,7 +4996,7 @@ class Compiler
switch ($value[0]) {
case Type::T_KEYWORD:
if ($value === self::$true || $value === self::$false) {
if ($value === static::$true || $value === static::$false) {
return 'bool';
}
@ -5031,7 +5069,7 @@ class Compiler
$result = strpos($stringContent, $substringContent);
return $result === false ? self::$null : new Node\Number($result + 1, '');
return $result === false ? static::$null : new Node\Number($result + 1, '');
}
protected static $libStrInsert = ['string', 'insert', 'index'];
@ -5063,7 +5101,7 @@ class Compiler
protected function libStrSlice($args)
{
if (isset($args[2]) && $args[2][1] == 0) {
return self::$nullString;
return static::$nullString;
}
$string = $this->coerceString($args[0]);
@ -5125,7 +5163,7 @@ class Compiler
$name = $this->compileStringContent($string);
// user defined functions
if ($this->has(self::$namespaces['function'] . $name)) {
if ($this->has(static::$namespaces['function'] . $name)) {
return true;
}
@ -5156,7 +5194,7 @@ class Compiler
$string = $this->coerceString($args[0]);
$name = $this->compileStringContent($string);
return $this->has(self::$namespaces['mixin'] . $name);
return $this->has(static::$namespaces['mixin'] . $name);
}
protected static $libVariableExists = ['name'];
@ -5214,7 +5252,7 @@ class Compiler
protected static $libInspect = ['value'];
protected function libInspect($args)
{
if ($args[0] === self::$null) {
if ($args[0] === static::$null) {
return [Type::T_KEYWORD, 'null'];
}

View file

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2015 Leaf Corcoran
* @copyright 2012-2017 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*

View file

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2015 Leaf Corcoran
* @copyright 2012-2017 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*

View file

@ -2,7 +2,7 @@
/**
* SCSSPHP
*
* @copyright 2012-2015 Leaf Corcoran
* @copyright 2012-2017 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*

View file

@ -0,0 +1,21 @@
<?php
/**
* SCSSPHP
*
* @copyright 2012-2017 Leaf Corcoran
*
* @license http://opensource.org/licenses/MIT MIT
*
* @link http://leafo.github.io/scssphp
*/
namespace Leafo\ScssPhp\Exception;
/**
* Range exception
*
* @author Anthon Pang <anthon.pang@gmail.com>
*/
class RangeException extends \Exception
{
}

Some files were not shown because too many files have changed in this diff Show more