package updates

This commit is contained in:
nobody 2020-11-10 12:41:03 +11:00
parent eaa2675a1a
commit f8804f2b17
143 changed files with 3003 additions and 13404 deletions

903
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -6,8 +6,7 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'ArithmeticError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php',
'AssertionError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php',
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'CommerceGuys\\Intl\\Calculator' => $vendorDir . '/commerceguys/intl/src/Calculator.php',
'CommerceGuys\\Intl\\Country\\Country' => $vendorDir . '/commerceguys/intl/src/Country/Country.php',
'CommerceGuys\\Intl\\Country\\CountryEntityInterface' => $vendorDir . '/commerceguys/intl/src/Country/CountryEntityInterface.php',
@ -38,7 +37,6 @@ return array(
'CommerceGuys\\Intl\\NumberFormat\\NumberFormatInterface' => $vendorDir . '/commerceguys/intl/src/NumberFormat/NumberFormatInterface.php',
'CommerceGuys\\Intl\\NumberFormat\\NumberFormatRepository' => $vendorDir . '/commerceguys/intl/src/NumberFormat/NumberFormatRepository.php',
'CommerceGuys\\Intl\\NumberFormat\\NumberFormatRepositoryInterface' => $vendorDir . '/commerceguys/intl/src/NumberFormat/NumberFormatRepositoryInterface.php',
'DivisionByZeroError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php',
'Doctrine\\Common\\Collections\\AbstractLazyCollection' => $vendorDir . '/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php',
'Doctrine\\Common\\Collections\\ArrayCollection' => $vendorDir . '/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php',
'Doctrine\\Common\\Collections\\Collection' => $vendorDir . '/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php',
@ -51,7 +49,6 @@ return array(
'Doctrine\\Common\\Collections\\Expr\\Value' => $vendorDir . '/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php',
'Doctrine\\Common\\Collections\\ExpressionBuilder' => $vendorDir . '/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php',
'Doctrine\\Common\\Collections\\Selectable' => $vendorDir . '/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php',
'Error' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/Error.php',
'GuzzleHttp\\Client' => $vendorDir . '/guzzlehttp/guzzle/src/Client.php',
'GuzzleHttp\\ClientInterface' => $vendorDir . '/guzzlehttp/guzzle/src/ClientInterface.php',
'GuzzleHttp\\Cookie\\CookieJar' => $vendorDir . '/guzzlehttp/guzzle/src/Cookie/CookieJar.php',
@ -85,8 +82,11 @@ return array(
'GuzzleHttp\\Promise\\AggregateException' => $vendorDir . '/guzzlehttp/promises/src/AggregateException.php',
'GuzzleHttp\\Promise\\CancellationException' => $vendorDir . '/guzzlehttp/promises/src/CancellationException.php',
'GuzzleHttp\\Promise\\Coroutine' => $vendorDir . '/guzzlehttp/promises/src/Coroutine.php',
'GuzzleHttp\\Promise\\Create' => $vendorDir . '/guzzlehttp/promises/src/Create.php',
'GuzzleHttp\\Promise\\Each' => $vendorDir . '/guzzlehttp/promises/src/Each.php',
'GuzzleHttp\\Promise\\EachPromise' => $vendorDir . '/guzzlehttp/promises/src/EachPromise.php',
'GuzzleHttp\\Promise\\FulfilledPromise' => $vendorDir . '/guzzlehttp/promises/src/FulfilledPromise.php',
'GuzzleHttp\\Promise\\Is' => $vendorDir . '/guzzlehttp/promises/src/Is.php',
'GuzzleHttp\\Promise\\Promise' => $vendorDir . '/guzzlehttp/promises/src/Promise.php',
'GuzzleHttp\\Promise\\PromiseInterface' => $vendorDir . '/guzzlehttp/promises/src/PromiseInterface.php',
'GuzzleHttp\\Promise\\PromisorInterface' => $vendorDir . '/guzzlehttp/promises/src/PromisorInterface.php',
@ -94,18 +94,23 @@ return array(
'GuzzleHttp\\Promise\\RejectionException' => $vendorDir . '/guzzlehttp/promises/src/RejectionException.php',
'GuzzleHttp\\Promise\\TaskQueue' => $vendorDir . '/guzzlehttp/promises/src/TaskQueue.php',
'GuzzleHttp\\Promise\\TaskQueueInterface' => $vendorDir . '/guzzlehttp/promises/src/TaskQueueInterface.php',
'GuzzleHttp\\Promise\\Utils' => $vendorDir . '/guzzlehttp/promises/src/Utils.php',
'GuzzleHttp\\Psr7\\AppendStream' => $vendorDir . '/guzzlehttp/psr7/src/AppendStream.php',
'GuzzleHttp\\Psr7\\BufferStream' => $vendorDir . '/guzzlehttp/psr7/src/BufferStream.php',
'GuzzleHttp\\Psr7\\CachingStream' => $vendorDir . '/guzzlehttp/psr7/src/CachingStream.php',
'GuzzleHttp\\Psr7\\DroppingStream' => $vendorDir . '/guzzlehttp/psr7/src/DroppingStream.php',
'GuzzleHttp\\Psr7\\FnStream' => $vendorDir . '/guzzlehttp/psr7/src/FnStream.php',
'GuzzleHttp\\Psr7\\Header' => $vendorDir . '/guzzlehttp/psr7/src/Header.php',
'GuzzleHttp\\Psr7\\InflateStream' => $vendorDir . '/guzzlehttp/psr7/src/InflateStream.php',
'GuzzleHttp\\Psr7\\LazyOpenStream' => $vendorDir . '/guzzlehttp/psr7/src/LazyOpenStream.php',
'GuzzleHttp\\Psr7\\LimitStream' => $vendorDir . '/guzzlehttp/psr7/src/LimitStream.php',
'GuzzleHttp\\Psr7\\Message' => $vendorDir . '/guzzlehttp/psr7/src/Message.php',
'GuzzleHttp\\Psr7\\MessageTrait' => $vendorDir . '/guzzlehttp/psr7/src/MessageTrait.php',
'GuzzleHttp\\Psr7\\MimeType' => $vendorDir . '/guzzlehttp/psr7/src/MimeType.php',
'GuzzleHttp\\Psr7\\MultipartStream' => $vendorDir . '/guzzlehttp/psr7/src/MultipartStream.php',
'GuzzleHttp\\Psr7\\NoSeekStream' => $vendorDir . '/guzzlehttp/psr7/src/NoSeekStream.php',
'GuzzleHttp\\Psr7\\PumpStream' => $vendorDir . '/guzzlehttp/psr7/src/PumpStream.php',
'GuzzleHttp\\Psr7\\Query' => $vendorDir . '/guzzlehttp/psr7/src/Query.php',
'GuzzleHttp\\Psr7\\Request' => $vendorDir . '/guzzlehttp/psr7/src/Request.php',
'GuzzleHttp\\Psr7\\Response' => $vendorDir . '/guzzlehttp/psr7/src/Response.php',
'GuzzleHttp\\Psr7\\Rfc7230' => $vendorDir . '/guzzlehttp/psr7/src/Rfc7230.php',
@ -117,6 +122,7 @@ return array(
'GuzzleHttp\\Psr7\\Uri' => $vendorDir . '/guzzlehttp/psr7/src/Uri.php',
'GuzzleHttp\\Psr7\\UriNormalizer' => $vendorDir . '/guzzlehttp/psr7/src/UriNormalizer.php',
'GuzzleHttp\\Psr7\\UriResolver' => $vendorDir . '/guzzlehttp/psr7/src/UriResolver.php',
'GuzzleHttp\\Psr7\\Utils' => $vendorDir . '/guzzlehttp/psr7/src/Utils.php',
'GuzzleHttp\\RedirectMiddleware' => $vendorDir . '/guzzlehttp/guzzle/src/RedirectMiddleware.php',
'GuzzleHttp\\RequestOptions' => $vendorDir . '/guzzlehttp/guzzle/src/RequestOptions.php',
'GuzzleHttp\\RetryMiddleware' => $vendorDir . '/guzzlehttp/guzzle/src/RetryMiddleware.php',
@ -603,7 +609,6 @@ return array(
'PHPGit\\Command\\TreeCommand' => $vendorDir . '/kzykhys/git/src/PHPGit/Command/TreeCommand.php',
'PHPGit\\Exception\\GitException' => $vendorDir . '/kzykhys/git/src/PHPGit/Exception/GitException.php',
'PHPGit\\Git' => $vendorDir . '/kzykhys/git/src/PHPGit/Git.php',
'ParseError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ParseError.php',
'Psr\\Http\\Message\\MessageInterface' => $vendorDir . '/psr/http-message/src/MessageInterface.php',
'Psr\\Http\\Message\\RequestInterface' => $vendorDir . '/psr/http-message/src/RequestInterface.php',
'Psr\\Http\\Message\\ResponseInterface' => $vendorDir . '/psr/http-message/src/ResponseInterface.php',
@ -619,6 +624,9 @@ return array(
'Psr\\Log\\LoggerInterface' => $vendorDir . '/psr/log/Psr/Log/LoggerInterface.php',
'Psr\\Log\\LoggerTrait' => $vendorDir . '/psr/log/Psr/Log/LoggerTrait.php',
'Psr\\Log\\NullLogger' => $vendorDir . '/psr/log/Psr/Log/NullLogger.php',
'Psr\\Log\\Test\\DummyTest' => $vendorDir . '/psr/log/Psr/Log/Test/DummyTest.php',
'Psr\\Log\\Test\\LoggerInterfaceTest' => $vendorDir . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
'Psr\\Log\\Test\\TestLogger' => $vendorDir . '/psr/log/Psr/Log/Test/TestLogger.php',
'Ramsey\\Uuid\\BinaryUtils' => $vendorDir . '/ramsey/uuid/src/BinaryUtils.php',
'Ramsey\\Uuid\\Builder\\DefaultUuidBuilder' => $vendorDir . '/ramsey/uuid/src/Builder/DefaultUuidBuilder.php',
'Ramsey\\Uuid\\Builder\\DegradedUuidBuilder' => $vendorDir . '/ramsey/uuid/src/Builder/DegradedUuidBuilder.php',
@ -1030,7 +1038,6 @@ return array(
'Sabre\\Xml\\Writer' => $vendorDir . '/sabre/xml/lib/Writer.php',
'Sabre\\Xml\\XmlDeserializable' => $vendorDir . '/sabre/xml/lib/XmlDeserializable.php',
'Sabre\\Xml\\XmlSerializable' => $vendorDir . '/sabre/xml/lib/XmlSerializable.php',
'SessionUpdateTimestampHandlerInterface' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php',
'Smarty' => $vendorDir . '/smarty/smarty/libs/Smarty.class.php',
'SmartyBC' => $vendorDir . '/smarty/smarty/libs/SmartyBC.class.php',
'SmartyCompilerException' => $vendorDir . '/smarty/smarty/libs/sysplugins/smartycompilerexception.php',
@ -1239,12 +1246,10 @@ return array(
'Symfony\\Polyfill\\Intl\\Idn\\Resources\\unidata\\DisallowedRanges' => $vendorDir . '/symfony/polyfill-intl-idn/Resources/unidata/DisallowedRanges.php',
'Symfony\\Polyfill\\Intl\\Idn\\Resources\\unidata\\Regex' => $vendorDir . '/symfony/polyfill-intl-idn/Resources/unidata/Regex.php',
'Symfony\\Polyfill\\Intl\\Normalizer\\Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Normalizer.php',
'Symfony\\Polyfill\\Php70\\Php70' => $vendorDir . '/symfony/polyfill-php70/Php70.php',
'Symfony\\Polyfill\\Php72\\Php72' => $vendorDir . '/symfony/polyfill-php72/Php72.php',
'Symfony\\Polyfill\\Php80\\Php80' => $vendorDir . '/symfony/polyfill-php80/Php80.php',
'TPC_yyStackEntry' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_configfileparser.php',
'TP_yyStackEntry' => $vendorDir . '/smarty/smarty/libs/sysplugins/smarty_internal_templateparser.php',
'TypeError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/TypeError.php',
'URLify' => $vendorDir . '/jbroadway/urlify/URLify.php',
'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'UploadHandler' => $vendorDir . '/blueimp/jquery-file-upload/server/php/UploadHandler.php',
@ -1257,7 +1262,6 @@ return array(
'Zotlabs\\Daemon\\CacheThumb' => $baseDir . '/Zotlabs/Daemon/CacheThumb.php',
'Zotlabs\\Daemon\\Cache_embeds' => $baseDir . '/Zotlabs/Daemon/Cache_embeds.php',
'Zotlabs\\Daemon\\Checksites' => $baseDir . '/Zotlabs/Daemon/Checksites.php',
'Zotlabs\\Daemon\\Cli_suggest' => $baseDir . '/Zotlabs/Daemon/Cli_suggest.php',
'Zotlabs\\Daemon\\Convo' => $baseDir . '/Zotlabs/Daemon/Convo.php',
'Zotlabs\\Daemon\\Cron' => $baseDir . '/Zotlabs/Daemon/Cron.php',
'Zotlabs\\Daemon\\Cron_daily' => $baseDir . '/Zotlabs/Daemon/Cron_daily.php',
@ -1320,6 +1324,7 @@ return array(
'Zotlabs\\Lib\\Markdown' => $baseDir . '/Zotlabs/Lib/Markdown.php',
'Zotlabs\\Lib\\MarkdownSoap' => $baseDir . '/Zotlabs/Lib/MarkdownSoap.php',
'Zotlabs\\Lib\\MessageFilter' => $baseDir . '/Zotlabs/Lib/MessageFilter.php',
'Zotlabs\\Lib\\Nodeinfo' => $baseDir . '/Zotlabs/Lib/Nodeinfo.php',
'Zotlabs\\Lib\\PConfig' => $baseDir . '/Zotlabs/Lib/PConfig.php',
'Zotlabs\\Lib\\Permcat' => $baseDir . '/Zotlabs/Lib/Permcat.php',
'Zotlabs\\Lib\\PermissionDescription' => $baseDir . '/Zotlabs/Lib/PermissionDescription.php',
@ -1483,7 +1488,6 @@ return array(
'Zotlabs\\Module\\Q' => $baseDir . '/Zotlabs/Module/Q.php',
'Zotlabs\\Module\\Randprof' => $baseDir . '/Zotlabs/Module/Randprof.php',
'Zotlabs\\Module\\React' => $baseDir . '/Zotlabs/Module/React.php',
'Zotlabs\\Module\\Regdir' => $baseDir . '/Zotlabs/Module/Regdir.php',
'Zotlabs\\Module\\Register' => $baseDir . '/Zotlabs/Module/Register.php',
'Zotlabs\\Module\\Regmod' => $baseDir . '/Zotlabs/Module/Regmod.php',
'Zotlabs\\Module\\Regver' => $baseDir . '/Zotlabs/Module/Regver.php',
@ -1521,6 +1525,7 @@ return array(
'Zotlabs\\Module\\Subthread' => $baseDir . '/Zotlabs/Module/Subthread.php',
'Zotlabs\\Module\\Suggestions' => $baseDir . '/Zotlabs/Module/Suggestions.php',
'Zotlabs\\Module\\Superblock' => $baseDir . '/Zotlabs/Module/Superblock.php',
'Zotlabs\\Module\\Tagadelic' => $baseDir . '/Zotlabs/Module/Tagadelic.php',
'Zotlabs\\Module\\Tagger' => $baseDir . '/Zotlabs/Module/Tagger.php',
'Zotlabs\\Module\\Tagrm' => $baseDir . '/Zotlabs/Module/Tagrm.php',
'Zotlabs\\Module\\Tasks' => $baseDir . '/Zotlabs/Module/Tasks.php',

View file

@ -8,7 +8,6 @@ $baseDir = dirname($vendorDir);
return array(
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => $vendorDir . '/guzzlehttp/psr7/src/functions_include.php',

View file

@ -10,7 +10,6 @@ return array(
'Zotlabs\\' => array($baseDir . '/Zotlabs'),
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'),
'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'),
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),

View file

@ -9,7 +9,6 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
public static $files = array (
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
'023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php',
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
'a0edc8309cc5e1d60e3047b5df6b7052' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/functions_include.php',
@ -46,7 +45,6 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
array (
'Symfony\\Polyfill\\Php80\\' => 23,
'Symfony\\Polyfill\\Php72\\' => 23,
'Symfony\\Polyfill\\Php70\\' => 23,
'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33,
'Symfony\\Polyfill\\Intl\\Idn\\' => 26,
'Symfony\\Polyfill\\Ctype\\' => 23,
@ -122,10 +120,6 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php72',
),
'Symfony\\Polyfill\\Php70\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php70',
),
'Symfony\\Polyfill\\Intl\\Normalizer\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer',
@ -282,8 +276,7 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
);
public static $classMap = array (
'ArithmeticError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php',
'AssertionError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php',
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'CommerceGuys\\Intl\\Calculator' => __DIR__ . '/..' . '/commerceguys/intl/src/Calculator.php',
'CommerceGuys\\Intl\\Country\\Country' => __DIR__ . '/..' . '/commerceguys/intl/src/Country/Country.php',
'CommerceGuys\\Intl\\Country\\CountryEntityInterface' => __DIR__ . '/..' . '/commerceguys/intl/src/Country/CountryEntityInterface.php',
@ -314,7 +307,6 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'CommerceGuys\\Intl\\NumberFormat\\NumberFormatInterface' => __DIR__ . '/..' . '/commerceguys/intl/src/NumberFormat/NumberFormatInterface.php',
'CommerceGuys\\Intl\\NumberFormat\\NumberFormatRepository' => __DIR__ . '/..' . '/commerceguys/intl/src/NumberFormat/NumberFormatRepository.php',
'CommerceGuys\\Intl\\NumberFormat\\NumberFormatRepositoryInterface' => __DIR__ . '/..' . '/commerceguys/intl/src/NumberFormat/NumberFormatRepositoryInterface.php',
'DivisionByZeroError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php',
'Doctrine\\Common\\Collections\\AbstractLazyCollection' => __DIR__ . '/..' . '/doctrine/collections/lib/Doctrine/Common/Collections/AbstractLazyCollection.php',
'Doctrine\\Common\\Collections\\ArrayCollection' => __DIR__ . '/..' . '/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php',
'Doctrine\\Common\\Collections\\Collection' => __DIR__ . '/..' . '/doctrine/collections/lib/Doctrine/Common/Collections/Collection.php',
@ -327,7 +319,6 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'Doctrine\\Common\\Collections\\Expr\\Value' => __DIR__ . '/..' . '/doctrine/collections/lib/Doctrine/Common/Collections/Expr/Value.php',
'Doctrine\\Common\\Collections\\ExpressionBuilder' => __DIR__ . '/..' . '/doctrine/collections/lib/Doctrine/Common/Collections/ExpressionBuilder.php',
'Doctrine\\Common\\Collections\\Selectable' => __DIR__ . '/..' . '/doctrine/collections/lib/Doctrine/Common/Collections/Selectable.php',
'Error' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/Error.php',
'GuzzleHttp\\Client' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Client.php',
'GuzzleHttp\\ClientInterface' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/ClientInterface.php',
'GuzzleHttp\\Cookie\\CookieJar' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/Cookie/CookieJar.php',
@ -361,8 +352,11 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'GuzzleHttp\\Promise\\AggregateException' => __DIR__ . '/..' . '/guzzlehttp/promises/src/AggregateException.php',
'GuzzleHttp\\Promise\\CancellationException' => __DIR__ . '/..' . '/guzzlehttp/promises/src/CancellationException.php',
'GuzzleHttp\\Promise\\Coroutine' => __DIR__ . '/..' . '/guzzlehttp/promises/src/Coroutine.php',
'GuzzleHttp\\Promise\\Create' => __DIR__ . '/..' . '/guzzlehttp/promises/src/Create.php',
'GuzzleHttp\\Promise\\Each' => __DIR__ . '/..' . '/guzzlehttp/promises/src/Each.php',
'GuzzleHttp\\Promise\\EachPromise' => __DIR__ . '/..' . '/guzzlehttp/promises/src/EachPromise.php',
'GuzzleHttp\\Promise\\FulfilledPromise' => __DIR__ . '/..' . '/guzzlehttp/promises/src/FulfilledPromise.php',
'GuzzleHttp\\Promise\\Is' => __DIR__ . '/..' . '/guzzlehttp/promises/src/Is.php',
'GuzzleHttp\\Promise\\Promise' => __DIR__ . '/..' . '/guzzlehttp/promises/src/Promise.php',
'GuzzleHttp\\Promise\\PromiseInterface' => __DIR__ . '/..' . '/guzzlehttp/promises/src/PromiseInterface.php',
'GuzzleHttp\\Promise\\PromisorInterface' => __DIR__ . '/..' . '/guzzlehttp/promises/src/PromisorInterface.php',
@ -370,18 +364,23 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'GuzzleHttp\\Promise\\RejectionException' => __DIR__ . '/..' . '/guzzlehttp/promises/src/RejectionException.php',
'GuzzleHttp\\Promise\\TaskQueue' => __DIR__ . '/..' . '/guzzlehttp/promises/src/TaskQueue.php',
'GuzzleHttp\\Promise\\TaskQueueInterface' => __DIR__ . '/..' . '/guzzlehttp/promises/src/TaskQueueInterface.php',
'GuzzleHttp\\Promise\\Utils' => __DIR__ . '/..' . '/guzzlehttp/promises/src/Utils.php',
'GuzzleHttp\\Psr7\\AppendStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/AppendStream.php',
'GuzzleHttp\\Psr7\\BufferStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/BufferStream.php',
'GuzzleHttp\\Psr7\\CachingStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/CachingStream.php',
'GuzzleHttp\\Psr7\\DroppingStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/DroppingStream.php',
'GuzzleHttp\\Psr7\\FnStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/FnStream.php',
'GuzzleHttp\\Psr7\\Header' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Header.php',
'GuzzleHttp\\Psr7\\InflateStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/InflateStream.php',
'GuzzleHttp\\Psr7\\LazyOpenStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/LazyOpenStream.php',
'GuzzleHttp\\Psr7\\LimitStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/LimitStream.php',
'GuzzleHttp\\Psr7\\Message' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Message.php',
'GuzzleHttp\\Psr7\\MessageTrait' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/MessageTrait.php',
'GuzzleHttp\\Psr7\\MimeType' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/MimeType.php',
'GuzzleHttp\\Psr7\\MultipartStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/MultipartStream.php',
'GuzzleHttp\\Psr7\\NoSeekStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/NoSeekStream.php',
'GuzzleHttp\\Psr7\\PumpStream' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/PumpStream.php',
'GuzzleHttp\\Psr7\\Query' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Query.php',
'GuzzleHttp\\Psr7\\Request' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Request.php',
'GuzzleHttp\\Psr7\\Response' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Response.php',
'GuzzleHttp\\Psr7\\Rfc7230' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Rfc7230.php',
@ -393,6 +392,7 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'GuzzleHttp\\Psr7\\Uri' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Uri.php',
'GuzzleHttp\\Psr7\\UriNormalizer' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/UriNormalizer.php',
'GuzzleHttp\\Psr7\\UriResolver' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/UriResolver.php',
'GuzzleHttp\\Psr7\\Utils' => __DIR__ . '/..' . '/guzzlehttp/psr7/src/Utils.php',
'GuzzleHttp\\RedirectMiddleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/RedirectMiddleware.php',
'GuzzleHttp\\RequestOptions' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/RequestOptions.php',
'GuzzleHttp\\RetryMiddleware' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/RetryMiddleware.php',
@ -879,7 +879,6 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'PHPGit\\Command\\TreeCommand' => __DIR__ . '/..' . '/kzykhys/git/src/PHPGit/Command/TreeCommand.php',
'PHPGit\\Exception\\GitException' => __DIR__ . '/..' . '/kzykhys/git/src/PHPGit/Exception/GitException.php',
'PHPGit\\Git' => __DIR__ . '/..' . '/kzykhys/git/src/PHPGit/Git.php',
'ParseError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ParseError.php',
'Psr\\Http\\Message\\MessageInterface' => __DIR__ . '/..' . '/psr/http-message/src/MessageInterface.php',
'Psr\\Http\\Message\\RequestInterface' => __DIR__ . '/..' . '/psr/http-message/src/RequestInterface.php',
'Psr\\Http\\Message\\ResponseInterface' => __DIR__ . '/..' . '/psr/http-message/src/ResponseInterface.php',
@ -895,6 +894,9 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'Psr\\Log\\LoggerInterface' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerInterface.php',
'Psr\\Log\\LoggerTrait' => __DIR__ . '/..' . '/psr/log/Psr/Log/LoggerTrait.php',
'Psr\\Log\\NullLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/NullLogger.php',
'Psr\\Log\\Test\\DummyTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/DummyTest.php',
'Psr\\Log\\Test\\LoggerInterfaceTest' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/LoggerInterfaceTest.php',
'Psr\\Log\\Test\\TestLogger' => __DIR__ . '/..' . '/psr/log/Psr/Log/Test/TestLogger.php',
'Ramsey\\Uuid\\BinaryUtils' => __DIR__ . '/..' . '/ramsey/uuid/src/BinaryUtils.php',
'Ramsey\\Uuid\\Builder\\DefaultUuidBuilder' => __DIR__ . '/..' . '/ramsey/uuid/src/Builder/DefaultUuidBuilder.php',
'Ramsey\\Uuid\\Builder\\DegradedUuidBuilder' => __DIR__ . '/..' . '/ramsey/uuid/src/Builder/DegradedUuidBuilder.php',
@ -1306,7 +1308,6 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'Sabre\\Xml\\Writer' => __DIR__ . '/..' . '/sabre/xml/lib/Writer.php',
'Sabre\\Xml\\XmlDeserializable' => __DIR__ . '/..' . '/sabre/xml/lib/XmlDeserializable.php',
'Sabre\\Xml\\XmlSerializable' => __DIR__ . '/..' . '/sabre/xml/lib/XmlSerializable.php',
'SessionUpdateTimestampHandlerInterface' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php',
'Smarty' => __DIR__ . '/..' . '/smarty/smarty/libs/Smarty.class.php',
'SmartyBC' => __DIR__ . '/..' . '/smarty/smarty/libs/SmartyBC.class.php',
'SmartyCompilerException' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smartycompilerexception.php',
@ -1515,12 +1516,10 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'Symfony\\Polyfill\\Intl\\Idn\\Resources\\unidata\\DisallowedRanges' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/Resources/unidata/DisallowedRanges.php',
'Symfony\\Polyfill\\Intl\\Idn\\Resources\\unidata\\Regex' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/Resources/unidata/Regex.php',
'Symfony\\Polyfill\\Intl\\Normalizer\\Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Normalizer.php',
'Symfony\\Polyfill\\Php70\\Php70' => __DIR__ . '/..' . '/symfony/polyfill-php70/Php70.php',
'Symfony\\Polyfill\\Php72\\Php72' => __DIR__ . '/..' . '/symfony/polyfill-php72/Php72.php',
'Symfony\\Polyfill\\Php80\\Php80' => __DIR__ . '/..' . '/symfony/polyfill-php80/Php80.php',
'TPC_yyStackEntry' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_configfileparser.php',
'TP_yyStackEntry' => __DIR__ . '/..' . '/smarty/smarty/libs/sysplugins/smarty_internal_templateparser.php',
'TypeError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/TypeError.php',
'URLify' => __DIR__ . '/..' . '/jbroadway/urlify/URLify.php',
'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'UploadHandler' => __DIR__ . '/..' . '/blueimp/jquery-file-upload/server/php/UploadHandler.php',
@ -1533,7 +1532,6 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'Zotlabs\\Daemon\\CacheThumb' => __DIR__ . '/../..' . '/Zotlabs/Daemon/CacheThumb.php',
'Zotlabs\\Daemon\\Cache_embeds' => __DIR__ . '/../..' . '/Zotlabs/Daemon/Cache_embeds.php',
'Zotlabs\\Daemon\\Checksites' => __DIR__ . '/../..' . '/Zotlabs/Daemon/Checksites.php',
'Zotlabs\\Daemon\\Cli_suggest' => __DIR__ . '/../..' . '/Zotlabs/Daemon/Cli_suggest.php',
'Zotlabs\\Daemon\\Convo' => __DIR__ . '/../..' . '/Zotlabs/Daemon/Convo.php',
'Zotlabs\\Daemon\\Cron' => __DIR__ . '/../..' . '/Zotlabs/Daemon/Cron.php',
'Zotlabs\\Daemon\\Cron_daily' => __DIR__ . '/../..' . '/Zotlabs/Daemon/Cron_daily.php',
@ -1596,6 +1594,7 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'Zotlabs\\Lib\\Markdown' => __DIR__ . '/../..' . '/Zotlabs/Lib/Markdown.php',
'Zotlabs\\Lib\\MarkdownSoap' => __DIR__ . '/../..' . '/Zotlabs/Lib/MarkdownSoap.php',
'Zotlabs\\Lib\\MessageFilter' => __DIR__ . '/../..' . '/Zotlabs/Lib/MessageFilter.php',
'Zotlabs\\Lib\\Nodeinfo' => __DIR__ . '/../..' . '/Zotlabs/Lib/Nodeinfo.php',
'Zotlabs\\Lib\\PConfig' => __DIR__ . '/../..' . '/Zotlabs/Lib/PConfig.php',
'Zotlabs\\Lib\\Permcat' => __DIR__ . '/../..' . '/Zotlabs/Lib/Permcat.php',
'Zotlabs\\Lib\\PermissionDescription' => __DIR__ . '/../..' . '/Zotlabs/Lib/PermissionDescription.php',
@ -1759,7 +1758,6 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'Zotlabs\\Module\\Q' => __DIR__ . '/../..' . '/Zotlabs/Module/Q.php',
'Zotlabs\\Module\\Randprof' => __DIR__ . '/../..' . '/Zotlabs/Module/Randprof.php',
'Zotlabs\\Module\\React' => __DIR__ . '/../..' . '/Zotlabs/Module/React.php',
'Zotlabs\\Module\\Regdir' => __DIR__ . '/../..' . '/Zotlabs/Module/Regdir.php',
'Zotlabs\\Module\\Register' => __DIR__ . '/../..' . '/Zotlabs/Module/Register.php',
'Zotlabs\\Module\\Regmod' => __DIR__ . '/../..' . '/Zotlabs/Module/Regmod.php',
'Zotlabs\\Module\\Regver' => __DIR__ . '/../..' . '/Zotlabs/Module/Regver.php',
@ -1797,6 +1795,7 @@ class ComposerStaticInit7b34d7e50a62201ec5d5e526a5b8b35d
'Zotlabs\\Module\\Subthread' => __DIR__ . '/../..' . '/Zotlabs/Module/Subthread.php',
'Zotlabs\\Module\\Suggestions' => __DIR__ . '/../..' . '/Zotlabs/Module/Suggestions.php',
'Zotlabs\\Module\\Superblock' => __DIR__ . '/../..' . '/Zotlabs/Module/Superblock.php',
'Zotlabs\\Module\\Tagadelic' => __DIR__ . '/../..' . '/Zotlabs/Module/Tagadelic.php',
'Zotlabs\\Module\\Tagger' => __DIR__ . '/../..' . '/Zotlabs/Module/Tagger.php',
'Zotlabs\\Module\\Tagrm' => __DIR__ . '/../..' . '/Zotlabs/Module/Tagrm.php',
'Zotlabs\\Module\\Tasks' => __DIR__ . '/../..' . '/Zotlabs/Module/Tasks.php',

View file

@ -407,26 +407,26 @@
},
{
"name": "guzzlehttp/promises",
"version": "v1.3.1",
"version_normalized": "1.3.1.0",
"version": "1.4.0",
"version_normalized": "1.4.0.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
"reference": "60d379c243457e073cff02bc323a2a86cb355631"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
"reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
"url": "https://api.github.com/repos/guzzle/promises/zipball/60d379c243457e073cff02bc323a2a86cb355631",
"reference": "60d379c243457e073cff02bc323a2a86cb355631",
"shasum": ""
},
"require": {
"php": ">=5.5.0"
"php": ">=5.5"
},
"require-dev": {
"phpunit/phpunit": "^4.0"
"symfony/phpunit-bridge": "^4.4 || ^5.1"
},
"time": "2016-12-20T10:07:11+00:00",
"time": "2020-09-30T07:37:28+00:00",
"type": "library",
"extra": {
"branch-alias": {
@ -460,17 +460,17 @@
},
{
"name": "guzzlehttp/psr7",
"version": "1.6.1",
"version_normalized": "1.6.1.0",
"version": "1.7.0",
"version_normalized": "1.7.0.0",
"source": {
"type": "git",
"url": "https://github.com/guzzle/psr7.git",
"reference": "239400de7a173fe9901b9ac7c06497751f00727a"
"reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a",
"reference": "239400de7a173fe9901b9ac7c06497751f00727a",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/53330f47520498c0ae1f61f7e2c90f55690c06a3",
"reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3",
"shasum": ""
},
"require": {
@ -483,16 +483,16 @@
},
"require-dev": {
"ext-zlib": "*",
"phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
"phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10"
},
"suggest": {
"zendframework/zend-httphandlerrunner": "Emit PSR-7 responses"
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
},
"time": "2019-07-01T23:21:34+00:00",
"time": "2020-09-30T07:37:11+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.6-dev"
"dev-master": "1.7-dev"
}
},
"installation-source": "dist",
@ -891,17 +891,17 @@
},
{
"name": "league/mime-type-detection",
"version": "1.5.0",
"version_normalized": "1.5.0.0",
"version": "1.5.1",
"version_normalized": "1.5.1.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/mime-type-detection.git",
"reference": "ea2fbfc988bade315acd5967e6d02274086d0f28"
"reference": "353f66d7555d8a90781f6f5e7091932f9a4250aa"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/ea2fbfc988bade315acd5967e6d02274086d0f28",
"reference": "ea2fbfc988bade315acd5967e6d02274086d0f28",
"url": "https://api.github.com/repos/thephpleague/mime-type-detection/zipball/353f66d7555d8a90781f6f5e7091932f9a4250aa",
"reference": "353f66d7555d8a90781f6f5e7091932f9a4250aa",
"shasum": ""
},
"require": {
@ -912,7 +912,7 @@
"phpstan/phpstan": "^0.12.36",
"phpunit/phpunit": "^8.5.8"
},
"time": "2020-09-21T18:10:53+00:00",
"time": "2020-10-18T11:50:25+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -944,32 +944,31 @@
},
{
"name": "league/oauth2-client",
"version": "2.5.0",
"version_normalized": "2.5.0.0",
"version": "2.6.0",
"version_normalized": "2.6.0.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/oauth2-client.git",
"reference": "d9f2a1e000dc14eb3c02e15d15759385ec7ff0fb"
"reference": "badb01e62383430706433191b82506b6df24ad98"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/d9f2a1e000dc14eb3c02e15d15759385ec7ff0fb",
"reference": "d9f2a1e000dc14eb3c02e15d15759385ec7ff0fb",
"url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/badb01e62383430706433191b82506b6df24ad98",
"reference": "badb01e62383430706433191b82506b6df24ad98",
"shasum": ""
},
"require": {
"guzzlehttp/guzzle": "^6.0 || ^7.0",
"paragonie/random_compat": "^1|^2|^9.99",
"php": "^5.6|^7.0"
"paragonie/random_compat": "^1 || ^2 || ^9.99",
"php": "^5.6 || ^7.0 || ^8.0"
},
"require-dev": {
"eloquent/liberator": "^2.0",
"eloquent/phony-phpunit": "^1.0|^3.0",
"jakub-onderka/php-parallel-lint": "^0.9.2",
"phpunit/phpunit": "^5.7|^6.0",
"squizlabs/php_codesniffer": "^2.3|^3.0"
"mockery/mockery": "^1.3",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpunit/phpunit": "^5.7 || ^6.0 || ^9.3",
"squizlabs/php_codesniffer": "^2.3 || ^3.0"
},
"time": "2020-07-18T17:54:32+00:00",
"time": "2020-10-28T02:03:40+00:00",
"type": "library",
"extra": {
"branch-alias": {
@ -1013,17 +1012,17 @@
},
{
"name": "league/oauth2-facebook",
"version": "2.0.2",
"version_normalized": "2.0.2.0",
"version": "2.0.3",
"version_normalized": "2.0.3.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/oauth2-facebook.git",
"reference": "c482a851c93a6cb718270773635d6a0c8384b560"
"reference": "093cf588b9d508ee426c71d6bc68f138fe914ddc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/oauth2-facebook/zipball/c482a851c93a6cb718270773635d6a0c8384b560",
"reference": "c482a851c93a6cb718270773635d6a0c8384b560",
"url": "https://api.github.com/repos/thephpleague/oauth2-facebook/zipball/093cf588b9d508ee426c71d6bc68f138fe914ddc",
"reference": "093cf588b9d508ee426c71d6bc68f138fe914ddc",
"shasum": ""
},
"require": {
@ -1036,7 +1035,7 @@
"phpunit/phpunit": "^5.7|^6.0",
"squizlabs/php_codesniffer": "~3.0"
},
"time": "2020-07-25T13:30:06+00:00",
"time": "2020-10-01T15:22:47+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -1293,17 +1292,17 @@
},
{
"name": "masterminds/html5",
"version": "2.7.3",
"version_normalized": "2.7.3.0",
"version": "2.7.4",
"version_normalized": "2.7.4.0",
"source": {
"type": "git",
"url": "https://github.com/Masterminds/html5-php.git",
"reference": "aad73dbfefd71d46072138109ce1288d96c329cc"
"reference": "9227822783c75406cfe400984b2f095cdf03d417"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Masterminds/html5-php/zipball/aad73dbfefd71d46072138109ce1288d96c329cc",
"reference": "aad73dbfefd71d46072138109ce1288d96c329cc",
"url": "https://api.github.com/repos/Masterminds/html5-php/zipball/9227822783c75406cfe400984b2f095cdf03d417",
"reference": "9227822783c75406cfe400984b2f095cdf03d417",
"shasum": ""
},
"require": {
@ -1313,11 +1312,9 @@
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35",
"sami/sami": "~2.0",
"satooshi/php-coveralls": "1.0.*"
"phpunit/phpunit": "^4.8.35"
},
"time": "2020-07-05T07:53:37+00:00",
"time": "2020-10-01T13:52:52+00:00",
"type": "library",
"extra": {
"branch-alias": {
@ -2449,17 +2446,17 @@
},
{
"name": "symfony/options-resolver",
"version": "v5.1.5",
"version_normalized": "5.1.5.0",
"version": "v5.1.8",
"version_normalized": "5.1.8.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
"reference": "9ff59517938f88d90b6e65311fef08faa640f681"
"reference": "c6a02905e4ffc7a1498e8ee019db2b477cd1cc02"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/9ff59517938f88d90b6e65311fef08faa640f681",
"reference": "9ff59517938f88d90b6e65311fef08faa640f681",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/c6a02905e4ffc7a1498e8ee019db2b477cd1cc02",
"reference": "c6a02905e4ffc7a1498e8ee019db2b477cd1cc02",
"shasum": ""
},
"require": {
@ -2467,13 +2464,8 @@
"symfony/deprecation-contracts": "^2.1",
"symfony/polyfill-php80": "^1.15"
},
"time": "2020-07-12T12:58:00+00:00",
"time": "2020-10-24T12:01:57+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.1-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
@ -2521,30 +2513,30 @@
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.18.1",
"version_normalized": "1.18.1.0",
"version": "v1.20.0",
"version_normalized": "1.20.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "1c302646f6efc070cd46856e600e5e0684d6b454"
"reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454",
"reference": "1c302646f6efc070cd46856e600e5e0684d6b454",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f4ba089a5b6366e453971d3aad5fe8e897b37f41",
"reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
"php": ">=7.1"
},
"suggest": {
"ext-ctype": "For best performance"
},
"time": "2020-07-14T12:35:20+00:00",
"time": "2020-10-23T14:02:19+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
"dev-main": "1.20-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -2599,33 +2591,32 @@
},
{
"name": "symfony/polyfill-intl-idn",
"version": "v1.18.1",
"version_normalized": "1.18.1.0",
"version": "v1.20.0",
"version_normalized": "1.20.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "5dcab1bc7146cf8c1beaa4502a3d9be344334251"
"reference": "3b75acd829741c768bc8b1f84eb33265e7cc5117"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/5dcab1bc7146cf8c1beaa4502a3d9be344334251",
"reference": "5dcab1bc7146cf8c1beaa4502a3d9be344334251",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/3b75acd829741c768bc8b1f84eb33265e7cc5117",
"reference": "3b75acd829741c768bc8b1f84eb33265e7cc5117",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"php": ">=7.1",
"symfony/polyfill-intl-normalizer": "^1.10",
"symfony/polyfill-php70": "^1.10",
"symfony/polyfill-php72": "^1.10"
},
"suggest": {
"ext-intl": "For best performance"
},
"time": "2020-08-04T06:02:08+00:00",
"time": "2020-10-23T14:02:19+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
"dev-main": "1.20-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -2686,30 +2677,30 @@
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.18.1",
"version_normalized": "1.18.1.0",
"version": "v1.20.0",
"version_normalized": "1.20.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e"
"reference": "727d1096295d807c309fb01a851577302394c897"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e",
"reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/727d1096295d807c309fb01a851577302394c897",
"reference": "727d1096295d807c309fb01a851577302394c897",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
"php": ">=7.1"
},
"suggest": {
"ext-intl": "For best performance"
},
"time": "2020-07-14T12:35:20+00:00",
"time": "2020-10-23T14:02:19+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
"dev-main": "1.20-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -2767,108 +2758,29 @@
}
]
},
{
"name": "symfony/polyfill-php70",
"version": "v1.18.1",
"version_normalized": "1.18.1.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php70.git",
"reference": "0dd93f2c578bdc9c72697eaa5f1dd25644e618d3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/0dd93f2c578bdc9c72697eaa5f1dd25644e618d3",
"reference": "0dd93f2c578bdc9c72697eaa5f1dd25644e618d3",
"shasum": ""
},
"require": {
"paragonie/random_compat": "~1.0|~2.0|~9.99",
"php": ">=5.3.3"
},
"time": "2020-07-14T12:35:20+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Php70\\": ""
},
"files": [
"bootstrap.php"
],
"classmap": [
"Resources/stubs"
]
},
"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 backporting some PHP 7.0+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
]
},
{
"name": "symfony/polyfill-php72",
"version": "v1.18.1",
"version_normalized": "1.18.1.0",
"version": "v1.20.0",
"version_normalized": "1.20.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "639447d008615574653fb3bc60d1986d7172eaae"
"reference": "cede45fcdfabdd6043b3592e83678e42ec69e930"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/639447d008615574653fb3bc60d1986d7172eaae",
"reference": "639447d008615574653fb3bc60d1986d7172eaae",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cede45fcdfabdd6043b3592e83678e42ec69e930",
"reference": "cede45fcdfabdd6043b3592e83678e42ec69e930",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
"php": ">=7.1"
},
"time": "2020-07-14T12:35:20+00:00",
"time": "2020-10-23T14:02:19+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
"dev-main": "1.20-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -2923,27 +2835,27 @@
},
{
"name": "symfony/polyfill-php80",
"version": "v1.18.1",
"version_normalized": "1.18.1.0",
"version": "v1.20.0",
"version_normalized": "1.20.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981"
"reference": "e70aa8b064c5b72d3df2abd5ab1e90464ad009de"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/d87d5766cbf48d72388a9f6b85f280c8ad51f981",
"reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/e70aa8b064c5b72d3df2abd5ab1e90464ad009de",
"reference": "e70aa8b064c5b72d3df2abd5ab1e90464ad009de",
"shasum": ""
},
"require": {
"php": ">=7.0.8"
"php": ">=7.1"
},
"time": "2020-07-14T12:35:20+00:00",
"time": "2020-10-23T14:02:19+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
"dev-main": "1.20-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -3005,30 +2917,25 @@
},
{
"name": "symfony/process",
"version": "v5.1.5",
"version_normalized": "5.1.5.0",
"version": "v5.1.8",
"version_normalized": "5.1.8.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "1864216226af21eb76d9477f691e7cbf198e0402"
"reference": "f00872c3f6804150d6a0f73b4151daab96248101"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/1864216226af21eb76d9477f691e7cbf198e0402",
"reference": "1864216226af21eb76d9477f691e7cbf198e0402",
"url": "https://api.github.com/repos/symfony/process/zipball/f00872c3f6804150d6a0f73b4151daab96248101",
"reference": "f00872c3f6804150d6a0f73b4151daab96248101",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"symfony/polyfill-php80": "^1.15"
},
"time": "2020-07-23T08:36:24+00:00",
"time": "2020-10-24T12:01:57+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.1-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-4": {
@ -3124,29 +3031,29 @@
},
{
"name": "voku/portable-ascii",
"version": "1.5.3",
"version_normalized": "1.5.3.0",
"version": "1.5.4",
"version_normalized": "1.5.4.0",
"source": {
"type": "git",
"url": "https://github.com/voku/portable-ascii.git",
"reference": "25bcbf01678930251fd572891447d9e318a6e2b8"
"reference": "87dc337d4200b32717dd3849fea846319e897658"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/voku/portable-ascii/zipball/25bcbf01678930251fd572891447d9e318a6e2b8",
"reference": "25bcbf01678930251fd572891447d9e318a6e2b8",
"url": "https://api.github.com/repos/voku/portable-ascii/zipball/87dc337d4200b32717dd3849fea846319e897658",
"reference": "87dc337d4200b32717dd3849fea846319e897658",
"shasum": ""
},
"require": {
"php": ">=7.0.0"
},
"require-dev": {
"phpunit/phpunit": "~6.0 || ~7.0"
"phpunit/phpunit": "~6.0 || ~7.0 || ~9.0"
},
"suggest": {
"ext-intl": "Use Intl for transliterator_transliterate() support"
},
"time": "2020-07-22T23:32:04+00:00",
"time": "2020-11-08T21:15:15+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {

88
vendor/guzzlehttp/promises/.php_cs.dist vendored Normal file
View file

@ -0,0 +1,88 @@
<?php
$config = PhpCsFixer\Config::create()
->setRiskyAllowed(true)
->setRules([
'@PSR2' => true,
'array_syntax' => ['syntax' => 'short'],
'binary_operator_spaces' => ['operators' => ['=>' => null]],
'blank_line_after_opening_tag' => true,
'class_attributes_separation' => ['elements' => ['method']],
'compact_nullable_typehint' => true,
'concat_space' => ['spacing' => 'one'],
'declare_equal_normalize' => ['space' => 'none'],
'declare_strict_types' => false,
'dir_constant' => true,
'final_static_access' => true,
'fully_qualified_strict_types' => true,
'function_to_constant' => true,
'function_typehint_space' => true,
'header_comment' => false,
'is_null' => ['use_yoda_style' => false],
'list_syntax' => ['syntax' => 'short'],
'lowercase_cast' => true,
'magic_method_casing' => true,
'modernize_types_casting' => true,
'multiline_comment_opening_closing' => true,
//'native_constant_invocation' => true,
'no_alias_functions' => true,
'no_alternative_syntax' => true,
'no_blank_lines_after_phpdoc' => true,
'no_empty_comment' => true,
'no_empty_phpdoc' => true,
'no_extra_blank_lines' => true,
'no_leading_import_slash' => true,
'no_leading_namespace_whitespace' => true,
'no_spaces_around_offset' => true,
'no_superfluous_phpdoc_tags' => ['allow_mixed' => true],
'no_trailing_comma_in_singleline_array' => true,
'no_unneeded_control_parentheses' => true,
'no_unset_cast' => true,
'no_unused_imports' => true,
'no_useless_else' => true,
'no_useless_return' => true,
'no_whitespace_in_blank_line' => true,
'normalize_index_brace' => true,
'ordered_imports' => true,
'php_unit_construct' => true,
'php_unit_dedicate_assert' => ['target' => 'newest'],
'php_unit_dedicate_assert_internal_type' => ['target' => 'newest'],
'php_unit_expectation' => ['target' => 'newest'],
'php_unit_mock' => ['target' => 'newest'],
'php_unit_mock_short_will_return' => true,
'php_unit_no_expectation_annotation' => ['target' => 'newest'],
'php_unit_test_annotation' => ['style' => 'prefix'],
//'php_unit_test_case_static_method_calls' => ['call_type' => 'self'],
'phpdoc_align' => ['align' => 'vertical'],
//'phpdoc_line_span' => ['method' => 'multi', 'property' => 'multi'],
'phpdoc_no_package' => true,
'phpdoc_no_useless_inheritdoc' => true,
'phpdoc_scalar' => true,
'phpdoc_separation' => true,
'phpdoc_single_line_var_spacing' => true,
'phpdoc_trim' => true,
'phpdoc_trim_consecutive_blank_line_separation' => true,
'phpdoc_types' => true,
'phpdoc_types_order' => ['null_adjustment' => 'always_last', 'sort_algorithm' => 'none'],
'phpdoc_var_without_name' => true,
'return_assignment' => true,
'short_scalar_cast' => true,
'single_trait_insert_per_statement' => true,
'standardize_not_equals' => true,
//'static_lambda' => true,
'ternary_to_null_coalescing' => true,
'trim_array_spaces' => true,
'visibility_required' => true,
'yoda_style' => false,
// 'native_function_invocation' => true,
'braces' => ['allow_single_line_closure'=>true],
])
->setFinder(
PhpCsFixer\Finder::create()
->in(__DIR__.'/src')
->in(__DIR__.'/tests')
->name('*.php')
)
;
return $config;

View file

@ -1,6 +1,22 @@
# CHANGELOG
## 1.4.0 - 2020-09-30
### Added
- Support for PHP 8
- Optional `$recursive` flag to `all`
- Replaced functions by static methods
### Fixed
- Fix empty `each` processing
- Fix promise handling for Iterators of non-unique keys
- Fixed `method_exists` crashes on PHP 8
- Memory leak on exceptions
## 1.3.1 - 2016-12-20
### Fixed

View file

@ -26,7 +26,7 @@ for a general introduction to promises.
- Promises can be cancelled.
- Works with any object that has a `then` function.
- C# style async/await coroutine promises using
`GuzzleHttp\Promise\coroutine()`.
`GuzzleHttp\Promise\Coroutine::of()`.
# Quick start
@ -88,7 +88,7 @@ $promise
});
// Resolving the promise triggers the $onFulfilled callbacks and outputs
// "Hello, reader".
// "Hello, reader."
$promise->resolve('reader.');
```
@ -150,7 +150,7 @@ use GuzzleHttp\Promise\Promise;
$promise = new Promise();
$promise->then(null, function ($reason) {
throw new \Exception($reason);
throw new Exception($reason);
})->then(null, function ($reason) {
assert($reason->getMessage() === 'Error!');
});
@ -182,7 +182,6 @@ invoked using the value returned from the `$onRejected` callback.
```php
use GuzzleHttp\Promise\Promise;
use GuzzleHttp\Promise\RejectedPromise;
$promise = new Promise();
$promise
@ -220,7 +219,7 @@ the promise is rejected with the exception and the exception is thrown.
```php
$promise = new Promise(function () use (&$promise) {
throw new \Exception('foo');
throw new Exception('foo');
});
$promise->wait(); // throws the exception.
@ -397,7 +396,7 @@ $deferred = new React\Promise\Deferred();
$reactPromise = $deferred->promise();
// Create a Guzzle promise that is fulfilled with a React promise.
$guzzlePromise = new \GuzzleHttp\Promise\Promise();
$guzzlePromise = new GuzzleHttp\Promise\Promise();
$guzzlePromise->then(function ($value) use ($reactPromise) {
// Do something something with the value...
// Return the React promise
@ -424,7 +423,7 @@ instance.
```php
// Get the global task queue
$queue = \GuzzleHttp\Promise\queue();
$queue = GuzzleHttp\Promise\Utils::queue();
$queue->run();
```
@ -502,3 +501,32 @@ $promise->then(function ($value) { echo $value; });
$promise->resolve('foo');
// prints "foo"
```
## Upgrading from Function API
A static API was first introduced in 1.4.0, in order to mitigate problems with functions conflicting between global and local copies of the package. The function API will be removed in 2.0.0. A migration table has been provided here for your convenience:
| Original Function | Replacement Method |
|----------------|----------------|
| `queue` | `Utils::queue` |
| `task` | `Utils::task` |
| `promise_for` | `Create::promiseFor` |
| `rejection_for` | `Create::rejectionFor` |
| `exception_for` | `Create::exceptionFor` |
| `iter_for` | `Create::iterFor` |
| `inspect` | `Utils::inspect` |
| `inspect_all` | `Utils::inspectAll` |
| `unwrap` | `Utils::unwrap` |
| `all` | `Utils::all` |
| `some` | `Utils::some` |
| `any` | `Utils::any` |
| `settle` | `Utils::settle` |
| `each` | `Each::of` |
| `each_limit` | `Each::ofLimit` |
| `each_limit_all` | `Each::ofLimitAll` |
| `!is_fulfilled` | `Is::pending` |
| `is_fulfilled` | `Is::fulfilled` |
| `is_rejected` | `Is::rejected` |
| `is_settled` | `Is::settled` |
| `coroutine` | `Coroutine::of` |

View file

@ -11,10 +11,10 @@
}
],
"require": {
"php": ">=5.5.0"
"php": ">=5.5"
},
"require-dev": {
"phpunit/phpunit": "^4.0"
"symfony/phpunit-bridge": "^4.4 || ^5.1"
},
"autoload": {
"psr-4": {
@ -22,9 +22,14 @@
},
"files": ["src/functions_include.php"]
},
"autoload-dev": {
"psr-4": {
"GuzzleHttp\\Promise\\Tests\\": "tests/"
}
},
"scripts": {
"test": "vendor/bin/phpunit",
"test-ci": "vendor/bin/phpunit --coverage-text"
"test": "vendor/bin/simple-phpunit",
"test-ci": "vendor/bin/simple-phpunit --coverage-text"
},
"extra": {
"branch-alias": {

View file

@ -0,0 +1,7 @@
parameters:
ignoreErrors:
-
message: "#^Parameter \\#1 \\$function of function register_shutdown_function expects callable\\(\\)\\: void, Closure\\(\\)\\: mixed given\\.$#"
count: 1
path: src/TaskQueue.php

View file

@ -0,0 +1,10 @@
includes:
- phpstan-baseline.neon
parameters:
level: 5
paths:
- src
ignoreErrors:
- "#^Dead catch - Exception is already caught by Throwable above\\.$#"

15
vendor/guzzlehttp/promises/psalm.xml vendored Normal file
View file

@ -0,0 +1,15 @@
<?xml version="1.0"?>
<psalm
errorLevel="4"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
</psalm>

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Promise;
/**

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Promise;
/**

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Promise;
use Exception;
@ -9,7 +10,7 @@ use Throwable;
* Creates a promise that is resolved using a generator that yields values or
* promises (somewhat similar to C#'s async keyword).
*
* When called, the coroutine function will start an instance of the generator
* When called, the Coroutine::of method will start an instance of the generator
* and returns a promise that is fulfilled with its final yielded value.
*
* Control is returned back to the generator when the yielded promise settles.
@ -22,7 +23,7 @@ use Throwable;
* return new Promise\FulfilledPromise($value);
* }
*
* $promise = Promise\coroutine(function () {
* $promise = Promise\Coroutine::of(function () {
* $value = (yield createPromise('a'));
* try {
* $value = (yield createPromise($value . 'b'));
@ -38,6 +39,7 @@ use Throwable;
* @param callable $generatorFn Generator function to wrap into a promise.
*
* @return Promise
*
* @link https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration
*/
final class Coroutine implements PromiseInterface
@ -65,7 +67,23 @@ final class Coroutine implements PromiseInterface
$this->currentPromise->wait();
}
});
$this->nextCoroutine($this->generator->current());
try {
$this->nextCoroutine($this->generator->current());
} catch (\Exception $exception) {
$this->result->reject($exception);
} catch (Throwable $throwable) {
$this->result->reject($throwable);
}
}
/**
* Create a new coroutine.
*
* @return self
*/
public static function of(callable $generatorFn)
{
return new self($generatorFn);
}
public function then(
@ -108,7 +126,7 @@ final class Coroutine implements PromiseInterface
private function nextCoroutine($yielded)
{
$this->currentPromise = promise_for($yielded)
$this->currentPromise = Create::promiseFor($yielded)
->then([$this, '_handleSuccess'], [$this, '_handleFailure']);
}
@ -139,7 +157,7 @@ final class Coroutine implements PromiseInterface
{
unset($this->currentPromise);
try {
$nextYield = $this->generator->throw(exception_for($reason));
$nextYield = $this->generator->throw(Create::exceptionFor($reason));
// The throw was caught, so keep iterating on the coroutine
$this->nextCoroutine($nextYield);
} catch (Exception $exception) {

View file

@ -0,0 +1,84 @@
<?php
namespace GuzzleHttp\Promise;
final class Create
{
/**
* Creates a promise for a value if the value is not a promise.
*
* @param mixed $value Promise or value.
*
* @return PromiseInterface
*/
public static function promiseFor($value)
{
if ($value instanceof PromiseInterface) {
return $value;
}
// Return a Guzzle promise that shadows the given promise.
if (is_object($value) && method_exists($value, 'then')) {
$wfn = method_exists($value, 'wait') ? [$value, 'wait'] : null;
$cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null;
$promise = new Promise($wfn, $cfn);
$value->then([$promise, 'resolve'], [$promise, 'reject']);
return $promise;
}
return new FulfilledPromise($value);
}
/**
* Creates a rejected promise for a reason if the reason is not a promise.
* If the provided reason is a promise, then it is returned as-is.
*
* @param mixed $reason Promise or reason.
*
* @return PromiseInterface
*/
public static function rejectionFor($reason)
{
if ($reason instanceof PromiseInterface) {
return $reason;
}
return new RejectedPromise($reason);
}
/**
* Create an exception for a rejected promise value.
*
* @param mixed $reason
*
* @return \Exception|\Throwable
*/
public static function exceptionFor($reason)
{
if ($reason instanceof \Exception || $reason instanceof \Throwable) {
return $reason;
}
return new RejectionException($reason);
}
/**
* Returns an iterator for the given value.
*
* @param mixed $value
*
* @return \Iterator
*/
public static function iterFor($value)
{
if ($value instanceof \Iterator) {
return $value;
}
if (is_array($value)) {
return new \ArrayIterator($value);
}
return new \ArrayIterator([$value]);
}
}

90
vendor/guzzlehttp/promises/src/Each.php vendored Normal file
View file

@ -0,0 +1,90 @@
<?php
namespace GuzzleHttp\Promise;
final class Each
{
/**
* Given an iterator that yields promises or values, returns a promise that
* is fulfilled with a null value when the iterator has been consumed or
* the aggregate promise has been fulfilled or rejected.
*
* $onFulfilled is a function that accepts the fulfilled value, iterator
* index, and the aggregate promise. The callback can invoke any necessary
* side effects and choose to resolve or reject the aggregate if needed.
*
* $onRejected is a function that accepts the rejection reason, iterator
* index, and the aggregate promise. The callback can invoke any necessary
* side effects and choose to resolve or reject the aggregate if needed.
*
* @param mixed $iterable Iterator or array to iterate over.
* @param callable $onFulfilled
* @param callable $onRejected
*
* @return PromiseInterface
*/
public static function of(
$iterable,
callable $onFulfilled = null,
callable $onRejected = null
) {
return (new EachPromise($iterable, [
'fulfilled' => $onFulfilled,
'rejected' => $onRejected
]))->promise();
}
/**
* Like of, but only allows a certain number of outstanding promises at any
* given time.
*
* $concurrency may be an integer or a function that accepts the number of
* pending promises and returns a numeric concurrency limit value to allow
* for dynamic a concurrency size.
*
* @param mixed $iterable
* @param int|callable $concurrency
* @param callable $onFulfilled
* @param callable $onRejected
*
* @return PromiseInterface
*/
public static function ofLimit(
$iterable,
$concurrency,
callable $onFulfilled = null,
callable $onRejected = null
) {
return (new EachPromise($iterable, [
'fulfilled' => $onFulfilled,
'rejected' => $onRejected,
'concurrency' => $concurrency
]))->promise();
}
/**
* Like limit, but ensures that no promise in the given $iterable argument
* is rejected. If any promise is rejected, then the aggregate promise is
* rejected with the encountered rejection.
*
* @param mixed $iterable
* @param int|callable $concurrency
* @param callable $onFulfilled
*
* @return PromiseInterface
*/
public static function ofLimitAll(
$iterable,
$concurrency,
callable $onFulfilled = null
) {
return each_limit(
$iterable,
$concurrency,
$onFulfilled,
function ($reason, $idx, PromiseInterface $aggregate) {
$aggregate->reject($reason);
}
);
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Promise;
/**
@ -9,22 +10,22 @@ class EachPromise implements PromisorInterface
{
private $pending = [];
/** @var \Iterator */
/** @var \Iterator|null */
private $iterable;
/** @var callable|int */
/** @var callable|int|null */
private $concurrency;
/** @var callable */
/** @var callable|null */
private $onFulfilled;
/** @var callable */
/** @var callable|null */
private $onRejected;
/** @var Promise */
/** @var Promise|null */
private $aggregate;
/** @var bool */
/** @var bool|null */
private $mutex;
/**
@ -45,12 +46,12 @@ class EachPromise implements PromisorInterface
* allowed number of outstanding concurrently executing promises,
* creating a capped pool of promises. There is no limit by default.
*
* @param mixed $iterable Promises or values to iterate.
* @param array $config Configuration options
* @param mixed $iterable Promises or values to iterate.
* @param array $config Configuration options
*/
public function __construct($iterable, array $config = [])
{
$this->iterable = iter_for($iterable);
$this->iterable = Create::iterFor($iterable);
if (isset($config['concurrency'])) {
$this->concurrency = $config['concurrency'];
@ -65,6 +66,7 @@ class EachPromise implements PromisorInterface
}
}
/** @psalm-suppress InvalidNullableReturnType */
public function promise()
{
if ($this->aggregate) {
@ -73,14 +75,29 @@ class EachPromise implements PromisorInterface
try {
$this->createPromise();
/** @psalm-assert Promise $this->aggregate */
$this->iterable->rewind();
$this->refillPending();
if (!$this->checkIfFinished()) {
$this->refillPending();
}
} catch (\Throwable $e) {
/**
* @psalm-suppress NullReference
* @phpstan-ignore-next-line
*/
$this->aggregate->reject($e);
} catch (\Exception $e) {
/**
* @psalm-suppress NullReference
* @phpstan-ignore-next-line
*/
$this->aggregate->reject($e);
}
/**
* @psalm-suppress NullableReturnStatement
* @phpstan-ignore-next-line
*/
return $this->aggregate;
}
@ -89,17 +106,12 @@ class EachPromise implements PromisorInterface
$this->mutex = false;
$this->aggregate = new Promise(function () {
reset($this->pending);
if (empty($this->pending) && !$this->iterable->valid()) {
$this->aggregate->resolve(null);
return;
}
// Consume a potentially fluctuating list of promises while
// ensuring that indexes are maintained (precluding array_shift).
while ($promise = current($this->pending)) {
next($this->pending);
$promise->wait();
if ($this->aggregate->getState() !== PromiseInterface::PENDING) {
if (Is::settled($this->aggregate)) {
return;
}
}
@ -148,22 +160,34 @@ class EachPromise implements PromisorInterface
return false;
}
$promise = promise_for($this->iterable->current());
$idx = $this->iterable->key();
$promise = Create::promiseFor($this->iterable->current());
$key = $this->iterable->key();
// Iterable keys may not be unique, so we add the promises at the end
// of the pending array and retrieve the array index being used
$this->pending[] = null;
end($this->pending);
$idx = key($this->pending);
$this->pending[$idx] = $promise->then(
function ($value) use ($idx) {
function ($value) use ($idx, $key) {
if ($this->onFulfilled) {
call_user_func(
$this->onFulfilled, $value, $idx, $this->aggregate
$this->onFulfilled,
$value,
$key,
$this->aggregate
);
}
$this->step($idx);
},
function ($reason) use ($idx) {
function ($reason) use ($idx, $key) {
if ($this->onRejected) {
call_user_func(
$this->onRejected, $reason, $idx, $this->aggregate
$this->onRejected,
$reason,
$key,
$this->aggregate
);
}
$this->step($idx);
@ -201,7 +225,7 @@ class EachPromise implements PromisorInterface
private function step($idx)
{
// If the promise was already resolved, then ignore this step.
if ($this->aggregate->getState() !== PromiseInterface::PENDING) {
if (Is::settled($this->aggregate)) {
return;
}

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Promise;
/**
@ -13,9 +14,10 @@ class FulfilledPromise implements PromiseInterface
public function __construct($value)
{
if (method_exists($value, 'then')) {
if (is_object($value) && method_exists($value, 'then')) {
throw new \InvalidArgumentException(
'You cannot create a FulfilledPromise with a promise.');
'You cannot create a FulfilledPromise with a promise.'
);
}
$this->value = $value;
@ -30,11 +32,11 @@ class FulfilledPromise implements PromiseInterface
return $this;
}
$queue = queue();
$queue = Utils::queue();
$p = new Promise([$queue, 'run']);
$value = $this->value;
$queue->add(static function () use ($p, $value, $onFulfilled) {
if ($p->getState() === self::PENDING) {
if (Is::pending($p)) {
try {
$p->resolve($onFulfilled($value));
} catch (\Throwable $e) {

46
vendor/guzzlehttp/promises/src/Is.php vendored Normal file
View file

@ -0,0 +1,46 @@
<?php
namespace GuzzleHttp\Promise;
final class Is
{
/**
* Returns true if a promise is pending.
*
* @return bool
*/
public static function pending(PromiseInterface $promise)
{
return $promise->getState() === PromiseInterface::PENDING;
}
/**
* Returns true if a promise is fulfilled or rejected.
*
* @return bool
*/
public static function settled(PromiseInterface $promise)
{
return $promise->getState() !== PromiseInterface::PENDING;
}
/**
* Returns true if a promise is fulfilled.
*
* @return bool
*/
public static function fulfilled(PromiseInterface $promise)
{
return $promise->getState() === PromiseInterface::FULFILLED;
}
/**
* Returns true if a promise is rejected.
*
* @return bool
*/
public static function rejected(PromiseInterface $promise)
{
return $promise->getState() === PromiseInterface::REJECTED;
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Promise;
/**
@ -41,14 +42,13 @@ class Promise implements PromiseInterface
// Return a fulfilled promise and immediately invoke any callbacks.
if ($this->state === self::FULFILLED) {
return $onFulfilled
? promise_for($this->result)->then($onFulfilled)
: promise_for($this->result);
$promise = Create::promiseFor($this->result);
return $onFulfilled ? $promise->then($onFulfilled) : $promise;
}
// It's either cancelled or rejected, so return a rejected promise
// and immediately invoke any callbacks.
$rejection = rejection_for($this->result);
$rejection = Create::rejectionFor($this->result);
return $onRejected ? $rejection->then(null, $onRejected) : $rejection;
}
@ -61,19 +61,15 @@ class Promise implements PromiseInterface
{
$this->waitIfPending();
$inner = $this->result instanceof PromiseInterface
? $this->result->wait($unwrap)
: $this->result;
if ($this->result instanceof PromiseInterface) {
return $this->result->wait($unwrap);
}
if ($unwrap) {
if ($this->result instanceof PromiseInterface
|| $this->state === self::FULFILLED
) {
return $inner;
} else {
// It's rejected so "unwrap" and throw an exception.
throw exception_for($inner);
if ($this->state === self::FULFILLED) {
return $this->result;
}
// It's rejected so "unwrap" and throw an exception.
throw Create::exceptionFor($this->result);
}
}
@ -103,6 +99,7 @@ class Promise implements PromiseInterface
}
// Reject the promise only if it wasn't rejected in a then callback.
/** @psalm-suppress RedundantCondition */
if ($this->state === self::PENDING) {
$this->reject(new CancellationException('Promise has been cancelled'));
}
@ -148,17 +145,15 @@ class Promise implements PromiseInterface
// If the value was not a settled promise or a thenable, then resolve
// it in the task queue using the correct ID.
if (!method_exists($value, 'then')) {
if (!is_object($value) || !method_exists($value, 'then')) {
$id = $state === self::FULFILLED ? 1 : 2;
// It's a success, so resolve the handlers in the queue.
queue()->add(static function () use ($id, $value, $handlers) {
Utils::queue()->add(static function () use ($id, $value, $handlers) {
foreach ($handlers as $handler) {
self::callHandler($id, $value, $handler);
}
});
} elseif ($value instanceof Promise
&& $value->getState() === self::PENDING
) {
} elseif ($value instanceof Promise && Is::pending($value)) {
// We can just merge our handlers onto the next promise.
$value->handlers = array_merge($value->handlers, $handlers);
} else {
@ -184,8 +179,6 @@ class Promise implements PromiseInterface
* @param int $index 1 (resolve) or 2 (reject).
* @param mixed $value Value to pass to the callback.
* @param array $handler Array of handler data (promise and callbacks).
*
* @return array Returns the next group to resolve.
*/
private static function callHandler($index, $value, array $handler)
{
@ -194,13 +187,21 @@ class Promise implements PromiseInterface
// The promise may have been cancelled or resolved before placing
// this thunk in the queue.
if ($promise->getState() !== self::PENDING) {
if (Is::settled($promise)) {
return;
}
try {
if (isset($handler[$index])) {
$promise->resolve($handler[$index]($value));
/*
* If $f throws an exception, then $handler will be in the exception
* stack trace. Since $handler contains a reference to the callable
* itself we get a circular reference. We clear the $handler
* here to avoid that memory leak.
*/
$f = $handler[$index];
unset($handler);
$promise->resolve($f($value));
} elseif ($index === 1) {
// Forward resolution values as-is.
$promise->resolve($value);
@ -224,15 +225,16 @@ class Promise implements PromiseInterface
} elseif ($this->waitList) {
$this->invokeWaitList();
} else {
// If there's not wait function, then reject the promise.
// If there's no wait function, then reject the promise.
$this->reject('Cannot wait on a promise that has '
. 'no internal wait function. You must provide a wait '
. 'function when constructing the promise to be able to '
. 'wait on a promise.');
}
queue()->run();
Utils::queue()->run();
/** @psalm-suppress RedundantCondition */
if ($this->state === self::PENDING) {
$this->reject('Invoking the wait callback did not resolve the promise');
}
@ -263,17 +265,13 @@ class Promise implements PromiseInterface
$this->waitList = null;
foreach ($waitList as $result) {
while (true) {
do {
$result->waitIfPending();
$result = $result->result;
} while ($result instanceof Promise);
if ($result->result instanceof Promise) {
$result = $result->result;
} else {
if ($result->result instanceof PromiseInterface) {
$result->result->wait(false);
}
break;
}
if ($result instanceof PromiseInterface) {
$result->wait(false);
}
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Promise;
/**
@ -56,6 +57,7 @@ interface PromiseInterface
* Resolve the promise with the given value.
*
* @param mixed $value
*
* @throws \RuntimeException if the promise is already resolved.
*/
public function resolve($value);
@ -64,6 +66,7 @@ interface PromiseInterface
* Reject the promise with the given reason.
*
* @param mixed $reason
*
* @throws \RuntimeException if the promise is already resolved.
*/
public function reject($reason);
@ -86,6 +89,7 @@ interface PromiseInterface
* @param bool $unwrap
*
* @return mixed
*
* @throws \LogicException if the promise has no wait function or if the
* promise does not settle after waiting.
*/

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Promise;
/**

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Promise;
/**
@ -13,9 +14,10 @@ class RejectedPromise implements PromiseInterface
public function __construct($reason)
{
if (method_exists($reason, 'then')) {
if (is_object($reason) && method_exists($reason, 'then')) {
throw new \InvalidArgumentException(
'You cannot create a RejectedPromise with a promise.');
'You cannot create a RejectedPromise with a promise.'
);
}
$this->reason = $reason;
@ -30,11 +32,11 @@ class RejectedPromise implements PromiseInterface
return $this;
}
$queue = queue();
$queue = Utils::queue();
$reason = $this->reason;
$p = new Promise([$queue, 'run']);
$queue->add(static function () use ($p, $reason, $onRejected) {
if ($p->getState() === self::PENDING) {
if (Is::pending($p)) {
try {
// Return a resolved promise if onRejected does not throw.
$p->resolve($onRejected($reason));
@ -59,8 +61,10 @@ class RejectedPromise implements PromiseInterface
public function wait($unwrap = true, $defaultDelivery = null)
{
if ($unwrap) {
throw exception_for($this->reason);
throw Create::exceptionFor($this->reason);
}
return null;
}
public function getState()

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Promise;
/**
@ -12,7 +13,7 @@ class RejectionException extends \RuntimeException
private $reason;
/**
* @param mixed $reason Rejection reason.
* @param mixed $reason Rejection reason.
* @param string $description Optional description
*/
public function __construct($reason, $description = null)

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Promise;
/**
@ -8,7 +9,7 @@ namespace GuzzleHttp\Promise;
* maintains a constant stack size. You can use the task queue asynchronously
* by calling the `run()` function of the global task queue in an event loop.
*
* GuzzleHttp\Promise\queue()->run();
* GuzzleHttp\Promise\Utils::queue()->run();
*/
class TaskQueue implements TaskQueueInterface
{
@ -42,8 +43,8 @@ class TaskQueue implements TaskQueueInterface
public function run()
{
/** @var callable $task */
while ($task = array_shift($this->queue)) {
/** @var callable $task */
$task();
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Promise;
interface TaskQueueInterface
@ -13,8 +14,6 @@ interface TaskQueueInterface
/**
* Adds a task to the queue that will be executed the next time run is
* called.
*
* @param callable $task
*/
public function add(callable $task);

274
vendor/guzzlehttp/promises/src/Utils.php vendored Normal file
View file

@ -0,0 +1,274 @@
<?php
namespace GuzzleHttp\Promise;
final class Utils
{
/**
* Get the global task queue used for promise resolution.
*
* This task queue MUST be run in an event loop in order for promises to be
* settled asynchronously. It will be automatically run when synchronously
* waiting on a promise.
*
* <code>
* while ($eventLoop->isRunning()) {
* GuzzleHttp\Promise\Utils::queue()->run();
* }
* </code>
*
* @param TaskQueueInterface $assign Optionally specify a new queue instance.
*
* @return TaskQueueInterface
*/
public static function queue(TaskQueueInterface $assign = null)
{
static $queue;
if ($assign) {
$queue = $assign;
} elseif (!$queue) {
$queue = new TaskQueue();
}
return $queue;
}
/**
* Adds a function to run in the task queue when it is next `run()` and
* returns a promise that is fulfilled or rejected with the result.
*
* @param callable $task Task function to run.
*
* @return PromiseInterface
*/
public static function task(callable $task)
{
$queue = self::queue();
$promise = new Promise([$queue, 'run']);
$queue->add(function () use ($task, $promise) {
try {
$promise->resolve($task());
} catch (\Throwable $e) {
$promise->reject($e);
} catch (\Exception $e) {
$promise->reject($e);
}
});
return $promise;
}
/**
* Synchronously waits on a promise to resolve and returns an inspection
* state array.
*
* Returns a state associative array containing a "state" key mapping to a
* valid promise state. If the state of the promise is "fulfilled", the
* array will contain a "value" key mapping to the fulfilled value of the
* promise. If the promise is rejected, the array will contain a "reason"
* key mapping to the rejection reason of the promise.
*
* @param PromiseInterface $promise Promise or value.
*
* @return array
*/
public static function inspect(PromiseInterface $promise)
{
try {
return [
'state' => PromiseInterface::FULFILLED,
'value' => $promise->wait()
];
} catch (RejectionException $e) {
return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()];
} catch (\Throwable $e) {
return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
} catch (\Exception $e) {
return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
}
}
/**
* Waits on all of the provided promises, but does not unwrap rejected
* promises as thrown exception.
*
* Returns an array of inspection state arrays.
*
* @see inspect for the inspection state array format.
*
* @param PromiseInterface[] $promises Traversable of promises to wait upon.
*
* @return array
*/
public static function inspectAll($promises)
{
$results = [];
foreach ($promises as $key => $promise) {
$results[$key] = inspect($promise);
}
return $results;
}
/**
* Waits on all of the provided promises and returns the fulfilled values.
*
* Returns an array that contains the value of each promise (in the same
* order the promises were provided). An exception is thrown if any of the
* promises are rejected.
*
* @param iterable<PromiseInterface> $promises Iterable of PromiseInterface objects to wait on.
*
* @return array
*
* @throws \Exception on error
* @throws \Throwable on error in PHP >=7
*/
public static function unwrap($promises)
{
$results = [];
foreach ($promises as $key => $promise) {
$results[$key] = $promise->wait();
}
return $results;
}
/**
* Given an array of promises, return a promise that is fulfilled when all
* the items in the array are fulfilled.
*
* The promise's fulfillment value is an array with fulfillment values at
* respective positions to the original array. If any promise in the array
* rejects, the returned promise is rejected with the rejection reason.
*
* @param mixed $promises Promises or values.
* @param bool $recursive If true, resolves new promises that might have been added to the stack during its own resolution.
*
* @return PromiseInterface
*/
public static function all($promises, $recursive = false)
{
$results = [];
$promise = Each::of(
$promises,
function ($value, $idx) use (&$results) {
$results[$idx] = $value;
},
function ($reason, $idx, Promise $aggregate) {
$aggregate->reject($reason);
}
)->then(function () use (&$results) {
ksort($results);
return $results;
});
if (true === $recursive) {
$promise = $promise->then(function ($results) use ($recursive, &$promises) {
foreach ($promises as $promise) {
if (Is::pending($promise)) {
return self::all($promises, $recursive);
}
}
return $results;
});
}
return $promise;
}
/**
* Initiate a competitive race between multiple promises or values (values
* will become immediately fulfilled promises).
*
* When count amount of promises have been fulfilled, the returned promise
* is fulfilled with an array that contains the fulfillment values of the
* winners in order of resolution.
*
* This promise is rejected with a {@see AggregateException} if the number
* of fulfilled promises is less than the desired $count.
*
* @param int $count Total number of promises.
* @param mixed $promises Promises or values.
*
* @return PromiseInterface
*/
public static function some($count, $promises)
{
$results = [];
$rejections = [];
return Each::of(
$promises,
function ($value, $idx, PromiseInterface $p) use (&$results, $count) {
if (Is::settled($p)) {
return;
}
$results[$idx] = $value;
if (count($results) >= $count) {
$p->resolve(null);
}
},
function ($reason) use (&$rejections) {
$rejections[] = $reason;
}
)->then(
function () use (&$results, &$rejections, $count) {
if (count($results) !== $count) {
throw new AggregateException(
'Not enough promises to fulfill count',
$rejections
);
}
ksort($results);
return array_values($results);
}
);
}
/**
* Like some(), with 1 as count. However, if the promise fulfills, the
* fulfillment value is not an array of 1 but the value directly.
*
* @param mixed $promises Promises or values.
*
* @return PromiseInterface
*/
public static function any($promises)
{
return self::some(1, $promises)->then(function ($values) {
return $values[0];
});
}
/**
* Returns a promise that is fulfilled when all of the provided promises have
* been fulfilled or rejected.
*
* The returned promise is fulfilled with an array of inspection state arrays.
*
* @see inspect for the inspection state array format.
*
* @param mixed $promises Promises or values.
*
* @return PromiseInterface
*/
public static function settle($promises)
{
$results = [];
return Each::of(
$promises,
function ($value, $idx) use (&$results) {
$results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
},
function ($reason, $idx) use (&$results) {
$results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
}
)->then(function () use (&$results) {
ksort($results);
return $results;
});
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Promise;
/**
@ -17,18 +18,12 @@ namespace GuzzleHttp\Promise;
* @param TaskQueueInterface $assign Optionally specify a new queue instance.
*
* @return TaskQueueInterface
*
* @deprecated queue will be removed in guzzlehttp/promises:2.0. Use Utils::queue instead.
*/
function queue(TaskQueueInterface $assign = null)
{
static $queue;
if ($assign) {
$queue = $assign;
} elseif (!$queue) {
$queue = new TaskQueue();
}
return $queue;
return Utils::queue($assign);
}
/**
@ -38,22 +33,12 @@ function queue(TaskQueueInterface $assign = null)
* @param callable $task Task function to run.
*
* @return PromiseInterface
*
* @deprecated task will be removed in guzzlehttp/promises:2.0. Use Utils::task instead.
*/
function task(callable $task)
{
$queue = queue();
$promise = new Promise([$queue, 'run']);
$queue->add(function () use ($task, $promise) {
try {
$promise->resolve($task());
} catch (\Throwable $e) {
$promise->reject($e);
} catch (\Exception $e) {
$promise->reject($e);
}
});
return $promise;
return Utils::task($task);
}
/**
@ -62,23 +47,12 @@ function task(callable $task)
* @param mixed $value Promise or value.
*
* @return PromiseInterface
*
* @deprecated promise_for will be removed in guzzlehttp/promises:2.0. Use Create::promiseFor instead.
*/
function promise_for($value)
{
if ($value instanceof PromiseInterface) {
return $value;
}
// Return a Guzzle promise that shadows the given promise.
if (method_exists($value, 'then')) {
$wfn = method_exists($value, 'wait') ? [$value, 'wait'] : null;
$cfn = method_exists($value, 'cancel') ? [$value, 'cancel'] : null;
$promise = new Promise($wfn, $cfn);
$value->then([$promise, 'resolve'], [$promise, 'reject']);
return $promise;
}
return new FulfilledPromise($value);
return Create::promiseFor($value);
}
/**
@ -88,14 +62,12 @@ function promise_for($value)
* @param mixed $reason Promise or reason.
*
* @return PromiseInterface
*
* @deprecated rejection_for will be removed in guzzlehttp/promises:2.0. Use Create::rejectionFor instead.
*/
function rejection_for($reason)
{
if ($reason instanceof PromiseInterface) {
return $reason;
}
return new RejectedPromise($reason);
return Create::rejectionFor($reason);
}
/**
@ -104,12 +76,12 @@ function rejection_for($reason)
* @param mixed $reason
*
* @return \Exception|\Throwable
*
* @deprecated exception_for will be removed in guzzlehttp/promises:2.0. Use Create::exceptionFor instead.
*/
function exception_for($reason)
{
return $reason instanceof \Exception || $reason instanceof \Throwable
? $reason
: new RejectionException($reason);
return Create::exceptionFor($reason);
}
/**
@ -118,16 +90,12 @@ function exception_for($reason)
* @param mixed $value
*
* @return \Iterator
*
* @deprecated iter_for will be removed in guzzlehttp/promises:2.0. Use Create::iterFor instead.
*/
function iter_for($value)
{
if ($value instanceof \Iterator) {
return $value;
} elseif (is_array($value)) {
return new \ArrayIterator($value);
} else {
return new \ArrayIterator([$value]);
}
return Create::iterFor($value);
}
/**
@ -143,21 +111,12 @@ function iter_for($value)
* @param PromiseInterface $promise Promise or value.
*
* @return array
*
* @deprecated inspect will be removed in guzzlehttp/promises:2.0. Use Utils::inspect instead.
*/
function inspect(PromiseInterface $promise)
{
try {
return [
'state' => PromiseInterface::FULFILLED,
'value' => $promise->wait()
];
} catch (RejectionException $e) {
return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()];
} catch (\Throwable $e) {
return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
} catch (\Exception $e) {
return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
}
return Utils::inspect($promise);
}
/**
@ -166,19 +125,17 @@ function inspect(PromiseInterface $promise)
*
* Returns an array of inspection state arrays.
*
* @see inspect for the inspection state array format.
*
* @param PromiseInterface[] $promises Traversable of promises to wait upon.
*
* @return array
* @see GuzzleHttp\Promise\inspect for the inspection state array format.
*
* @deprecated inspect will be removed in guzzlehttp/promises:2.0. Use Utils::inspectAll instead.
*/
function inspect_all($promises)
{
$results = [];
foreach ($promises as $key => $promise) {
$results[$key] = inspect($promise);
}
return $results;
return Utils::inspectAll($promises);
}
/**
@ -188,20 +145,18 @@ function inspect_all($promises)
* the promises were provided). An exception is thrown if any of the promises
* are rejected.
*
* @param mixed $promises Iterable of PromiseInterface objects to wait on.
* @param iterable<PromiseInterface> $promises Iterable of PromiseInterface objects to wait on.
*
* @return array
*
* @throws \Exception on error
* @throws \Throwable on error in PHP >=7
*
* @deprecated unwrap will be removed in guzzlehttp/promises:2.0. Use Utils::unwrap instead.
*/
function unwrap($promises)
{
$results = [];
foreach ($promises as $key => $promise) {
$results[$key] = $promise->wait();
}
return $results;
return Utils::unwrap($promises);
}
/**
@ -212,25 +167,16 @@ function unwrap($promises)
* respective positions to the original array. If any promise in the array
* rejects, the returned promise is rejected with the rejection reason.
*
* @param mixed $promises Promises or values.
* @param mixed $promises Promises or values.
* @param bool $recursive If true, resolves new promises that might have been added to the stack during its own resolution.
*
* @return PromiseInterface
*
* @deprecated all will be removed in guzzlehttp/promises:2.0. Use Utils::all instead.
*/
function all($promises)
function all($promises, $recursive = false)
{
$results = [];
return each(
$promises,
function ($value, $idx) use (&$results) {
$results[$idx] = $value;
},
function ($reason, $idx, Promise $aggregate) {
$aggregate->reject($reason);
}
)->then(function () use (&$results) {
ksort($results);
return $results;
});
return Utils::all($promises, $recursive);
}
/**
@ -241,45 +187,19 @@ function all($promises)
* fulfilled with an array that contains the fulfillment values of the winners
* in order of resolution.
*
* This prommise is rejected with a {@see GuzzleHttp\Promise\AggregateException}
* if the number of fulfilled promises is less than the desired $count.
* This promise is rejected with a {@see AggregateException} if the number of
* fulfilled promises is less than the desired $count.
*
* @param int $count Total number of promises.
* @param mixed $promises Promises or values.
*
* @return PromiseInterface
*
* @deprecated some will be removed in guzzlehttp/promises:2.0. Use Utils::some instead.
*/
function some($count, $promises)
{
$results = [];
$rejections = [];
return each(
$promises,
function ($value, $idx, PromiseInterface $p) use (&$results, $count) {
if ($p->getState() !== PromiseInterface::PENDING) {
return;
}
$results[$idx] = $value;
if (count($results) >= $count) {
$p->resolve(null);
}
},
function ($reason) use (&$rejections) {
$rejections[] = $reason;
}
)->then(
function () use (&$results, &$rejections, $count) {
if (count($results) !== $count) {
throw new AggregateException(
'Not enough promises to fulfill count',
$rejections
);
}
ksort($results);
return array_values($results);
}
);
return Utils::some($count, $promises);
}
/**
@ -289,10 +209,12 @@ function some($count, $promises)
* @param mixed $promises Promises or values.
*
* @return PromiseInterface
*
* @deprecated any will be removed in guzzlehttp/promises:2.0. Use Utils::any instead.
*/
function any($promises)
{
return some(1, $promises)->then(function ($values) { return $values[0]; });
return Utils::any($promises);
}
/**
@ -301,27 +223,17 @@ function any($promises)
*
* The returned promise is fulfilled with an array of inspection state arrays.
*
* @see inspect for the inspection state array format.
*
* @param mixed $promises Promises or values.
*
* @return PromiseInterface
* @see GuzzleHttp\Promise\inspect for the inspection state array format.
*
* @deprecated settle will be removed in guzzlehttp/promises:2.0. Use Utils::settle instead.
*/
function settle($promises)
{
$results = [];
return each(
$promises,
function ($value, $idx) use (&$results) {
$results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
},
function ($reason, $idx) use (&$results) {
$results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
}
)->then(function () use (&$results) {
ksort($results);
return $results;
});
return Utils::settle($promises);
}
/**
@ -329,29 +241,28 @@ function settle($promises)
* fulfilled with a null value when the iterator has been consumed or the
* aggregate promise has been fulfilled or rejected.
*
* $onFulfilled is a function that accepts the fulfilled value, iterator
* index, and the aggregate promise. The callback can invoke any necessary side
* effects and choose to resolve or reject the aggregate promise if needed.
* $onFulfilled is a function that accepts the fulfilled value, iterator index,
* and the aggregate promise. The callback can invoke any necessary side
* effects and choose to resolve or reject the aggregate if needed.
*
* $onRejected is a function that accepts the rejection reason, iterator
* index, and the aggregate promise. The callback can invoke any necessary side
* effects and choose to resolve or reject the aggregate promise if needed.
* $onRejected is a function that accepts the rejection reason, iterator index,
* and the aggregate promise. The callback can invoke any necessary side
* effects and choose to resolve or reject the aggregate if needed.
*
* @param mixed $iterable Iterator or array to iterate over.
* @param callable $onFulfilled
* @param callable $onRejected
*
* @return PromiseInterface
*
* @deprecated each will be removed in guzzlehttp/promises:2.0. Use Each::of instead.
*/
function each(
$iterable,
callable $onFulfilled = null,
callable $onRejected = null
) {
return (new EachPromise($iterable, [
'fulfilled' => $onFulfilled,
'rejected' => $onRejected
]))->promise();
return Each::of($iterable, $onFulfilled, $onRejected);
}
/**
@ -368,6 +279,8 @@ function each(
* @param callable $onRejected
*
* @return PromiseInterface
*
* @deprecated each_limit will be removed in guzzlehttp/promises:2.0. Use Each::ofLimit instead.
*/
function each_limit(
$iterable,
@ -375,11 +288,7 @@ function each_limit(
callable $onFulfilled = null,
callable $onRejected = null
) {
return (new EachPromise($iterable, [
'fulfilled' => $onFulfilled,
'rejected' => $onRejected,
'concurrency' => $concurrency
]))->promise();
return Each::ofLimit($iterable, $concurrency, $onFulfilled, $onRejected);
}
/**
@ -392,66 +301,63 @@ function each_limit(
* @param callable $onFulfilled
*
* @return PromiseInterface
*
* @deprecated each_limit_all will be removed in guzzlehttp/promises:2.0. Use Each::ofLimitAll instead.
*/
function each_limit_all(
$iterable,
$concurrency,
callable $onFulfilled = null
) {
return each_limit(
$iterable,
$concurrency,
$onFulfilled,
function ($reason, $idx, PromiseInterface $aggregate) {
$aggregate->reject($reason);
}
);
return Each::ofLimitAll($iterable, $concurrency, $onFulfilled);
}
/**
* Returns true if a promise is fulfilled.
*
* @param PromiseInterface $promise
*
* @return bool
*
* @deprecated is_fulfilled will be removed in guzzlehttp/promises:2.0. Use Is::fulfilled instead.
*/
function is_fulfilled(PromiseInterface $promise)
{
return $promise->getState() === PromiseInterface::FULFILLED;
return Is::fulfilled($promise);
}
/**
* Returns true if a promise is rejected.
*
* @param PromiseInterface $promise
*
* @return bool
*
* @deprecated is_rejected will be removed in guzzlehttp/promises:2.0. Use Is::rejected instead.
*/
function is_rejected(PromiseInterface $promise)
{
return $promise->getState() === PromiseInterface::REJECTED;
return Is::rejected($promise);
}
/**
* Returns true if a promise is fulfilled or rejected.
*
* @param PromiseInterface $promise
*
* @return bool
*
* @deprecated is_settled will be removed in guzzlehttp/promises:2.0. Use Is::settled instead.
*/
function is_settled(PromiseInterface $promise)
{
return $promise->getState() !== PromiseInterface::PENDING;
return Is::settled($promise);
}
/**
* Create a new coroutine.
*
* @see Coroutine
*
* @param callable $generatorFn
*
* @return PromiseInterface
*
* @deprecated coroutine will be removed in guzzlehttp/promises:2.0. Use Coroutine::of instead.
*/
function coroutine(callable $generatorFn)
{
return new Coroutine($generatorFn);
return Coroutine::of($generatorFn);
}

View file

@ -9,8 +9,32 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
## [1.7.0] - 2020-09-30
## [1.6.0]
### Added
- Replaced functions by static methods
### Fixed
- Converting a non-seekable stream to a string
- Handle multiple Set-Cookie correctly
- Ignore array keys in header values when merging
- Allow multibyte characters to be parsed in `Message:bodySummary()`
### Changed
- Restored partial HHVM 3 support
## [1.6.1] - 2019-07-02
### Fixed
- Accept null and bool header values again
## [1.6.0] - 2019-06-30
### Added

View file

@ -23,11 +23,11 @@ Reads from multiple streams, one after the other.
```php
use GuzzleHttp\Psr7;
$a = Psr7\stream_for('abc, ');
$b = Psr7\stream_for('123.');
$a = Psr7\Utils::streamFor('abc, ');
$b = Psr7\Utils::streamFor('123.');
$composed = new Psr7\AppendStream([$a, $b]);
$composed->addStream(Psr7\stream_for(' Above all listen to me'));
$composed->addStream(Psr7\Utils::streamFor(' Above all listen to me'));
echo $composed; // abc, 123. Above all listen to me.
```
@ -65,7 +65,7 @@ then on disk.
```php
use GuzzleHttp\Psr7;
$original = Psr7\stream_for(fopen('http://www.google.com', 'r'));
$original = Psr7\Utils::streamFor(fopen('http://www.google.com', 'r'));
$stream = new Psr7\CachingStream($original);
$stream->read(1024);
@ -89,7 +89,7 @@ stream becomes too full.
use GuzzleHttp\Psr7;
// Create an empty stream
$stream = Psr7\stream_for();
$stream = Psr7\Utils::streamFor();
// Start dropping data when the stream has more than 10 bytes
$dropping = new Psr7\DroppingStream($stream, 10);
@ -112,7 +112,7 @@ to create a concrete class for a simple extension point.
use GuzzleHttp\Psr7;
$stream = Psr7\stream_for('hi');
$stream = Psr7\Utils::streamFor('hi');
$fnStream = Psr7\FnStream::decorate($stream, [
'rewind' => function () use ($stream) {
echo 'About to rewind - ';
@ -167,7 +167,7 @@ chunks (e.g. Amazon S3's multipart upload API).
```php
use GuzzleHttp\Psr7;
$original = Psr7\stream_for(fopen('/tmp/test.txt', 'r+'));
$original = Psr7\Utils::streamFor(fopen('/tmp/test.txt', 'r+'));
echo $original->getSize();
// >>> 1048576
@ -197,7 +197,7 @@ NoSeekStream wraps a stream and does not allow seeking.
```php
use GuzzleHttp\Psr7;
$original = Psr7\stream_for('foo');
$original = Psr7\Utils::streamFor('foo');
$noSeek = new Psr7\NoSeekStream($original);
echo $noSeek->read(3);
@ -271,7 +271,7 @@ This decorator could be added to any existing stream and used like so:
```php
use GuzzleHttp\Psr7;
$original = Psr7\stream_for('foo');
$original = Psr7\Utils::streamFor('foo');
$eofStream = new EofCallbackStream($original, function () {
echo 'EOF!';
@ -297,53 +297,189 @@ stream from a PSR-7 stream.
```php
use GuzzleHttp\Psr7\StreamWrapper;
$stream = GuzzleHttp\Psr7\stream_for('hello!');
$stream = GuzzleHttp\Psr7\Utils::streamFor('hello!');
$resource = StreamWrapper::getResource($stream);
echo fread($resource, 6); // outputs hello!
```
# Function API
# Static API
There are various functions available under the `GuzzleHttp\Psr7` namespace.
There are various static methods available under the `GuzzleHttp\Psr7` namespace.
## `function str`
## `GuzzleHttp\Psr7\Message::toString`
`function str(MessageInterface $message)`
`public static function toString(MessageInterface $message): string`
Returns the string representation of an HTTP message.
```php
$request = new GuzzleHttp\Psr7\Request('GET', 'http://example.com');
echo GuzzleHttp\Psr7\str($request);
echo GuzzleHttp\Psr7\Message::toString($request);
```
## `function uri_for`
## `GuzzleHttp\Psr7\Message::bodySummary`
`function uri_for($uri)`
`public static function bodySummary(MessageInterface $message, int $truncateAt = 120): string|null`
This function accepts a string or `Psr\Http\Message\UriInterface` and returns a
UriInterface for the given value. If the value is already a `UriInterface`, it
is returned as-is.
Get a short summary of the message body.
```php
$uri = GuzzleHttp\Psr7\uri_for('http://example.com');
assert($uri === GuzzleHttp\Psr7\uri_for($uri));
```
Will return `null` if the response is not printable.
## `function stream_for`
## `GuzzleHttp\Psr7\Message::rewindBody`
`function stream_for($resource = '', array $options = [])`
`public static function rewindBody(MessageInterface $message): void`
Attempts to rewind a message body and throws an exception on failure.
The body of the message will only be rewound if a call to `tell()`
returns a value other than `0`.
## `GuzzleHttp\Psr7\Message::parseMessage`
`public static function parseMessage(string $message): array`
Parses an HTTP message into an associative array.
The array contains the "start-line" key containing the start line of
the message, "headers" key containing an associative array of header
array values, and a "body" key containing the body of the message.
## `GuzzleHttp\Psr7\Message::parseRequestUri`
`public static function parseRequestUri(string $path, array $headers): string`
Constructs a URI for an HTTP request message.
## `GuzzleHttp\Psr7\Message::parseRequest`
`public static function parseRequest(string $message): Request`
Parses a request message string into a request object.
## `GuzzleHttp\Psr7\Message::parseResponse`
`public static function parseResponse(string $message): Response`
Parses a response message string into a response object.
## `GuzzleHttp\Psr7\Header::parse`
`public static function parse(string|array $header): array`
Parse an array of header values containing ";" separated data into an
array of associative arrays representing the header key value pair data
of the header. When a parameter does not contain a value, but just
contains a key, this function will inject a key with a '' string value.
## `GuzzleHttp\Psr7\Header::normalize`
`public static function normalize(string|array $header): array`
Converts an array of header values that may contain comma separated
headers into an array of headers with no comma separated values.
## `GuzzleHttp\Psr7\Query::parse`
`public static function parse(string $str, int|bool $urlEncoding = true): array`
Parse a query string into an associative array.
If multiple values are found for the same key, the value of that key
value pair will become an array. This function does not parse nested
PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2`
will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`.
## `GuzzleHttp\Psr7\Query::build`
`public static function build(array $params, int|false $encoding = PHP_QUERY_RFC3986): string`
Build a query string from an array of key value pairs.
This function can use the return value of `parse()` to build a query
string. This function does not modify the provided keys when an array is
encountered (like `http_build_query()` would).
## `GuzzleHttp\Psr7\Utils::caselessRemove`
`public static function caselessRemove(iterable<string> $keys, $keys, array $data): array`
Remove the items given by the keys, case insensitively from the data.
## `GuzzleHttp\Psr7\Utils::copyToStream`
`public static function copyToStream(StreamInterface $source, StreamInterface $dest, int $maxLen = -1): void`
Copy the contents of a stream into another stream until the given number
of bytes have been read.
## `GuzzleHttp\Psr7\Utils::copyToString`
`public static function copyToString(StreamInterface $stream, int $maxLen = -1): string`
Copy the contents of a stream into a string until the given number of
bytes have been read.
## `GuzzleHttp\Psr7\Utils::hash`
`public static function hash(StreamInterface $stream, string $algo, bool $rawOutput = false): string`
Calculate a hash of a stream.
This method reads the entire stream to calculate a rolling hash, based on
PHP's `hash_init` functions.
## `GuzzleHttp\Psr7\Utils::modifyRequest`
`public static function modifyRequest(RequestInterface $request, array $changes): RequestInterface`
Clone and modify a request with the given changes.
This method is useful for reducing the number of clones needed to mutate
a message.
- method: (string) Changes the HTTP method.
- set_headers: (array) Sets the given headers.
- remove_headers: (array) Remove the given headers.
- body: (mixed) Sets the given body.
- uri: (UriInterface) Set the URI.
- query: (string) Set the query string value of the URI.
- version: (string) Set the protocol version.
## `GuzzleHttp\Psr7\Utils::readLine`
`public static function readLine(StreamInterface $stream, int $maxLength = null): string`
Read a line from the stream up to the maximum allowed buffer length.
## `GuzzleHttp\Psr7\Utils::streamFor`
`public static function streamFor(resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource = '', array $options = []): StreamInterface`
Create a new stream based on the input type.
Options is an associative array that can contain the following keys:
* - metadata: Array of custom metadata.
* - size: Size of the stream.
- metadata: Array of custom metadata.
- size: Size of the stream.
This method accepts the following `$resource` types:
@ -369,8 +505,8 @@ This method accepts the following `$resource` types:
buffered and used in subsequent reads.
```php
$stream = GuzzleHttp\Psr7\stream_for('foo');
$stream = GuzzleHttp\Psr7\stream_for(fopen('/path/to/file', 'r'));
$stream = GuzzleHttp\Psr7\Utils::streamFor('foo');
$stream = GuzzleHttp\Psr7\Utils::streamFor(fopen('/path/to/file', 'r'));
$generator = function ($bytes) {
for ($i = 0; $i < $bytes; $i++) {
@ -378,147 +514,75 @@ $generator = function ($bytes) {
}
}
$stream = GuzzleHttp\Psr7\stream_for($generator(100));
$stream = GuzzleHttp\Psr7\Utils::streamFor($generator(100));
```
## `function parse_header`
## `GuzzleHttp\Psr7\Utils::tryFopen`
`function parse_header($header)`
Parse an array of header values containing ";" separated data into an array of
associative arrays representing the header key value pair data of the header.
When a parameter does not contain a value, but just contains a key, this
function will inject a key with a '' string value.
## `function normalize_header`
`function normalize_header($header)`
Converts an array of header values that may contain comma separated headers
into an array of headers with no comma separated values.
## `function modify_request`
`function modify_request(RequestInterface $request, array $changes)`
Clone and modify a request with the given changes. This method is useful for
reducing the number of clones needed to mutate a message.
The changes can be one of:
- method: (string) Changes the HTTP method.
- set_headers: (array) Sets the given headers.
- remove_headers: (array) Remove the given headers.
- body: (mixed) Sets the given body.
- uri: (UriInterface) Set the URI.
- query: (string) Set the query string value of the URI.
- version: (string) Set the protocol version.
## `function rewind_body`
`function rewind_body(MessageInterface $message)`
Attempts to rewind a message body and throws an exception on failure. The body
of the message will only be rewound if a call to `tell()` returns a value other
than `0`.
## `function try_fopen`
`function try_fopen($filename, $mode)`
`public static function tryFopen(string $filename, string $mode): resource`
Safely opens a PHP stream resource using a filename.
When fopen fails, PHP normally raises a warning. This function adds an error
handler that checks for errors and throws an exception instead.
When fopen fails, PHP normally raises a warning. This function adds an
error handler that checks for errors and throws an exception instead.
## `function copy_to_string`
## `GuzzleHttp\Psr7\Utils::uriFor`
`function copy_to_string(StreamInterface $stream, $maxLen = -1)`
`public static function uriFor(string|UriInterface $uri): UriInterface`
Copy the contents of a stream into a string until the given number of bytes
have been read.
Returns a UriInterface for the given value.
This function accepts a string or UriInterface and returns a
UriInterface for the given value. If the value is already a
UriInterface, it is returned as-is.
## `function copy_to_stream`
## `GuzzleHttp\Psr7\MimeType::fromFilename`
`function copy_to_stream(StreamInterface $source, StreamInterface $dest, $maxLen = -1)`
Copy the contents of a stream into another stream until the given number of
bytes have been read.
## `function hash`
`function hash(StreamInterface $stream, $algo, $rawOutput = false)`
Calculate a hash of a Stream. This method reads the entire stream to calculate
a rolling hash (based on PHP's hash_init functions).
## `function readline`
`function readline(StreamInterface $stream, $maxLength = null)`
Read a line from the stream up to the maximum allowed buffer length.
## `function parse_request`
`function parse_request($message)`
Parses a request message string into a request object.
## `function parse_response`
`function parse_response($message)`
Parses a response message string into a response object.
## `function parse_query`
`function parse_query($str, $urlEncoding = true)`
Parse a query string into an associative array.
If multiple values are found for the same key, the value of that key value pair
will become an array. This function does not parse nested PHP style arrays into
an associative array (e.g., `foo[a]=1&foo[b]=2` will be parsed into
`['foo[a]' => '1', 'foo[b]' => '2']`).
## `function build_query`
`function build_query(array $params, $encoding = PHP_QUERY_RFC3986)`
Build a query string from an array of key value pairs.
This function can use the return value of parse_query() to build a query string.
This function does not modify the provided keys when an array is encountered
(like http_build_query would).
## `function mimetype_from_filename`
`function mimetype_from_filename($filename)`
`public static function fromFilename(string $filename): string|null`
Determines the mimetype of a file by looking at its extension.
## `function mimetype_from_extension`
## `GuzzleHttp\Psr7\MimeType::fromExtension`
`function mimetype_from_extension($extension)`
`public static function fromExtension(string $extension): string|null`
Maps a file extensions to a mimetype.
## Upgrading from Function API
The static API was first introduced in 1.7.0, in order to mitigate problems with functions conflicting between global and local copies of the package. The function API will be removed in 2.0.0. A migration table has been provided here for your convenience:
| Original Function | Replacement Method |
|----------------|----------------|
| `str` | `Message::toString` |
| `uri_for` | `Utils::uriFor` |
| `stream_for` | `Utils::streamFor` |
| `parse_header` | `Header::parse` |
| `normalize_header` | `Header::normalize` |
| `modify_request` | `Utils::modifyRequest` |
| `rewind_body` | `Message::rewindBody` |
| `try_fopen` | `Utils::tryFopen` |
| `copy_to_string` | `Utils::copyToString` |
| `copy_to_stream` | `Utils::copyToStream` |
| `hash` | `Utils::hash` |
| `readline` | `Utils::readLine` |
| `parse_request` | `Message::parseRequest` |
| `parse_response` | `Message::parseResponse` |
| `parse_query` | `Query::parse` |
| `build_query` | `Query::build` |
| `mimetype_from_filename` | `MimeType::fromFilename` |
| `mimetype_from_extension` | `MimeType::fromExtension` |
| `_parse_message` | `Message::parseMessage` |
| `_parse_request_uri` | `Message::parseRequestUri` |
| `get_message_body_summary` | `Message::bodySummary` |
| `_caseless_remove` | `Utils::caselessRemove` |
# Additional URI Methods
Aside from the standard `Psr\Http\Message\UriInterface` implementation in form of the `GuzzleHttp\Psr7\Uri` class,

View file

@ -21,14 +21,14 @@
"ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
},
"require-dev": {
"phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8",
"phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.10",
"ext-zlib": "*"
},
"provide": {
"psr/http-message-implementation": "1.0"
},
"suggest": {
"zendframework/zend-httphandlerrunner": "Emit PSR-7 responses"
"laminas/laminas-httphandlerrunner": "Emit PSR-7 responses"
},
"autoload": {
"psr-4": {
@ -43,7 +43,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.6-dev"
"dev-master": "1.7-dev"
}
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;
@ -61,7 +62,7 @@ class AppendStream implements StreamInterface
public function getContents()
{
return copy_to_string($this);
return Utils::copyToString($this);
}
/**
@ -98,6 +99,8 @@ class AppendStream implements StreamInterface
}
$this->streams = [];
return null;
}
public function tell()

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;
@ -49,6 +50,8 @@ class BufferStream implements StreamInterface
public function detach()
{
$this->close();
return null;
}
public function getSize()

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;
@ -131,7 +132,7 @@ class CachingStream implements StreamInterface
private function cacheEntireStream()
{
$target = new FnStream(['write' => 'strlen']);
copy_to_stream($this, $target);
Utils::copyToStream($this, $target);
return $this->tell();
}

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;
@ -34,6 +35,7 @@ class FnStream implements StreamInterface
/**
* Lazily determine which methods are not implemented.
*
* @throws \BadMethodCallException
*/
public function __get($name)

71
vendor/guzzlehttp/psr7/src/Header.php vendored Normal file
View file

@ -0,0 +1,71 @@
<?php
namespace GuzzleHttp\Psr7;
final class Header
{
/**
* Parse an array of header values containing ";" separated data into an
* array of associative arrays representing the header key value pair data
* of the header. When a parameter does not contain a value, but just
* contains a key, this function will inject a key with a '' string value.
*
* @param string|array $header Header to parse into components.
*
* @return array Returns the parsed header values.
*/
public static function parse($header)
{
static $trimmed = "\"' \n\t\r";
$params = $matches = [];
foreach (self::normalize($header) as $val) {
$part = [];
foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) {
$m = $matches[0];
if (isset($m[1])) {
$part[trim($m[0], $trimmed)] = trim($m[1], $trimmed);
} else {
$part[] = trim($m[0], $trimmed);
}
}
}
if ($part) {
$params[] = $part;
}
}
return $params;
}
/**
* Converts an array of header values that may contain comma separated
* headers into an array of headers with no comma separated values.
*
* @param string|array $header Header to normalize.
*
* @return array Returns the normalized header field values.
*/
public static function normalize($header)
{
if (!is_array($header)) {
return array_map('trim', explode(',', $header));
}
$result = [];
foreach ($header as $value) {
foreach ((array) $value as $v) {
if (strpos($v, ',') === false) {
$result[] = $v;
continue;
}
foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) {
$result[] = trim($vv);
}
}
}
return $result;
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;
@ -34,6 +35,6 @@ class LazyOpenStream implements StreamInterface
*/
protected function createStream()
{
return stream_for(try_fopen($this->filename, $this->mode));
return Utils::streamFor(Utils::tryFopen($this->filename, $this->mode));
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;

252
vendor/guzzlehttp/psr7/src/Message.php vendored Normal file
View file

@ -0,0 +1,252 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
final class Message
{
/**
* Returns the string representation of an HTTP message.
*
* @param MessageInterface $message Message to convert to a string.
*
* @return string
*/
public static function toString(MessageInterface $message)
{
if ($message instanceof RequestInterface) {
$msg = trim($message->getMethod() . ' '
. $message->getRequestTarget())
. ' HTTP/' . $message->getProtocolVersion();
if (!$message->hasHeader('host')) {
$msg .= "\r\nHost: " . $message->getUri()->getHost();
}
} elseif ($message instanceof ResponseInterface) {
$msg = 'HTTP/' . $message->getProtocolVersion() . ' '
. $message->getStatusCode() . ' '
. $message->getReasonPhrase();
} else {
throw new \InvalidArgumentException('Unknown message type');
}
foreach ($message->getHeaders() as $name => $values) {
if (strtolower($name) === 'set-cookie') {
foreach ($values as $value) {
$msg .= "\r\n{$name}: " . $value;
}
} else {
$msg .= "\r\n{$name}: " . implode(', ', $values);
}
}
return "{$msg}\r\n\r\n" . $message->getBody();
}
/**
* Get a short summary of the message body.
*
* Will return `null` if the response is not printable.
*
* @param MessageInterface $message The message to get the body summary
* @param int $truncateAt The maximum allowed size of the summary
*
* @return string|null
*/
public static function bodySummary(MessageInterface $message, $truncateAt = 120)
{
$body = $message->getBody();
if (!$body->isSeekable() || !$body->isReadable()) {
return null;
}
$size = $body->getSize();
if ($size === 0) {
return null;
}
$summary = $body->read($truncateAt);
$body->rewind();
if ($size > $truncateAt) {
$summary .= ' (truncated...)';
}
// Matches any printable character, including unicode characters:
// letters, marks, numbers, punctuation, spacing, and separators.
if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/u', $summary)) {
return null;
}
return $summary;
}
/**
* Attempts to rewind a message body and throws an exception on failure.
*
* The body of the message will only be rewound if a call to `tell()`
* returns a value other than `0`.
*
* @param MessageInterface $message Message to rewind
*
* @throws \RuntimeException
*/
public static function rewindBody(MessageInterface $message)
{
$body = $message->getBody();
if ($body->tell()) {
$body->rewind();
}
}
/**
* Parses an HTTP message into an associative array.
*
* The array contains the "start-line" key containing the start line of
* the message, "headers" key containing an associative array of header
* array values, and a "body" key containing the body of the message.
*
* @param string $message HTTP request or response to parse.
*
* @return array
*/
public static function parseMessage($message)
{
if (!$message) {
throw new \InvalidArgumentException('Invalid message');
}
$message = ltrim($message, "\r\n");
$messageParts = preg_split("/\r?\n\r?\n/", $message, 2);
if ($messageParts === false || count($messageParts) !== 2) {
throw new \InvalidArgumentException('Invalid message: Missing header delimiter');
}
list($rawHeaders, $body) = $messageParts;
$rawHeaders .= "\r\n"; // Put back the delimiter we split previously
$headerParts = preg_split("/\r?\n/", $rawHeaders, 2);
if ($headerParts === false || count($headerParts) !== 2) {
throw new \InvalidArgumentException('Invalid message: Missing status line');
}
list($startLine, $rawHeaders) = $headerParts;
if (preg_match("/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches) && $matches[1] === '1.0') {
// Header folding is deprecated for HTTP/1.1, but allowed in HTTP/1.0
$rawHeaders = preg_replace(Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders);
}
/** @var array[] $headerLines */
$count = preg_match_all(Rfc7230::HEADER_REGEX, $rawHeaders, $headerLines, PREG_SET_ORDER);
// If these aren't the same, then one line didn't match and there's an invalid header.
if ($count !== substr_count($rawHeaders, "\n")) {
// Folding is deprecated, see https://tools.ietf.org/html/rfc7230#section-3.2.4
if (preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) {
throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding');
}
throw new \InvalidArgumentException('Invalid header syntax');
}
$headers = [];
foreach ($headerLines as $headerLine) {
$headers[$headerLine[1]][] = $headerLine[2];
}
return [
'start-line' => $startLine,
'headers' => $headers,
'body' => $body,
];
}
/**
* Constructs a URI for an HTTP request message.
*
* @param string $path Path from the start-line
* @param array $headers Array of headers (each value an array).
*
* @return string
*/
public static function parseRequestUri($path, array $headers)
{
$hostKey = array_filter(array_keys($headers), function ($k) {
return strtolower($k) === 'host';
});
// If no host is found, then a full URI cannot be constructed.
if (!$hostKey) {
return $path;
}
$host = $headers[reset($hostKey)][0];
$scheme = substr($host, -4) === ':443' ? 'https' : 'http';
return $scheme . '://' . $host . '/' . ltrim($path, '/');
}
/**
* Parses a request message string into a request object.
*
* @param string $message Request message string.
*
* @return Request
*/
public static function parseRequest($message)
{
$data = self::parseMessage($message);
$matches = [];
if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) {
throw new \InvalidArgumentException('Invalid request string');
}
$parts = explode(' ', $data['start-line'], 3);
$version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1';
$request = new Request(
$parts[0],
$matches[1] === '/' ? self::parseRequestUri($parts[1], $data['headers']) : $parts[1],
$data['headers'],
$data['body'],
$version
);
return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]);
}
/**
* Parses a response message string into a response object.
*
* @param string $message Response message string.
*
* @return Response
*/
public static function parseResponse($message)
{
$data = self::parseMessage($message);
// According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space
// between status-code and reason-phrase is required. But browsers accept
// responses without space and reason as well.
if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) {
throw new \InvalidArgumentException('Invalid response string: ' . $data['start-line']);
}
$parts = explode(' ', $data['start-line'], 3);
return new Response(
(int) $parts[1],
$data['headers'],
$data['body'],
explode('/', $parts[0])[1],
isset($parts[2]) ? $parts[2] : null
);
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;
@ -17,7 +18,7 @@ trait MessageTrait
/** @var string */
private $protocol = '1.1';
/** @var StreamInterface */
/** @var StreamInterface|null */
private $stream;
public function getProtocolVersion()
@ -117,7 +118,7 @@ trait MessageTrait
public function getBody()
{
if (!$this->stream) {
$this->stream = stream_for('');
$this->stream = Utils::streamFor('');
}
return $this->stream;
@ -194,7 +195,7 @@ trait MessageTrait
}
return trim((string) $value, " \t");
}, $values);
}, array_values($values));
}
private function assertHeader($header)

140
vendor/guzzlehttp/psr7/src/MimeType.php vendored Normal file
View file

@ -0,0 +1,140 @@
<?php
namespace GuzzleHttp\Psr7;
final class MimeType
{
/**
* Determines the mimetype of a file by looking at its extension.
*
* @param string $filename
*
* @return string|null
*/
public static function fromFilename($filename)
{
return self::fromExtension(pathinfo($filename, PATHINFO_EXTENSION));
}
/**
* Maps a file extensions to a mimetype.
*
* @param string $extension string The file extension.
*
* @return string|null
*
* @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types
*/
public static function fromExtension($extension)
{
static $mimetypes = [
'3gp' => 'video/3gpp',
'7z' => 'application/x-7z-compressed',
'aac' => 'audio/x-aac',
'ai' => 'application/postscript',
'aif' => 'audio/x-aiff',
'asc' => 'text/plain',
'asf' => 'video/x-ms-asf',
'atom' => 'application/atom+xml',
'avi' => 'video/x-msvideo',
'bmp' => 'image/bmp',
'bz2' => 'application/x-bzip2',
'cer' => 'application/pkix-cert',
'crl' => 'application/pkix-crl',
'crt' => 'application/x-x509-ca-cert',
'css' => 'text/css',
'csv' => 'text/csv',
'cu' => 'application/cu-seeme',
'deb' => 'application/x-debian-package',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dvi' => 'application/x-dvi',
'eot' => 'application/vnd.ms-fontobject',
'eps' => 'application/postscript',
'epub' => 'application/epub+zip',
'etx' => 'text/x-setext',
'flac' => 'audio/flac',
'flv' => 'video/x-flv',
'gif' => 'image/gif',
'gz' => 'application/gzip',
'htm' => 'text/html',
'html' => 'text/html',
'ico' => 'image/x-icon',
'ics' => 'text/calendar',
'ini' => 'text/plain',
'iso' => 'application/x-iso9660-image',
'jar' => 'application/java-archive',
'jpe' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'jpg' => 'image/jpeg',
'js' => 'text/javascript',
'json' => 'application/json',
'latex' => 'application/x-latex',
'log' => 'text/plain',
'm4a' => 'audio/mp4',
'm4v' => 'video/mp4',
'mid' => 'audio/midi',
'midi' => 'audio/midi',
'mov' => 'video/quicktime',
'mkv' => 'video/x-matroska',
'mp3' => 'audio/mpeg',
'mp4' => 'video/mp4',
'mp4a' => 'audio/mp4',
'mp4v' => 'video/mp4',
'mpe' => 'video/mpeg',
'mpeg' => 'video/mpeg',
'mpg' => 'video/mpeg',
'mpg4' => 'video/mp4',
'oga' => 'audio/ogg',
'ogg' => 'audio/ogg',
'ogv' => 'video/ogg',
'ogx' => 'application/ogg',
'pbm' => 'image/x-portable-bitmap',
'pdf' => 'application/pdf',
'pgm' => 'image/x-portable-graymap',
'png' => 'image/png',
'pnm' => 'image/x-portable-anymap',
'ppm' => 'image/x-portable-pixmap',
'ppt' => 'application/vnd.ms-powerpoint',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'ps' => 'application/postscript',
'qt' => 'video/quicktime',
'rar' => 'application/x-rar-compressed',
'ras' => 'image/x-cmu-raster',
'rss' => 'application/rss+xml',
'rtf' => 'application/rtf',
'sgm' => 'text/sgml',
'sgml' => 'text/sgml',
'svg' => 'image/svg+xml',
'swf' => 'application/x-shockwave-flash',
'tar' => 'application/x-tar',
'tif' => 'image/tiff',
'tiff' => 'image/tiff',
'torrent' => 'application/x-bittorrent',
'ttf' => 'application/x-font-ttf',
'txt' => 'text/plain',
'wav' => 'audio/x-wav',
'webm' => 'video/webm',
'webp' => 'image/webp',
'wma' => 'audio/x-ms-wma',
'wmv' => 'video/x-ms-wmv',
'woff' => 'application/x-font-woff',
'wsdl' => 'application/wsdl+xml',
'xbm' => 'image/x-xbitmap',
'xls' => 'application/vnd.ms-excel',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xml' => 'application/xml',
'xpm' => 'image/x-xpixmap',
'xwd' => 'image/x-xwindowdump',
'yaml' => 'text/yaml',
'yml' => 'text/yaml',
'zip' => 'application/zip',
];
$extension = strtolower($extension);
return isset($mimetypes[$extension])
? $mimetypes[$extension]
: null;
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;
@ -71,7 +72,7 @@ class MultipartStream implements StreamInterface
}
// Add the trailing boundary with CRLF
$stream->addStream(stream_for("--{$this->boundary}--\r\n"));
$stream->addStream(Utils::streamFor("--{$this->boundary}--\r\n"));
return $stream;
}
@ -84,7 +85,7 @@ class MultipartStream implements StreamInterface
}
}
$element['contents'] = stream_for($element['contents']);
$element['contents'] = Utils::streamFor($element['contents']);
if (empty($element['filename'])) {
$uri = $element['contents']->getMetadata('uri');
@ -100,9 +101,9 @@ class MultipartStream implements StreamInterface
isset($element['headers']) ? $element['headers'] : []
);
$stream->addStream(stream_for($this->getHeaders($headers)));
$stream->addStream(Utils::streamFor($this->getHeaders($headers)));
$stream->addStream($body);
$stream->addStream(stream_for("\r\n"));
$stream->addStream(Utils::streamFor("\r\n"));
}
/**
@ -131,7 +132,7 @@ class MultipartStream implements StreamInterface
// Set a default Content-Type if one was not supplied
$type = $this->getHeader($headers, 'content-type');
if (!$type && ($filename === '0' || $filename)) {
if ($type = mimetype_from_filename($filename)) {
if ($type = MimeType::fromFilename($filename)) {
$headers['Content-Type'] = $type;
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;
@ -51,7 +52,7 @@ class PumpStream implements StreamInterface
public function __toString()
{
try {
return copy_to_string($this);
return Utils::copyToString($this);
} catch (\Exception $e) {
return '';
}
@ -66,6 +67,8 @@ class PumpStream implements StreamInterface
{
$this->tellPos = false;
$this->source = null;
return null;
}
public function getSize()

108
vendor/guzzlehttp/psr7/src/Query.php vendored Normal file
View file

@ -0,0 +1,108 @@
<?php
namespace GuzzleHttp\Psr7;
final class Query
{
/**
* Parse a query string into an associative array.
*
* If multiple values are found for the same key, the value of that key
* value pair will become an array. This function does not parse nested
* PHP style arrays into an associative array (e.g., `foo[a]=1&foo[b]=2`
* will be parsed into `['foo[a]' => '1', 'foo[b]' => '2'])`.
*
* @param string $str Query string to parse
* @param int|bool $urlEncoding How the query string is encoded
*
* @return array
*/
public static function parse($str, $urlEncoding = true)
{
$result = [];
if ($str === '') {
return $result;
}
if ($urlEncoding === true) {
$decoder = function ($value) {
return rawurldecode(str_replace('+', ' ', $value));
};
} elseif ($urlEncoding === PHP_QUERY_RFC3986) {
$decoder = 'rawurldecode';
} elseif ($urlEncoding === PHP_QUERY_RFC1738) {
$decoder = 'urldecode';
} else {
$decoder = function ($str) { return $str; };
}
foreach (explode('&', $str) as $kvp) {
$parts = explode('=', $kvp, 2);
$key = $decoder($parts[0]);
$value = isset($parts[1]) ? $decoder($parts[1]) : null;
if (!isset($result[$key])) {
$result[$key] = $value;
} else {
if (!is_array($result[$key])) {
$result[$key] = [$result[$key]];
}
$result[$key][] = $value;
}
}
return $result;
}
/**
* Build a query string from an array of key value pairs.
*
* This function can use the return value of `parse()` to build a query
* string. This function does not modify the provided keys when an array is
* encountered (like `http_build_query()` would).
*
* @param array $params Query string parameters.
* @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
* to encode using RFC3986, or PHP_QUERY_RFC1738
* to encode using RFC1738.
* @return string
*/
public static function build(array $params, $encoding = PHP_QUERY_RFC3986)
{
if (!$params) {
return '';
}
if ($encoding === false) {
$encoder = function ($str) { return $str; };
} elseif ($encoding === PHP_QUERY_RFC3986) {
$encoder = 'rawurlencode';
} elseif ($encoding === PHP_QUERY_RFC1738) {
$encoder = 'urlencode';
} else {
throw new \InvalidArgumentException('Invalid type');
}
$qs = '';
foreach ($params as $k => $v) {
$k = $encoder($k);
if (!is_array($v)) {
$qs .= $k;
if ($v !== null) {
$qs .= '=' . $encoder($v);
}
$qs .= '&';
} else {
foreach ($v as $vv) {
$qs .= $k;
if ($vv !== null) {
$qs .= '=' . $encoder($vv);
}
$qs .= '&';
}
}
}
return $qs ? (string) substr($qs, 0, -1) : '';
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use InvalidArgumentException;
@ -51,7 +52,7 @@ class Request implements RequestInterface
}
if ($body !== '' && $body !== null) {
$this->stream = stream_for($body);
$this->stream = Utils::streamFor($body);
}
}

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\ResponseInterface;
@ -100,7 +101,7 @@ class Response implements ResponseInterface
$this->statusCode = $status;
if ($body !== '' && $body !== null) {
$this->stream = stream_for($body);
$this->stream = Utils::streamFor($body);
}
$this->setHeaders($headers);
@ -134,7 +135,7 @@ class Response implements ResponseInterface
if ($reasonPhrase == '' && isset(self::$phrases[$new->statusCode])) {
$reasonPhrase = self::$phrases[$new->statusCode];
}
$new->reasonPhrase = $reasonPhrase;
$new->reasonPhrase = (string) $reasonPhrase;
return $new;
}

View file

@ -79,8 +79,10 @@ class ServerRequest extends Request implements ServerRequestInterface
* Return an UploadedFile instance array.
*
* @param array $files A array which respect $_FILES structure
* @throws InvalidArgumentException for unrecognized values
*
* @return array
*
* @throws InvalidArgumentException for unrecognized values
*/
public static function normalizeFiles(array $files)
{

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;
@ -76,8 +77,10 @@ class Stream implements StreamInterface
public function __toString()
{
try {
$this->seek(0);
return (string) stream_get_contents($this->stream);
if ($this->isSeekable()) {
$this->seek(0);
}
return $this->getContents();
} catch (\Exception $e) {
return '';
}
@ -193,7 +196,7 @@ class Stream implements StreamInterface
public function seek($offset, $whence = SEEK_SET)
{
$whence = (int) $whence;
if (!isset($this->stream)) {
throw new \RuntimeException('Stream is detached');
}

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;
@ -52,7 +53,7 @@ trait StreamDecoratorTrait
public function getContents()
{
return copy_to_string($this);
return Utils::copyToString($this);
}
/**
@ -140,6 +141,7 @@ trait StreamDecoratorTrait
* Implement in subclasses to dynamically create streams when requested.
*
* @return StreamInterface
*
* @throws \BadMethodCallException
*/
protected function createStream()

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\StreamInterface;
@ -23,6 +24,7 @@ class StreamWrapper
* @param StreamInterface $stream The stream to get a resource for
*
* @return resource
*
* @throws \InvalidArgumentException if stream is not readable or writable
*/
public static function getResource(StreamInterface $stream)

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use InvalidArgumentException;
@ -85,6 +86,7 @@ class UploadedFile implements UploadedFileInterface
* Depending on the value set file or stream variable
*
* @param mixed $streamOrFile
*
* @throws InvalidArgumentException
*/
private function setStreamOrFile($streamOrFile)
@ -104,6 +106,7 @@ class UploadedFile implements UploadedFileInterface
/**
* @param int $error
*
* @throws InvalidArgumentException
*/
private function setError($error)
@ -125,6 +128,7 @@ class UploadedFile implements UploadedFileInterface
/**
* @param int $size
*
* @throws InvalidArgumentException
*/
private function setSize($size)
@ -158,6 +162,7 @@ class UploadedFile implements UploadedFileInterface
/**
* @param string|null $clientFilename
*
* @throws InvalidArgumentException
*/
private function setClientFilename($clientFilename)
@ -173,6 +178,7 @@ class UploadedFile implements UploadedFileInterface
/**
* @param string|null $clientMediaType
*
* @throws InvalidArgumentException
*/
private function setClientMediaType($clientMediaType)
@ -220,6 +226,7 @@ class UploadedFile implements UploadedFileInterface
/**
* {@inheritdoc}
*
* @throws RuntimeException if the upload was not successful.
*/
public function getStream()
@ -238,7 +245,9 @@ class UploadedFile implements UploadedFileInterface
*
* @see http://php.net/is_uploaded_file
* @see http://php.net/move_uploaded_file
*
* @param string $targetPath Path to which to move the uploaded file.
*
* @throws RuntimeException if the upload was not successful.
* @throws InvalidArgumentException if the $path specified is invalid.
* @throws RuntimeException on any error during the move operation, or on
@ -259,7 +268,7 @@ class UploadedFile implements UploadedFileInterface
? rename($this->file, $targetPath)
: move_uploaded_file($this->file, $targetPath);
} else {
copy_to_stream(
Utils::copyToStream(
$this->getStream(),
new LazyOpenStream($targetPath, 'w')
);

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\UriInterface;

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\UriInterface;

View file

@ -1,4 +1,5 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\UriInterface;

398
vendor/guzzlehttp/psr7/src/Utils.php vendored Normal file
View file

@ -0,0 +1,398 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;
final class Utils
{
/**
* Remove the items given by the keys, case insensitively from the data.
*
* @param iterable<string> $keys
*
* @return array
*/
public static function caselessRemove($keys, array $data)
{
$result = [];
foreach ($keys as &$key) {
$key = strtolower($key);
}
foreach ($data as $k => $v) {
if (!in_array(strtolower($k), $keys)) {
$result[$k] = $v;
}
}
return $result;
}
/**
* Copy the contents of a stream into another stream until the given number
* of bytes have been read.
*
* @param StreamInterface $source Stream to read from
* @param StreamInterface $dest Stream to write to
* @param int $maxLen Maximum number of bytes to read. Pass -1
* to read the entire stream.
*
* @throws \RuntimeException on error.
*/
public static function copyToStream(StreamInterface $source, StreamInterface $dest, $maxLen = -1)
{
$bufferSize = 8192;
if ($maxLen === -1) {
while (!$source->eof()) {
if (!$dest->write($source->read($bufferSize))) {
break;
}
}
} else {
$remaining = $maxLen;
while ($remaining > 0 && !$source->eof()) {
$buf = $source->read(min($bufferSize, $remaining));
$len = strlen($buf);
if (!$len) {
break;
}
$remaining -= $len;
$dest->write($buf);
}
}
}
/**
* Copy the contents of a stream into a string until the given number of
* bytes have been read.
*
* @param StreamInterface $stream Stream to read
* @param int $maxLen Maximum number of bytes to read. Pass -1
* to read the entire stream.
* @return string
*
* @throws \RuntimeException on error.
*/
public static function copyToString(StreamInterface $stream, $maxLen = -1)
{
$buffer = '';
if ($maxLen === -1) {
while (!$stream->eof()) {
$buf = $stream->read(1048576);
// Using a loose equality here to match on '' and false.
if ($buf == null) {
break;
}
$buffer .= $buf;
}
return $buffer;
}
$len = 0;
while (!$stream->eof() && $len < $maxLen) {
$buf = $stream->read($maxLen - $len);
// Using a loose equality here to match on '' and false.
if ($buf == null) {
break;
}
$buffer .= $buf;
$len = strlen($buffer);
}
return $buffer;
}
/**
* Calculate a hash of a stream.
*
* This method reads the entire stream to calculate a rolling hash, based
* on PHP's `hash_init` functions.
*
* @param StreamInterface $stream Stream to calculate the hash for
* @param string $algo Hash algorithm (e.g. md5, crc32, etc)
* @param bool $rawOutput Whether or not to use raw output
*
* @return string Returns the hash of the stream
*
* @throws \RuntimeException on error.
*/
public static function hash(StreamInterface $stream, $algo, $rawOutput = false)
{
$pos = $stream->tell();
if ($pos > 0) {
$stream->rewind();
}
$ctx = hash_init($algo);
while (!$stream->eof()) {
hash_update($ctx, $stream->read(1048576));
}
$out = hash_final($ctx, (bool) $rawOutput);
$stream->seek($pos);
return $out;
}
/**
* Clone and modify a request with the given changes.
*
* This method is useful for reducing the number of clones needed to mutate
* a message.
*
* The changes can be one of:
* - method: (string) Changes the HTTP method.
* - set_headers: (array) Sets the given headers.
* - remove_headers: (array) Remove the given headers.
* - body: (mixed) Sets the given body.
* - uri: (UriInterface) Set the URI.
* - query: (string) Set the query string value of the URI.
* - version: (string) Set the protocol version.
*
* @param RequestInterface $request Request to clone and modify.
* @param array $changes Changes to apply.
*
* @return RequestInterface
*/
public static function modifyRequest(RequestInterface $request, array $changes)
{
if (!$changes) {
return $request;
}
$headers = $request->getHeaders();
if (!isset($changes['uri'])) {
$uri = $request->getUri();
} else {
// Remove the host header if one is on the URI
if ($host = $changes['uri']->getHost()) {
$changes['set_headers']['Host'] = $host;
if ($port = $changes['uri']->getPort()) {
$standardPorts = ['http' => 80, 'https' => 443];
$scheme = $changes['uri']->getScheme();
if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) {
$changes['set_headers']['Host'] .= ':'.$port;
}
}
}
$uri = $changes['uri'];
}
if (!empty($changes['remove_headers'])) {
$headers = self::caselessRemove($changes['remove_headers'], $headers);
}
if (!empty($changes['set_headers'])) {
$headers = self::caselessRemove(array_keys($changes['set_headers']), $headers);
$headers = $changes['set_headers'] + $headers;
}
if (isset($changes['query'])) {
$uri = $uri->withQuery($changes['query']);
}
if ($request instanceof ServerRequestInterface) {
return (new ServerRequest(
isset($changes['method']) ? $changes['method'] : $request->getMethod(),
$uri,
$headers,
isset($changes['body']) ? $changes['body'] : $request->getBody(),
isset($changes['version'])
? $changes['version']
: $request->getProtocolVersion(),
$request->getServerParams()
))
->withParsedBody($request->getParsedBody())
->withQueryParams($request->getQueryParams())
->withCookieParams($request->getCookieParams())
->withUploadedFiles($request->getUploadedFiles());
}
return new Request(
isset($changes['method']) ? $changes['method'] : $request->getMethod(),
$uri,
$headers,
isset($changes['body']) ? $changes['body'] : $request->getBody(),
isset($changes['version'])
? $changes['version']
: $request->getProtocolVersion()
);
}
/**
* Read a line from the stream up to the maximum allowed buffer length.
*
* @param StreamInterface $stream Stream to read from
* @param int|null $maxLength Maximum buffer length
*
* @return string
*/
public static function readLine(StreamInterface $stream, $maxLength = null)
{
$buffer = '';
$size = 0;
while (!$stream->eof()) {
// Using a loose equality here to match on '' and false.
if (null == ($byte = $stream->read(1))) {
return $buffer;
}
$buffer .= $byte;
// Break when a new line is found or the max length - 1 is reached
if ($byte === "\n" || ++$size === $maxLength - 1) {
break;
}
}
return $buffer;
}
/**
* Create a new stream based on the input type.
*
* Options is an associative array that can contain the following keys:
* - metadata: Array of custom metadata.
* - size: Size of the stream.
*
* This method accepts the following `$resource` types:
* - `Psr\Http\Message\StreamInterface`: Returns the value as-is.
* - `string`: Creates a stream object that uses the given string as the contents.
* - `resource`: Creates a stream object that wraps the given PHP stream resource.
* - `Iterator`: If the provided value implements `Iterator`, then a read-only
* stream object will be created that wraps the given iterable. Each time the
* stream is read from, data from the iterator will fill a buffer and will be
* continuously called until the buffer is equal to the requested read size.
* Subsequent read calls will first read from the buffer and then call `next`
* on the underlying iterator until it is exhausted.
* - `object` with `__toString()`: If the object has the `__toString()` method,
* the object will be cast to a string and then a stream will be returned that
* uses the string value.
* - `NULL`: When `null` is passed, an empty stream object is returned.
* - `callable` When a callable is passed, a read-only stream object will be
* created that invokes the given callable. The callable is invoked with the
* number of suggested bytes to read. The callable can return any number of
* bytes, but MUST return `false` when there is no more data to return. The
* stream object that wraps the callable will invoke the callable until the
* number of requested bytes are available. Any additional bytes will be
* buffered and used in subsequent reads.
*
* @param resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource Entity body data
* @param array $options Additional options
*
* @return StreamInterface
*
* @throws \InvalidArgumentException if the $resource arg is not valid.
*/
public static function streamFor($resource = '', array $options = [])
{
if (is_scalar($resource)) {
$stream = fopen('php://temp', 'r+');
if ($resource !== '') {
fwrite($stream, $resource);
fseek($stream, 0);
}
return new Stream($stream, $options);
}
switch (gettype($resource)) {
case 'resource':
return new Stream($resource, $options);
case 'object':
if ($resource instanceof StreamInterface) {
return $resource;
} elseif ($resource instanceof \Iterator) {
return new PumpStream(function () use ($resource) {
if (!$resource->valid()) {
return false;
}
$result = $resource->current();
$resource->next();
return $result;
}, $options);
} elseif (method_exists($resource, '__toString')) {
return Utils::streamFor((string) $resource, $options);
}
break;
case 'NULL':
return new Stream(fopen('php://temp', 'r+'), $options);
}
if (is_callable($resource)) {
return new PumpStream($resource, $options);
}
throw new \InvalidArgumentException('Invalid resource type: ' . gettype($resource));
}
/**
* Safely opens a PHP stream resource using a filename.
*
* When fopen fails, PHP normally raises a warning. This function adds an
* error handler that checks for errors and throws an exception instead.
*
* @param string $filename File to open
* @param string $mode Mode used to open the file
*
* @return resource
*
* @throws \RuntimeException if the file cannot be opened
*/
public static function tryFopen($filename, $mode)
{
$ex = null;
set_error_handler(function () use ($filename, $mode, &$ex) {
$ex = new \RuntimeException(sprintf(
'Unable to open %s using mode %s: %s',
$filename,
$mode,
func_get_args()[1]
));
});
$handle = fopen($filename, $mode);
restore_error_handler();
if ($ex) {
/** @var $ex \RuntimeException */
throw $ex;
}
return $handle;
}
/**
* Returns a UriInterface for the given value.
*
* This function accepts a string or UriInterface and returns a
* UriInterface for the given value. If the value is already a
* UriInterface, it is returned as-is.
*
* @param string|UriInterface $uri
*
* @return UriInterface
*
* @throws \InvalidArgumentException
*/
public static function uriFor($uri)
{
if ($uri instanceof UriInterface) {
return $uri;
}
if (is_string($uri)) {
return new Uri($uri);
}
throw new \InvalidArgumentException('URI must be a string or UriInterface');
}
}

View file

@ -1,10 +1,9 @@
<?php
namespace GuzzleHttp\Psr7;
use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;
@ -14,52 +13,32 @@ use Psr\Http\Message\UriInterface;
* @param MessageInterface $message Message to convert to a string.
*
* @return string
*
* @deprecated str will be removed in guzzlehttp/psr7:2.0. Use Message::toString instead.
*/
function str(MessageInterface $message)
{
if ($message instanceof RequestInterface) {
$msg = trim($message->getMethod() . ' '
. $message->getRequestTarget())
. ' HTTP/' . $message->getProtocolVersion();
if (!$message->hasHeader('host')) {
$msg .= "\r\nHost: " . $message->getUri()->getHost();
}
} elseif ($message instanceof ResponseInterface) {
$msg = 'HTTP/' . $message->getProtocolVersion() . ' '
. $message->getStatusCode() . ' '
. $message->getReasonPhrase();
} else {
throw new \InvalidArgumentException('Unknown message type');
}
foreach ($message->getHeaders() as $name => $values) {
$msg .= "\r\n{$name}: " . implode(', ', $values);
}
return "{$msg}\r\n\r\n" . $message->getBody();
return Message::toString($message);
}
/**
* Returns a UriInterface for the given value.
*
* This function accepts a string or {@see Psr\Http\Message\UriInterface} and
* returns a UriInterface for the given value. If the value is already a
* `UriInterface`, it is returned as-is.
* This function accepts a string or UriInterface and returns a
* UriInterface for the given value. If the value is already a
* UriInterface, it is returned as-is.
*
* @param string|UriInterface $uri
*
* @return UriInterface
*
* @throws \InvalidArgumentException
*
* @deprecated uri_for will be removed in guzzlehttp/psr7:2.0. Use Utils::uriFor instead.
*/
function uri_for($uri)
{
if ($uri instanceof UriInterface) {
return $uri;
} elseif (is_string($uri)) {
return new Uri($uri);
}
throw new \InvalidArgumentException('URI must be a string or UriInterface');
return Utils::uriFor($uri);
}
/**
@ -69,86 +48,57 @@ function uri_for($uri)
* - metadata: Array of custom metadata.
* - size: Size of the stream.
*
* This method accepts the following `$resource` types:
* - `Psr\Http\Message\StreamInterface`: Returns the value as-is.
* - `string`: Creates a stream object that uses the given string as the contents.
* - `resource`: Creates a stream object that wraps the given PHP stream resource.
* - `Iterator`: If the provided value implements `Iterator`, then a read-only
* stream object will be created that wraps the given iterable. Each time the
* stream is read from, data from the iterator will fill a buffer and will be
* continuously called until the buffer is equal to the requested read size.
* Subsequent read calls will first read from the buffer and then call `next`
* on the underlying iterator until it is exhausted.
* - `object` with `__toString()`: If the object has the `__toString()` method,
* the object will be cast to a string and then a stream will be returned that
* uses the string value.
* - `NULL`: When `null` is passed, an empty stream object is returned.
* - `callable` When a callable is passed, a read-only stream object will be
* created that invokes the given callable. The callable is invoked with the
* number of suggested bytes to read. The callable can return any number of
* bytes, but MUST return `false` when there is no more data to return. The
* stream object that wraps the callable will invoke the callable until the
* number of requested bytes are available. Any additional bytes will be
* buffered and used in subsequent reads.
*
* @param resource|string|null|int|float|bool|StreamInterface|callable|\Iterator $resource Entity body data
* @param array $options Additional options
*
* @return StreamInterface
*
* @throws \InvalidArgumentException if the $resource arg is not valid.
*
* @deprecated stream_for will be removed in guzzlehttp/psr7:2.0. Use Utils::streamFor instead.
*/
function stream_for($resource = '', array $options = [])
{
if (is_scalar($resource)) {
$stream = fopen('php://temp', 'r+');
if ($resource !== '') {
fwrite($stream, $resource);
fseek($stream, 0);
}
return new Stream($stream, $options);
}
switch (gettype($resource)) {
case 'resource':
return new Stream($resource, $options);
case 'object':
if ($resource instanceof StreamInterface) {
return $resource;
} elseif ($resource instanceof \Iterator) {
return new PumpStream(function () use ($resource) {
if (!$resource->valid()) {
return false;
}
$result = $resource->current();
$resource->next();
return $result;
}, $options);
} elseif (method_exists($resource, '__toString')) {
return stream_for((string) $resource, $options);
}
break;
case 'NULL':
return new Stream(fopen('php://temp', 'r+'), $options);
}
if (is_callable($resource)) {
return new PumpStream($resource, $options);
}
throw new \InvalidArgumentException('Invalid resource type: ' . gettype($resource));
return Utils::streamFor($resource, $options);
}
/**
* Parse an array of header values containing ";" separated data into an
* array of associative arrays representing the header key value pair
* data of the header. When a parameter does not contain a value, but just
* array of associative arrays representing the header key value pair data
* of the header. When a parameter does not contain a value, but just
* contains a key, this function will inject a key with a '' string value.
*
* @param string|array $header Header to parse into components.
*
* @return array Returns the parsed header values.
*
* @deprecated parse_header will be removed in guzzlehttp/psr7:2.0. Use Header::parse instead.
*/
function parse_header($header)
{
static $trimmed = "\"' \n\t\r";
$params = $matches = [];
foreach (normalize_header($header) as $val) {
$part = [];
foreach (preg_split('/;(?=([^"]*"[^"]*")*[^"]*$)/', $val) as $kvp) {
if (preg_match_all('/<[^>]+>|[^=]+/', $kvp, $matches)) {
$m = $matches[0];
if (isset($m[1])) {
$part[trim($m[0], $trimmed)] = trim($m[1], $trimmed);
} else {
$part[] = trim($m[0], $trimmed);
}
}
}
if ($part) {
$params[] = $part;
}
}
return $params;
return Header::parse($header);
}
/**
@ -158,32 +108,20 @@ function parse_header($header)
* @param string|array $header Header to normalize.
*
* @return array Returns the normalized header field values.
*
* @deprecated normalize_header will be removed in guzzlehttp/psr7:2.0. Use Header::normalize instead.
*/
function normalize_header($header)
{
if (!is_array($header)) {
return array_map('trim', explode(',', $header));
}
$result = [];
foreach ($header as $value) {
foreach ((array) $value as $v) {
if (strpos($v, ',') === false) {
$result[] = $v;
continue;
}
foreach (preg_split('/,(?=([^"]*"[^"]*")*[^"]*$)/', $v) as $vv) {
$result[] = trim($vv);
}
}
}
return $result;
return Header::normalize($header);
}
/**
* Clone and modify a request with the given changes.
*
* This method is useful for reducing the number of clones needed to mutate a
* message.
*
* The changes can be one of:
* - method: (string) Changes the HTTP method.
* - set_headers: (array) Sets the given headers.
@ -197,72 +135,12 @@ function normalize_header($header)
* @param array $changes Changes to apply.
*
* @return RequestInterface
*
* @deprecated modify_request will be removed in guzzlehttp/psr7:2.0. Use Utils::modifyRequest instead.
*/
function modify_request(RequestInterface $request, array $changes)
{
if (!$changes) {
return $request;
}
$headers = $request->getHeaders();
if (!isset($changes['uri'])) {
$uri = $request->getUri();
} else {
// Remove the host header if one is on the URI
if ($host = $changes['uri']->getHost()) {
$changes['set_headers']['Host'] = $host;
if ($port = $changes['uri']->getPort()) {
$standardPorts = ['http' => 80, 'https' => 443];
$scheme = $changes['uri']->getScheme();
if (isset($standardPorts[$scheme]) && $port != $standardPorts[$scheme]) {
$changes['set_headers']['Host'] .= ':'.$port;
}
}
}
$uri = $changes['uri'];
}
if (!empty($changes['remove_headers'])) {
$headers = _caseless_remove($changes['remove_headers'], $headers);
}
if (!empty($changes['set_headers'])) {
$headers = _caseless_remove(array_keys($changes['set_headers']), $headers);
$headers = $changes['set_headers'] + $headers;
}
if (isset($changes['query'])) {
$uri = $uri->withQuery($changes['query']);
}
if ($request instanceof ServerRequestInterface) {
return (new ServerRequest(
isset($changes['method']) ? $changes['method'] : $request->getMethod(),
$uri,
$headers,
isset($changes['body']) ? $changes['body'] : $request->getBody(),
isset($changes['version'])
? $changes['version']
: $request->getProtocolVersion(),
$request->getServerParams()
))
->withParsedBody($request->getParsedBody())
->withQueryParams($request->getQueryParams())
->withCookieParams($request->getCookieParams())
->withUploadedFiles($request->getUploadedFiles());
}
return new Request(
isset($changes['method']) ? $changes['method'] : $request->getMethod(),
$uri,
$headers,
isset($changes['body']) ? $changes['body'] : $request->getBody(),
isset($changes['version'])
? $changes['version']
: $request->getProtocolVersion()
);
return Utils::modifyRequest($request, $changes);
}
/**
@ -274,14 +152,12 @@ function modify_request(RequestInterface $request, array $changes)
* @param MessageInterface $message Message to rewind
*
* @throws \RuntimeException
*
* @deprecated rewind_body will be removed in guzzlehttp/psr7:2.0. Use Message::rewindBody instead.
*/
function rewind_body(MessageInterface $message)
{
$body = $message->getBody();
if ($body->tell()) {
$body->rewind();
}
Message::rewindBody($message);
}
/**
@ -294,29 +170,14 @@ function rewind_body(MessageInterface $message)
* @param string $mode Mode used to open the file
*
* @return resource
*
* @throws \RuntimeException if the file cannot be opened
*
* @deprecated try_fopen will be removed in guzzlehttp/psr7:2.0. Use Utils::tryFopen instead.
*/
function try_fopen($filename, $mode)
{
$ex = null;
set_error_handler(function () use ($filename, $mode, &$ex) {
$ex = new \RuntimeException(sprintf(
'Unable to open %s using mode %s: %s',
$filename,
$mode,
func_get_args()[1]
));
});
$handle = fopen($filename, $mode);
restore_error_handler();
if ($ex) {
/** @var $ex \RuntimeException */
throw $ex;
}
return $handle;
return Utils::tryFopen($filename, $mode);
}
/**
@ -327,36 +188,14 @@ function try_fopen($filename, $mode)
* @param int $maxLen Maximum number of bytes to read. Pass -1
* to read the entire stream.
* @return string
*
* @throws \RuntimeException on error.
*
* @deprecated copy_to_string will be removed in guzzlehttp/psr7:2.0. Use Utils::copyToString instead.
*/
function copy_to_string(StreamInterface $stream, $maxLen = -1)
{
$buffer = '';
if ($maxLen === -1) {
while (!$stream->eof()) {
$buf = $stream->read(1048576);
// Using a loose equality here to match on '' and false.
if ($buf == null) {
break;
}
$buffer .= $buf;
}
return $buffer;
}
$len = 0;
while (!$stream->eof() && $len < $maxLen) {
$buf = $stream->read($maxLen - $len);
// Using a loose equality here to match on '' and false.
if ($buf == null) {
break;
}
$buffer .= $buf;
$len = strlen($buffer);
}
return $buffer;
return Utils::copyToString($stream, $maxLen);
}
/**
@ -369,92 +208,48 @@ function copy_to_string(StreamInterface $stream, $maxLen = -1)
* to read the entire stream.
*
* @throws \RuntimeException on error.
*
* @deprecated copy_to_stream will be removed in guzzlehttp/psr7:2.0. Use Utils::copyToStream instead.
*/
function copy_to_stream(
StreamInterface $source,
StreamInterface $dest,
$maxLen = -1
) {
$bufferSize = 8192;
if ($maxLen === -1) {
while (!$source->eof()) {
if (!$dest->write($source->read($bufferSize))) {
break;
}
}
} else {
$remaining = $maxLen;
while ($remaining > 0 && !$source->eof()) {
$buf = $source->read(min($bufferSize, $remaining));
$len = strlen($buf);
if (!$len) {
break;
}
$remaining -= $len;
$dest->write($buf);
}
}
function copy_to_stream(StreamInterface $source, StreamInterface $dest, $maxLen = -1)
{
return Utils::copyToStream($source, $dest, $maxLen);
}
/**
* Calculate a hash of a Stream
* Calculate a hash of a stream.
*
* This method reads the entire stream to calculate a rolling hash, based on
* PHP's `hash_init` functions.
*
* @param StreamInterface $stream Stream to calculate the hash for
* @param string $algo Hash algorithm (e.g. md5, crc32, etc)
* @param bool $rawOutput Whether or not to use raw output
*
* @return string Returns the hash of the stream
*
* @throws \RuntimeException on error.
*
* @deprecated hash will be removed in guzzlehttp/psr7:2.0. Use Utils::hash instead.
*/
function hash(
StreamInterface $stream,
$algo,
$rawOutput = false
) {
$pos = $stream->tell();
if ($pos > 0) {
$stream->rewind();
}
$ctx = hash_init($algo);
while (!$stream->eof()) {
hash_update($ctx, $stream->read(1048576));
}
$out = hash_final($ctx, (bool) $rawOutput);
$stream->seek($pos);
return $out;
function hash(StreamInterface $stream, $algo, $rawOutput = false)
{
return Utils::hash($stream, $algo, $rawOutput);
}
/**
* Read a line from the stream up to the maximum allowed buffer length
* Read a line from the stream up to the maximum allowed buffer length.
*
* @param StreamInterface $stream Stream to read from
* @param int $maxLength Maximum buffer length
* @param int|null $maxLength Maximum buffer length
*
* @return string
*
* @deprecated readline will be removed in guzzlehttp/psr7:2.0. Use Utils::readLine instead.
*/
function readline(StreamInterface $stream, $maxLength = null)
{
$buffer = '';
$size = 0;
while (!$stream->eof()) {
// Using a loose equality here to match on '' and false.
if (null == ($byte = $stream->read(1))) {
return $buffer;
}
$buffer .= $byte;
// Break when a new line is found or the max length - 1 is reached
if ($byte === "\n" || ++$size === $maxLength - 1) {
break;
}
}
return $buffer;
return Utils::readLine($stream, $maxLength);
}
/**
@ -463,26 +258,12 @@ function readline(StreamInterface $stream, $maxLength = null)
* @param string $message Request message string.
*
* @return Request
*
* @deprecated parse_request will be removed in guzzlehttp/psr7:2.0. Use Message::parseRequest instead.
*/
function parse_request($message)
{
$data = _parse_message($message);
$matches = [];
if (!preg_match('/^[\S]+\s+([a-zA-Z]+:\/\/|\/).*/', $data['start-line'], $matches)) {
throw new \InvalidArgumentException('Invalid request string');
}
$parts = explode(' ', $data['start-line'], 3);
$version = isset($parts[2]) ? explode('/', $parts[2])[1] : '1.1';
$request = new Request(
$parts[0],
$matches[1] === '/' ? _parse_request_uri($parts[1], $data['headers']) : $parts[1],
$data['headers'],
$data['body'],
$version
);
return $matches[1] === '/' ? $request : $request->withRequestTarget($parts[1]);
return Message::parseRequest($message);
}
/**
@ -491,139 +272,66 @@ function parse_request($message)
* @param string $message Response message string.
*
* @return Response
*
* @deprecated parse_response will be removed in guzzlehttp/psr7:2.0. Use Message::parseResponse instead.
*/
function parse_response($message)
{
$data = _parse_message($message);
// According to https://tools.ietf.org/html/rfc7230#section-3.1.2 the space
// between status-code and reason-phrase is required. But browsers accept
// responses without space and reason as well.
if (!preg_match('/^HTTP\/.* [0-9]{3}( .*|$)/', $data['start-line'])) {
throw new \InvalidArgumentException('Invalid response string: ' . $data['start-line']);
}
$parts = explode(' ', $data['start-line'], 3);
return new Response(
$parts[1],
$data['headers'],
$data['body'],
explode('/', $parts[0])[1],
isset($parts[2]) ? $parts[2] : null
);
return Message::parseResponse($message);
}
/**
* Parse a query string into an associative array.
*
* If multiple values are found for the same key, the value of that key
* value pair will become an array. This function does not parse nested
* PHP style arrays into an associative array (e.g., foo[a]=1&foo[b]=2 will
* be parsed into ['foo[a]' => '1', 'foo[b]' => '2']).
* If multiple values are found for the same key, the value of that key value
* pair will become an array. This function does not parse nested PHP style
* arrays into an associative array (e.g., `foo[a]=1&foo[b]=2` will be parsed
* into `['foo[a]' => '1', 'foo[b]' => '2'])`.
*
* @param string $str Query string to parse
* @param int|bool $urlEncoding How the query string is encoded
*
* @return array
*
* @deprecated parse_query will be removed in guzzlehttp/psr7:2.0. Use Query::parse instead.
*/
function parse_query($str, $urlEncoding = true)
{
$result = [];
if ($str === '') {
return $result;
}
if ($urlEncoding === true) {
$decoder = function ($value) {
return rawurldecode(str_replace('+', ' ', $value));
};
} elseif ($urlEncoding === PHP_QUERY_RFC3986) {
$decoder = 'rawurldecode';
} elseif ($urlEncoding === PHP_QUERY_RFC1738) {
$decoder = 'urldecode';
} else {
$decoder = function ($str) { return $str; };
}
foreach (explode('&', $str) as $kvp) {
$parts = explode('=', $kvp, 2);
$key = $decoder($parts[0]);
$value = isset($parts[1]) ? $decoder($parts[1]) : null;
if (!isset($result[$key])) {
$result[$key] = $value;
} else {
if (!is_array($result[$key])) {
$result[$key] = [$result[$key]];
}
$result[$key][] = $value;
}
}
return $result;
return Query::parse($str, $urlEncoding);
}
/**
* Build a query string from an array of key value pairs.
*
* This function can use the return value of parse_query() to build a query
* This function can use the return value of `parse_query()` to build a query
* string. This function does not modify the provided keys when an array is
* encountered (like http_build_query would).
* encountered (like `http_build_query()` would).
*
* @param array $params Query string parameters.
* @param int|false $encoding Set to false to not encode, PHP_QUERY_RFC3986
* to encode using RFC3986, or PHP_QUERY_RFC1738
* to encode using RFC1738.
* @return string
*
* @deprecated build_query will be removed in guzzlehttp/psr7:2.0. Use Query::build instead.
*/
function build_query(array $params, $encoding = PHP_QUERY_RFC3986)
{
if (!$params) {
return '';
}
if ($encoding === false) {
$encoder = function ($str) { return $str; };
} elseif ($encoding === PHP_QUERY_RFC3986) {
$encoder = 'rawurlencode';
} elseif ($encoding === PHP_QUERY_RFC1738) {
$encoder = 'urlencode';
} else {
throw new \InvalidArgumentException('Invalid type');
}
$qs = '';
foreach ($params as $k => $v) {
$k = $encoder($k);
if (!is_array($v)) {
$qs .= $k;
if ($v !== null) {
$qs .= '=' . $encoder($v);
}
$qs .= '&';
} else {
foreach ($v as $vv) {
$qs .= $k;
if ($vv !== null) {
$qs .= '=' . $encoder($vv);
}
$qs .= '&';
}
}
}
return $qs ? (string) substr($qs, 0, -1) : '';
return Query::build($params, $encoding);
}
/**
* Determines the mimetype of a file by looking at its extension.
*
* @param $filename
* @param string $filename
*
* @return null|string
* @return string|null
*
* @deprecated mimetype_from_filename will be removed in guzzlehttp/psr7:2.0. Use MimeType::fromFilename instead.
*/
function mimetype_from_filename($filename)
{
return mimetype_from_extension(pathinfo($filename, PATHINFO_EXTENSION));
return MimeType::fromFilename($filename);
}
/**
@ -632,119 +340,13 @@ function mimetype_from_filename($filename)
* @param $extension string The file extension.
*
* @return string|null
*
* @link http://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x/conf/mime.types
* @deprecated mimetype_from_extension will be removed in guzzlehttp/psr7:2.0. Use MimeType::fromExtension instead.
*/
function mimetype_from_extension($extension)
{
static $mimetypes = [
'3gp' => 'video/3gpp',
'7z' => 'application/x-7z-compressed',
'aac' => 'audio/x-aac',
'ai' => 'application/postscript',
'aif' => 'audio/x-aiff',
'asc' => 'text/plain',
'asf' => 'video/x-ms-asf',
'atom' => 'application/atom+xml',
'avi' => 'video/x-msvideo',
'bmp' => 'image/bmp',
'bz2' => 'application/x-bzip2',
'cer' => 'application/pkix-cert',
'crl' => 'application/pkix-crl',
'crt' => 'application/x-x509-ca-cert',
'css' => 'text/css',
'csv' => 'text/csv',
'cu' => 'application/cu-seeme',
'deb' => 'application/x-debian-package',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dvi' => 'application/x-dvi',
'eot' => 'application/vnd.ms-fontobject',
'eps' => 'application/postscript',
'epub' => 'application/epub+zip',
'etx' => 'text/x-setext',
'flac' => 'audio/flac',
'flv' => 'video/x-flv',
'gif' => 'image/gif',
'gz' => 'application/gzip',
'htm' => 'text/html',
'html' => 'text/html',
'ico' => 'image/x-icon',
'ics' => 'text/calendar',
'ini' => 'text/plain',
'iso' => 'application/x-iso9660-image',
'jar' => 'application/java-archive',
'jpe' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'jpg' => 'image/jpeg',
'js' => 'text/javascript',
'json' => 'application/json',
'latex' => 'application/x-latex',
'log' => 'text/plain',
'm4a' => 'audio/mp4',
'm4v' => 'video/mp4',
'mid' => 'audio/midi',
'midi' => 'audio/midi',
'mov' => 'video/quicktime',
'mkv' => 'video/x-matroska',
'mp3' => 'audio/mpeg',
'mp4' => 'video/mp4',
'mp4a' => 'audio/mp4',
'mp4v' => 'video/mp4',
'mpe' => 'video/mpeg',
'mpeg' => 'video/mpeg',
'mpg' => 'video/mpeg',
'mpg4' => 'video/mp4',
'oga' => 'audio/ogg',
'ogg' => 'audio/ogg',
'ogv' => 'video/ogg',
'ogx' => 'application/ogg',
'pbm' => 'image/x-portable-bitmap',
'pdf' => 'application/pdf',
'pgm' => 'image/x-portable-graymap',
'png' => 'image/png',
'pnm' => 'image/x-portable-anymap',
'ppm' => 'image/x-portable-pixmap',
'ppt' => 'application/vnd.ms-powerpoint',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'ps' => 'application/postscript',
'qt' => 'video/quicktime',
'rar' => 'application/x-rar-compressed',
'ras' => 'image/x-cmu-raster',
'rss' => 'application/rss+xml',
'rtf' => 'application/rtf',
'sgm' => 'text/sgml',
'sgml' => 'text/sgml',
'svg' => 'image/svg+xml',
'swf' => 'application/x-shockwave-flash',
'tar' => 'application/x-tar',
'tif' => 'image/tiff',
'tiff' => 'image/tiff',
'torrent' => 'application/x-bittorrent',
'ttf' => 'application/x-font-ttf',
'txt' => 'text/plain',
'wav' => 'audio/x-wav',
'webm' => 'video/webm',
'webp' => 'image/webp',
'wma' => 'audio/x-ms-wma',
'wmv' => 'video/x-ms-wmv',
'woff' => 'application/x-font-woff',
'wsdl' => 'application/wsdl+xml',
'xbm' => 'image/x-xbitmap',
'xls' => 'application/vnd.ms-excel',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xml' => 'application/xml',
'xpm' => 'image/x-xpixmap',
'xwd' => 'image/x-xwindowdump',
'yaml' => 'text/yaml',
'yml' => 'text/yaml',
'zip' => 'application/zip',
];
$extension = strtolower($extension);
return isset($mimetypes[$extension])
? $mimetypes[$extension]
: null;
return MimeType::fromExtension($extension);
}
/**
@ -757,61 +359,13 @@ function mimetype_from_extension($extension)
* @param string $message HTTP request or response to parse.
*
* @return array
*
* @internal
* @deprecated _parse_message will be removed in guzzlehttp/psr7:2.0. Use Message::parseMessage instead.
*/
function _parse_message($message)
{
if (!$message) {
throw new \InvalidArgumentException('Invalid message');
}
$message = ltrim($message, "\r\n");
$messageParts = preg_split("/\r?\n\r?\n/", $message, 2);
if ($messageParts === false || count($messageParts) !== 2) {
throw new \InvalidArgumentException('Invalid message: Missing header delimiter');
}
list($rawHeaders, $body) = $messageParts;
$rawHeaders .= "\r\n"; // Put back the delimiter we split previously
$headerParts = preg_split("/\r?\n/", $rawHeaders, 2);
if ($headerParts === false || count($headerParts) !== 2) {
throw new \InvalidArgumentException('Invalid message: Missing status line');
}
list($startLine, $rawHeaders) = $headerParts;
if (preg_match("/(?:^HTTP\/|^[A-Z]+ \S+ HTTP\/)(\d+(?:\.\d+)?)/i", $startLine, $matches) && $matches[1] === '1.0') {
// Header folding is deprecated for HTTP/1.1, but allowed in HTTP/1.0
$rawHeaders = preg_replace(Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders);
}
/** @var array[] $headerLines */
$count = preg_match_all(Rfc7230::HEADER_REGEX, $rawHeaders, $headerLines, PREG_SET_ORDER);
// If these aren't the same, then one line didn't match and there's an invalid header.
if ($count !== substr_count($rawHeaders, "\n")) {
// Folding is deprecated, see https://tools.ietf.org/html/rfc7230#section-3.2.4
if (preg_match(Rfc7230::HEADER_FOLD_REGEX, $rawHeaders)) {
throw new \InvalidArgumentException('Invalid header syntax: Obsolete line folding');
}
throw new \InvalidArgumentException('Invalid header syntax');
}
$headers = [];
foreach ($headerLines as $headerLine) {
$headers[$headerLine[1]][] = $headerLine[2];
}
return [
'start-line' => $startLine,
'headers' => $headers,
'body' => $body,
];
return Message::parseMessage($message);
}
/**
@ -821,79 +375,43 @@ function _parse_message($message)
* @param array $headers Array of headers (each value an array).
*
* @return string
*
* @internal
* @deprecated _parse_request_uri will be removed in guzzlehttp/psr7:2.0. Use Message::parseRequestUri instead.
*/
function _parse_request_uri($path, array $headers)
{
$hostKey = array_filter(array_keys($headers), function ($k) {
return strtolower($k) === 'host';
});
// If no host is found, then a full URI cannot be constructed.
if (!$hostKey) {
return $path;
}
$host = $headers[reset($hostKey)][0];
$scheme = substr($host, -4) === ':443' ? 'https' : 'http';
return $scheme . '://' . $host . '/' . ltrim($path, '/');
return Message::parseRequestUri($path, $headers);
}
/**
* Get a short summary of the message body
* Get a short summary of the message body.
*
* Will return `null` if the response is not printable.
*
* @param MessageInterface $message The message to get the body summary
* @param int $truncateAt The maximum allowed size of the summary
*
* @return null|string
* @return string|null
*
* @deprecated get_message_body_summary will be removed in guzzlehttp/psr7:2.0. Use Message::bodySummary instead.
*/
function get_message_body_summary(MessageInterface $message, $truncateAt = 120)
{
$body = $message->getBody();
if (!$body->isSeekable() || !$body->isReadable()) {
return null;
}
$size = $body->getSize();
if ($size === 0) {
return null;
}
$summary = $body->read($truncateAt);
$body->rewind();
if ($size > $truncateAt) {
$summary .= ' (truncated...)';
}
// Matches any printable character, including unicode characters:
// letters, marks, numbers, punctuation, spacing, and separators.
if (preg_match('/[^\pL\pM\pN\pP\pS\pZ\n\r\t]/', $summary)) {
return null;
}
return $summary;
return Message::bodySummary($message, $truncateAt);
}
/** @internal */
/**
* Remove the items given by the keys, case insensitively from the data.
*
* @param iterable<string> $keys
*
* @return array
*
* @internal
* @deprecated _caseless_remove will be removed in guzzlehttp/psr7:2.0. Use Utils::caselessRemove instead.
*/
function _caseless_remove($keys, array $data)
{
$result = [];
foreach ($keys as &$key) {
$key = strtolower($key);
}
foreach ($data as $k => $v) {
if (!in_array(strtolower($k), $keys)) {
$result[$k] = $v;
}
}
return $result;
return Utils::caselessRemove($keys, $data);
}

View file

@ -70,6 +70,7 @@ class GeneratedExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
'azs' => 'application/vnd.airzip.filesecure.azs',
'azv' => 'image/vnd.airzip.accelerator.azv',
'azw' => 'application/vnd.amazon.ebook',
'b16' => 'image/vnd.pco.b16',
'bat' => 'application/x-msdownload',
'bcpio' => 'application/x-bcpio',
'bdf' => 'application/x-font-bdf',

View file

@ -1,335 +0,0 @@
# OAuth 2.0 Client Changelog
## 2.5.0
_Released: 2020-07-18_
* Allow Guzzle 7.x to be used [#847](https://github.com/thephpleague/oauth2-client/pull/847)
## 2.4.1
_Released: 2018-11-22_
* Revert to use of `AccessToken` in type hints to preserve backwards
compatibility; this fixes the issue reported in [#752](https://github.com/thephpleague/oauth2-client/issues/752)
and [#753](https://github.com/thephpleague/oauth2-client/issues/753)
## 2.4.0
_Released: 2018-11-21_
* Add `HttpBasicAuthOptionProvider` to ease implementation for providers
requiring HTTP basic auth
* Add `GuardedPropertyTrait` to allow providers the ability to specify
properties that may not be overridden by user-defined values passed to the
provider constructor
* Add `AccessTokenInterface` and `ResourceOwnerAccessTokenInterface` to allow
providers the ability to override the default `AccessToken`
## 2.3.1
_Released: 2018-11-19_
* Allow paragonie/random_compat's empty 9.99.99 placeholder
* Throw an `UnexpectedValueException` on non-JSON responses from access token
request (when calling `AbstractProvider::getAccessToken()`)
## 2.3.0
_Released: 2018-01-13_
* Add `ProviderRedirectTrait` tool for 3rd-party provider libraries to use when
handling provider redirections
* Fix TypeError thrown because `getResourceOwner()` receives a non-JSON Response
* Gracefully handle non-standard errors received from providers
* Update README to reflect official support of PHP 7.2
## 2.2.1
_Released: 2017-04-25_
* Fix potential type error when HTTP 500 errors are encountered
* Allow broader range of `random_compat` versions
## 2.2.0
_Released: 2017-02-01_
* Allow base URLs to contain query parameters
* Protect against `+` being improperly encoded in URL parameters
* Remove misleading `state` option from authorization parameters
* Stop generating more random bytes than necessary
## 2.1.0
_Released: 2017-01-24_
* Allow `expires_in` with a value of `0`
## 2.0.0
_Released: 2017-01-12_
* Rename `getResponse()` to `getParsedResponse()`
* Add `getResponse()` method that returns the unparsed PSR-7 `Response` instance
* Removed `RandomFactory`, switched to native random functions
## 1.4.1
_Released: 2016-04-29_
* Add `QueryBuilderTrait` to standardize query string generation.
## 1.4.0
_Released: 2016-04-19_
* Add `AccessToken::getValues()` to access additional vendor data provided with tokens.
## 1.3.0
_Released: 2016-02-13_
* Enable dynamic parameters being passed into the authorization URL.
* Minor documentation updates.
## 1.2.0
_Released: 2016-01-23_
* Add `resource_owner_id` to the JSON-serialized representation of the access token.
* Minor documentation updates and improved test coverage.
## 1.1.0
_Released: 2015-11-13_
* Add `ArrayAccessorTrait`, update `AbstractProvider` to utilize.
* Use `expires` to serialize access tokens.
* Documentation updates.
## 1.0.2
_Released: 2015-09-22_
* Allow access tokens to be created from storage (see #431).
* Minor fixes and documentation updates.
## 1.0.1
_Released: 2015-08-26_
* Allow required parameters checked using the `RequiredParameterTrait` to be set as `false`, `null`, `"0"`, etc.
## 1.0.0
_Released: 2015-08-19_
* We are running code-quality builds through Scrutinizer, and we are running unit test builds on the new Travis CI container-based infrastructure.
* Cleaned up code, as recommended by Scrutinizer.
* Documentation updates.
## 1.0.0-beta2
_Released: 2015-08-12_
* BREAK: Add toArray() to ResourceOwnerInterface.
* Always attempt to parse responses as JSON and fallback on failure.
* Add dot notation support to access token resource owner ID.
* Use the Bearer authorization header for the generic provider.
* Documentation updates.
## 1.0.0-beta1
_Released: 2015-07-16_
* API for 1.0 is now frozen!
* BREAK: Convert all uses of "User" to "ResourceOwner" to more closely match the OAuth 2.0 specification.
* BREAK: Rename `StandardProvider` to `GenericProvider`.
* BREAK: Move access token creation to the `AbstractProvider`. It was previously handled in the `AbstractGrant`.
* FIX: Add `Content-Type` header with value of `application/x-www-form-urlencoded` to the request header when retrieving access tokens. This adheres to the OAuth 2.0 specification and fixes issues where certain OAuth servers expect this header.
* Enhanced `json_encode()` serialization of AccessToken; when using `json_encode()` on an AccessToken, it will return a JSON object with these properties: `access_token`, `refresh_token`, and `expires_in`.
## 1.0.0-alpha2
_Released: 2015-07-04_
* BREAK: Renamed `AbstractProvider::ACCESS_TOKEN_METHOD_GET` to `AbstractProvider::METHOD_GET`.
* BREAK: Renamed `AbstractProvider::ACCESS_TOKEN_METHOD_POST` to `AbstractProvider::METHOD_POST`.
* BREAK: Renamed `AbstractProvider::prepareUserDetails()` to `AbstractProvider::createUser()`.
* BREAK: Renamed `AbstractProvider::getUserDetails()` to `AbstractProvider::getUser()`.
* BREAK: Removed `$token` parameter from `AbstractProvider::getDefaultHeaders()`.
* BREAK: Modify `AbstractProvider::getBaseAccessTokenUrl()` to accept a required array of parameters, allowing providers the ability to vary the access token URL, based on the parameters.
* Removed newline characters from MAC Authorization header.
* Documentation updates, notably:
- Moved list of providers to `README.PROVIDERS.md`.
- Moved provider creation notes to `README.PROVIDER-GUIDE.md`.
## 1.0.0-alpha1
_Released: 2015-06-25_
This release contains numerous BC breaks from the 0.x series. Please note these breaks and refer to the [upgrade guide](GUIDE-UPGRADING.md).
* BREAK: Requires PHP 5.5.0 and greater.
* BREAK: All providers have been moved to separate repositories, one for each provider.
* BREAK: All `public` properties have been set as `protected` or `private` and getters/setters have been introduced for access to these properties.
* BREAK: The `Provider\ProviderInterface` has been removed. Please extend from and override `Provider\AbstractProvider`.
* BREAK: The `Entity\User` has been removed. Providers should implement the `Provider\UserInterface` and provide user functionality instead of expecting it in this base library.
* BREAK: The `Grant\GrantInterface` has been removed. Providers needing to provide a new grant type should extend from and override `Grant\AbstractGrant`.
* A generic `Provider\StandardProvider` has been introduced, which may be used as a client to integrate with most OAuth 2.0 compatible servers.
* A `Grant\GrantFactory` has been introduced as a means to register and retrieve singleton grants from a registry.
* Introduced traits for bearer and MAC authorization (`Tool\BearerAuthorizationTrait` and `Tool\MacAuthorizationTrait`), which providers may use to enable these header authorization types.
## 0.12.1
_Released: 2015-06-20_
* FIX: Scope separators for LinkedIn and Instagram are now correctly a single space
## 0.12.0
_Released: 2015-06-15_
* BREAK: LinkedIn Provider: Default scopes removed from LinkedIn Provider. See "[Managing LinkedIn Scopes](https://github.com/thephpleague/oauth2-client/blob/9cea9864c2e89bce1b922d1e37ba5378b3b0b264/README.md#managing-linkedin-scopes)" in the README for information on how to set scopes. See [#327](https://github.com/thephpleague/oauth2-client/pull/327) and [#307](https://github.com/thephpleague/oauth2-client/pull/307) for details on this change.
* FIX: LinkedIn Provider: A scenario existed in which `publicProfileUrl` was not set, generating a PHP notice; this has been fixed.
* FIX: Instagram Provider: Fixed scope separator.
* Documentation updates and corrections.
## 0.11.0
_Released: 2015-04-25_
* Identity Provider: Better handling of error responses
* Documentation updates
## 0.10.1
_Released: 2015-04-02_
* FIX: Invalid JSON triggering fatal error
* FIX: Sending headers along with auth `getAccessToken()` requests
* Now running Travis CI tests on PHP 7
* Documentation updates
## 0.10.0
_Released: 2015-03-10_
* Providers: Added `getHeaders()` to ProviderInterface and updated AbstractProvider to provide the method
* Providers: Updated all bundled providers to support new `$authorizationHeader` property
* Identity Provider: Update IDPException to account for empty strings
* Identity Provider: Added `getResponseBody()` method to IDPException
* Documentation updates, minor bug fixes, and coding standards fixes
## 0.9.0
_Released: 2015-02-24_
* Add `AbstractProvider::prepareAccessTokenResult()` to provide additional token response preparation to providers
* Remove custom provider code from AccessToken
* Add links to README for Dropbox and Square providers
## 0.8.1
_Released: 2015-02-12_
* Allow `approval_prompt` to be set by providers. This fixes an issue where some providers have problems if the `approval_prompt` is present in the query string.
## 0.8.0
_Released: 2015-02-10_
* Facebook Provider: Upgrade to Graph API v2.2
* Google Provider: Add `access_type` parameter for Google authorization URL
* Get a more reliable response body on errors
## 0.7.2
_Released: 2015-02-03_
* GitHub Provider: Fix regression
* Documentation updates
## 0.7.1
_Released: 2015-01-06_
* Google Provider: fixed issue where Google API was not returning the user ID
## 0.7.0
_Released: 2014-12-29_
* Improvements to Provider\AbstractProvider (addition of `userUid()`, `userEmail()`, and `userScreenName()`)
* GitHub Provider: Support for GitHub Enterprise
* GitHub Provider: Methods to allow fetching user email addresses
* Google Provider: Updated scopes and endpoints to remove deprecated values
* Documentation updates, minor bug fixes, and coding standards fixes
## 0.6.0
_Released: 2014-12-03_
* Added ability to specify a redirect handler for providers through use of a callback (see [Provider\AbstractProvider::setRedirectHandler()](https://github.com/thephpleague/oauth2-client/blob/55de45401eaa21f53c0b2414091da6f3b0f3fcb7/src/Provider/AbstractProvider.php#L314-L317))
* Updated authorize and token URLs for the Microsoft provider; the old URLs had been phased out and were no longer working (see #146)
* Increased test coverage
* Documentation updates, minor bug fixes, and coding standards fixes
## 0.5.0
_Released: 2014-11-28_
* Added `ClientCredentials` and `Password` grants
* Added support for providers to set their own `uid` parameter key name
* Added support for Google's `hd` (hosted domain) parameter
* Added support for providing a custom `state` parameter to the authorization URL
* LinkedIn `pictureUrl` is now an optional response element
* Added Battle.net provider package link to README
* Added Meetup provider package link to README
* Added `.gitattributes` file
* Increased test coverage
* A number of documentation fixes, minor bug fixes, and coding standards fixes
## 0.4.0
_Released: 2014-10-28_
* Added `ProviderInterface` and removed `IdentityProvider`.
* Expose generated state to allow for CSRF validation.
* Renamed `League\OAuth2\Client\Provider\User` to `League\OAuth2\Client\Entity\User`.
* Entity: User: added `gender` and `locale` properties
* Updating logic for populating the token expiration time.
## 0.3.0
_Released: 2014-04-26_
* This release made some huge leaps forward, including 100% unit-coverage and a bunch of new features.
## 0.2.0
_Released: 2013-05-28_
* No release notes available.
## 0.1.0
_Released: 2013-05-25_
* Initial release.

View file

@ -1,76 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at https://twitter.com/thephpleague. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

View file

@ -1,39 +0,0 @@
# Contributing
Contributions are **welcome** and will be fully **credited**.
We accept contributions via Pull Requests on [Github](https://github.com/thephpleague/oauth2-client).
## Pull Requests
- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer).
- **Add tests!** - Your patch won't be accepted if it doesn't have tests.
- **Document any change in behaviour** - Make sure the README and any other relevant documentation are kept up-to-date.
- **Consider our release cycle** - We try to follow SemVer. Randomly breaking public APIs is not an option.
- **Create topic branches** - Don't ask us to pull from your master branch.
- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before submitting.
- **Ensure tests pass!** - Please run the tests (see below) before submitting your pull request, and make sure they pass. We won't accept a patch until all tests pass.
- **Ensure no coding standards violations** - Please run PHP Code Sniffer using the PSR-2 standard (see below) before submitting your pull request. A violation will cause the build to fail, so please make sure there are no violations. We can't accept a patch if the build fails.
## Testing
The following tests must pass for a build to be considered successful. If contributing, please ensure these pass before submitting a pull request.
``` bash
$ ./vendor/bin/parallel-lint src test
$ ./vendor/bin/phpunit --coverage-text
$ ./vendor/bin/phpcs src --standard=psr2 -sp
```
**Happy coding**!

View file

@ -1,20 +0,0 @@
# OAuth 2.0 Client
## Authors
Also see <https://github.com/thephpleague/oauth2-client/contributors>.
### Current Maintainer
- [Ben Ramsey](https://github.com/ramsey)
### Contributors
- [Alex Bilbie](https://github.com/alexbilbie)
- [Ben Corlett](https://github.com/bencorlett)
- [Ben Ramsey](https://github.com/ramsey)
- [James Mills](https://github.com/jamesmills)
- [Phil Sturgeon](https://github.com/philsturgeon)
- [Rudi Theunissen](https://github.com/rtheunissen)
- [Tom Anderson](https://github.com/TomHAnderson)
- [Woody Gilk](https://github.com/shadowhand)

View file

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2013-2018 Alex Bilbie <hello@alexbilbie.com>
Copyright (c) 2013-2020 Alex Bilbie <hello@alexbilbie.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -1,96 +0,0 @@
# OAuth 2.0 Client
## Provider Guide
New providers may be created by copying the layout of an existing package. See
the [list of providers](docs/providers/thirdparty.md) for good examples.
When choosing a name for your package, please dont use the `league` vendor
prefix, as this implies that it is officially supported. You should use your own
username as the vendor prefix, and prepend `oauth2-` to the package name to make
it clear that your package works with OAuth2 Client. For example, if your GitHub
username was "santa," and you were implementing the "giftpay" OAuth2 library, a
good name for your composer package would be `santa/oauth2-giftpay`.
### Implementing your own provider
If you are working with an oauth2 service not supported out-of-the-box or by an
existing package, it is quite simple to implement your own. Simply extend
[`League\OAuth2\Client\Provider\AbstractProvider`](src/Provider/AbstractProvider.php)
and implement the required abstract methods:
```php
abstract public function getBaseAuthorizationUrl();
abstract public function getBaseAccessTokenUrl(array $params);
abstract public function getResourceOwnerDetailsUrl(AccessToken $token);
abstract protected function getDefaultScopes();
abstract protected function checkResponse(ResponseInterface $response, $data);
abstract protected function createResourceOwner(array $response, AccessToken $token);
```
Each of these abstract methods contain a docblock defining their expectations
and typical behavior. Once you have extended this class, you can simply follow
the [usage example in the README](README.md#usage) using your new `Provider`.
If you wish to use the `Provider` to make authenticated requests to the
service, you will also need to define how you provide the token to the
service. If this is done via headers, you should override this method:
```php
protected function getAuthorizationHeaders($token = null);
```
This package comes with a trait for implementing `Bearer` authorization.
To use this, you just need to include the trait in your `Provider` class:
```php
<?php
class SomeProvider extends AbstractProvider
{
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
/** ... **/
}
```
### Resource owner identifiers in access token responses
In services where the resource owner is a person, the resource owner is sometimes
referred to as an end-user.
We have decided to abstract away as much of the resource owner details as possible,
since these are not part of the OAuth 2.0 specification and are very specific to each
service provider. This provides greater flexibility to each provider, allowing
them to handle the implementation details for resource owners.
The `AbstractProvider` does not specify an access token resource owner identifier. It is
the responsibility of the provider class to set the `ACCESS_TOKEN_RESOURCE_OWNER_ID` constant
to the string value of the key used in the access token response to identify the
resource owner.
```php
/**
* @var string Key used in the access token response to identify the resource owner.
*/
const ACCESS_TOKEN_RESOURCE_OWNER_ID = null;
```
Once this is set on your provider, when calling `AbstractProvider::getAccessToken()`,
the `AccessToken` returned will have its `$resourceOwnerId` property set, which you may
retrieve by calling `AccessToken::getResourceOwnerId()`.
The next step is to implement the `AbstractProvider::createResourceOwner()` method. This
method accepts as parameters a response array and an `AccessToken`. You may use
this information in order to request resource owner details from your service and
construct and return an object that implements
[`League\OAuth2\Client\Provider\ResourceOwnerInterface`](src/Provider/ResourceOwnerInterface.php).
This object is returned when calling `AbstractProvider::getResourceOwner()`.
### Make your gateway official
If you want to transfer your provider to the `thephpleague` GitHub organization
and add it to the list of officially supported providers, please open a pull
request on the thephpleague/oauth2-client package. Before new providers will be
accepted, they must have 100% unit test code coverage, and follow the
conventions and code style used in other OAuth2 Client providers.

View file

@ -3,12 +3,11 @@
This package makes it simple to integrate your application with [OAuth 2.0](http://oauth.net/2/) service providers.
[![Gitter Chat](https://img.shields.io/badge/gitter-join_chat-brightgreen.svg?style=flat-square)](https://gitter.im/thephpleague/oauth2-client)
[![Source Code](http://img.shields.io/badge/source-thephpleague/oauth2--client-blue.svg?style=flat-square)](https://github.com/thephpleague/oauth2-client)
[![Source Code](https://img.shields.io/badge/source-thephpleague/oauth2--client-blue.svg?style=flat-square)](https://github.com/thephpleague/oauth2-client)
[![Latest Version](https://img.shields.io/github/release/thephpleague/oauth2-client.svg?style=flat-square)](https://github.com/thephpleague/oauth2-client/releases)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](https://github.com/thephpleague/oauth2-client/blob/master/LICENSE)
[![Build Status](https://img.shields.io/travis/thephpleague/oauth2-client/master.svg?style=flat-square)](https://travis-ci.org/thephpleague/oauth2-client)
[![Scrutinizer](https://img.shields.io/scrutinizer/g/thephpleague/oauth2-client/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/oauth2-client/)
[![Coverage Status](https://img.shields.io/coveralls/thephpleague/oauth2-client/master.svg?style=flat-square)](https://coveralls.io/r/thephpleague/oauth2-client?branch=master)
[![Build Status](https://img.shields.io/github/workflow/status/thephpleague/oauth2-client/CI?label=CI&logo=github&style=flat-square)](https://github.com/thephpleague/oauth2-client/actions?query=workflow%3ACI)
[![Codecov Code Coverage](https://img.shields.io/codecov/c/gh/thephpleague/oauth2-client?label=codecov&logo=codecov&style=flat-square)](https://codecov.io/gh/thephpleague/oauth2-client)
[![Total Downloads](https://img.shields.io/packagist/dt/league/oauth2-client.svg?style=flat-square)](https://packagist.org/packages/league/oauth2-client)
---
@ -30,6 +29,8 @@ The following versions of PHP are supported.
* PHP 7.1
* PHP 7.2
* PHP 7.3
* PHP 7.4
* PHP 8.0
## Providers

View file

@ -6,16 +6,15 @@
"sort-packages": true
},
"require": {
"php": "^5.6|^7.0",
"php": "^5.6 || ^7.0 || ^8.0",
"guzzlehttp/guzzle": "^6.0 || ^7.0",
"paragonie/random_compat": "^1|^2|^9.99"
"paragonie/random_compat": "^1 || ^2 || ^9.99"
},
"require-dev": {
"eloquent/liberator": "^2.0",
"eloquent/phony-phpunit": "^1.0|^3.0",
"jakub-onderka/php-parallel-lint": "^0.9.2",
"phpunit/phpunit": "^5.7|^6.0",
"squizlabs/php_codesniffer": "^2.3|^3.0"
"mockery/mockery": "^1.3",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpunit/phpunit": "^5.7 || ^6.0 || ^9.3",
"squizlabs/php_codesniffer": "^2.3 || ^3.0"
},
"keywords": [
"oauth",

View file

@ -49,6 +49,40 @@ class AccessToken implements AccessTokenInterface, ResourceOwnerAccessTokenInter
*/
protected $values = [];
/**
* @var int
*/
private static $timeNow;
/**
* Set the time now. This should only be used for testing purposes.
*
* @param int $timeNow the time in seconds since epoch
* @return void
*/
public static function setTimeNow($timeNow)
{
self::$timeNow = $timeNow;
}
/**
* Reset the time now if it was set for test purposes.
*
* @return void
*/
public static function resetTimeNow()
{
self::$timeNow = null;
}
/**
* @return int
*/
public function getTimeNow()
{
return self::$timeNow ? self::$timeNow : time();
}
/**
* Constructs an access token.
*
@ -80,14 +114,14 @@ class AccessToken implements AccessTokenInterface, ResourceOwnerAccessTokenInter
throw new \InvalidArgumentException('expires_in value must be an integer');
}
$this->expires = $options['expires_in'] != 0 ? time() + $options['expires_in'] : 0;
$this->expires = $options['expires_in'] != 0 ? $this->getTimeNow() + $options['expires_in'] : 0;
} elseif (!empty($options['expires'])) {
// Some providers supply the seconds until expiration rather than
// the exact timestamp. Take a best guess at which we received.
$expires = $options['expires'];
if (!$this->isExpirationTimestamp($expires)) {
$expires += time();
$expires += $this->getTimeNow();
}
$this->expires = $expires;

View file

@ -34,7 +34,7 @@ interface AccessTokenInterface extends JsonSerializable
public function getRefreshToken();
/**
* Returns the expiration timestamp, if defined.
* Returns the expiration timestamp in seconds, if defined.
*
* @return integer|null
*/

View file

@ -7,6 +7,9 @@ use League\OAuth2\Client\Provider\Exception\FacebookProviderException;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use Psr\Http\Message\ResponseInterface;
/**
* @method FacebookUser getResourceOwner(AccessToken $token)
*/
class Facebook extends AbstractProvider
{
/**
@ -103,7 +106,7 @@ class Facebook extends AbstractProvider
$fields = [
'id', 'name', 'first_name', 'last_name',
'email', 'hometown', 'picture.type(large){url,is_silhouette}',
'gender', 'link', 'age_range'
'gender', 'age_range'
];
// backwards compatibility less than 2.8

View file

@ -1,5 +0,0 @@
vendor/
scratch.php
composer.lock
build/
.php_cs.cache

View file

@ -1,14 +0,0 @@
<?php
$finder = PhpCsFixer\Finder::create()
->in(__DIR__)
;
return PhpCsFixer\Config::create()
->setRules(array(
'@Symfony' => true,
'concat_space' => array('spacing' => 'one'),
'phpdoc_annotation_without_dot' => false,
))
->setFinder($finder)
;

View file

@ -1,41 +0,0 @@
tools:
php_mess_detector: true
php_analyzer:
config:
parameter_reference_check: { enabled: false }
checkstyle: { enabled: false, no_trailing_whitespace: true, naming: { enabled: true, local_variable: '^[a-z][a-zA-Z0-9]*$', abstract_class_name: ^Abstract|Factory$, utility_class_name: 'Utils?$', constant_name: '^[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)*$', property_name: '^[a-z][a-zA-Z0-9]*$', method_name: '^(?:[a-z]|__)[a-zA-Z0-9]*$', parameter_name: '^[a-z][a-zA-Z0-9]*$', interface_name: '^[A-Z][a-zA-Z0-9]*Interface$', type_name: '^[A-Z][a-zA-Z0-9]*$', exception_name: '^[A-Z][a-zA-Z0-9]*Exception$', isser_method_name: '^(?:is|has|should|may|supports)' } }
unreachable_code: { enabled: false }
check_access_control: { enabled: false }
typo_checks: { enabled: false }
check_variables: { enabled: false }
check_calls: { enabled: true, too_many_arguments: true, missing_argument: true, argument_type_checks: lenient }
suspicious_code: { enabled: false, overriding_parameter: false, overriding_closure_use: false, parameter_closure_use_conflict: false, parameter_multiple_times: false, non_existent_class_in_instanceof_check: false, non_existent_class_in_catch_clause: false, assignment_of_null_return: false, non_commented_switch_fallthrough: false, non_commented_empty_catch_block: false, overriding_private_members: false, use_statement_alias_conflict: false, precedence_in_condition_assignment: false }
dead_assignments: { enabled: false }
verify_php_doc_comments: { enabled: false, parameters: false, return: false, suggest_more_specific_types: false, ask_for_return_if_not_inferrable: false, ask_for_param_type_annotation: false }
loops_must_use_braces: { enabled: false }
check_usage_context: { enabled: true, foreach: { value_as_reference: true, traversable: true } }
simplify_boolean_return: { enabled: false }
phpunit_checks: { enabled: false }
reflection_checks: { enabled: false }
precedence_checks: { enabled: true, assignment_in_condition: true, comparison_of_bit_result: true }
basic_semantic_checks: { enabled: false }
unused_code: { enabled: false }
deprecation_checks: { enabled: false }
useless_function_calls: { enabled: false }
metrics_lack_of_cohesion_methods: { enabled: false }
metrics_coupling: { enabled: true, stable_code: { namespace_prefixes: { }, classes: { } } }
doctrine_parameter_binding: { enabled: false }
doctrine_entity_manager_injection: { enabled: false }
symfony_request_injection: { enabled: false }
doc_comment_fixes: { enabled: false }
reflection_fixes: { enabled: false }
use_statement_fixes: { enabled: true, remove_unused: true, preserve_multiple: false, preserve_blanklines: false, order_alphabetically: false }
php_code_sniffer: true
sensiolabs_security_checker: true
php_cpd: true
php_loc: true
php_pdepend: true
external_code_coverage: true
filter:
paths:
- src/*

View file

@ -1,43 +0,0 @@
language: php
dist: trusty
sudo: false
matrix:
include:
- php: 5.3
dist: precise
env: COMPOSER_FLAGS="--prefer-lowest"
- php: 5.4
- php: 5.5
- php: 5.6
env: SCRUTINIZER=1
- php: 7.0
env: COMPOSER_FLAGS="--prefer-lowest"
- php: 7.1
env: CS_FIXER=1
- php: 7.2
- php: 7.3
env: BENCHMARK=1
- php: 7.4snapshot
fast_finish: true
cache:
directories:
- $HOME/.composer/cache
before_script:
- composer self-update
- composer update $COMPOSER_FLAGS --prefer-dist
script:
- stty cols 120
- if [ "$CS_FIXER" == 1 ]; then wget https://github.com/FriendsOfPHP/PHP-CS-Fixer/releases/download/v2.13.1/php-cs-fixer.phar && php php-cs-fixer.phar fix --dry-run --diff; fi
- mkdir -p build/logs
- ./vendor/bin/phpunit --coverage-clover=coverage.xml
after_script:
- if [ "$SCRUTINIZER" == 1 ]; then wget https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover coverage.xml; fi
- if [ "$BENCHMARK" == 1 ]; then php test/benchmark/run.php 10; fi
notifications:
irc: "irc.freenode.net#masterminds"

View file

@ -1,5 +1,11 @@
# Release Notes
2.7.4 (2020-10-01)
- #191: Fix travisci build
- #195: Add .gitattributes file with export-ignore rules
- #194: Fix query parameter parsed as character entity
2.7.3 (2020-07-05)
- #190: mitigate cyclic reference between output rules and the traverser objects

View file

@ -26,9 +26,7 @@
"php" : ">=5.3.0"
},
"require-dev": {
"satooshi/php-coveralls": "1.0.*",
"phpunit/phpunit" : "^4.8.35",
"sami/sami": "~2.0"
"phpunit/phpunit" : "^4.8.35"
},
"autoload": {
"psr-4": {"Masterminds\\": "src"}

View file

@ -1,32 +0,0 @@
<?php
require 'vendor/autoload.php';
use Masterminds\HTML5;
$html = <<< 'HERE'
<html>
<head>
<title>TEST</title>
<script language="javascript">
if (2 > 1) { alert("Math wins."); }
</script>
</head>
<body id='foo'>
<!-- This space intentionally left blank. -->
<section class="section-a pretty" id="bar1">
<h1>Hello World</h1><p>This is a test of the HTML5 parser.</p>
<hr>
&amp; Nobody nowhere.
</section>
<test xmlns:foo="http://example.com/foo">TEST</test>
<![CDATA[Because we can.]]>
&copy;
</body></html>
HERE;
$html5 = new HTML5();
$dom = $html5->loadHTML($html);
echo "Converting to HTML 5\n";
$html5->save($dom, fopen('php://stdin', 'w'));

View file

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true" bootstrap="vendor/autoload.php">
<testsuites>
<testsuite name="PHPUnit">
<directory>test/HTML5/</directory>
</testsuite>
</testsuites>
<filter>
<blacklist>
<file>systemlib.phpreflection_hni</file>
<file>src/HTML5/Parser/InputStream.php</file>
<file>src/HTML5/Serializer/RulesInterface.php</file>
<file>src/HTML5/Entities.php</file>
<file>src/HTML5/Serializer/HTML5Entities.php</file>
</blacklist>
</filter>
</phpunit>

View file

@ -1,10 +0,0 @@
<?php
use Sami\Sami;
return new Sami(__DIR__ . '/src', array(
'title' => 'HTML5-PHP API',
'build_dir' => __DIR__ . '/build/apidoc',
'cache_dir' => __DIR__ . '/build/sami-cache',
'default_opened_level' => 1,
));

View file

@ -475,7 +475,7 @@ class DOMTreeBuilder implements EventHandler
$lname = $this->normalizeTagName($name);
// Special case within 12.2.6.4.7: An end tag whose tag name is "br" should be treated as an opening tag
if ($name === 'br') {
if ('br' === $name) {
$this->parseError('Closing tag encountered for void element br.');
$this->startTag('br');

View file

@ -1181,16 +1181,11 @@ class Tokenizer
return $entity;
}
// If in an attribute, then failing to match ; means unconsume the
// entire string. Otherwise, failure to match is an error.
if ($inAttribute) {
$this->scanner->unconsume($this->scanner->position() - $start);
return '&';
}
// Failing to match ; means unconsume the entire string.
$this->scanner->unconsume($this->scanner->position() - $start);
$this->parseError('Expected &ENTITY;, got &ENTITY%s (no trailing ;) ', $tok);
return '&' . $entity;
return '&';
}
}

View file

@ -1,485 +0,0 @@
<?php
namespace Masterminds\HTML5\Tests;
use Masterminds\HTML5\Elements;
class ElementsTest extends TestCase
{
public $html5Elements = array(
'a',
'abbr',
'address',
'area',
'article',
'aside',
'audio',
'b',
'base',
'bdi',
'bdo',
'blockquote',
'body',
'br',
'button',
'canvas',
'caption',
'cite',
'code',
'col',
'colgroup',
'command',
// "data",
'datalist',
'dd',
'del',
'details',
'dfn',
'dialog',
'div',
'dl',
'dt',
'em',
'embed',
'fieldset',
'figcaption',
'figure',
'footer',
'form',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'head',
'header',
'hgroup',
'hr',
'html',
'i',
'iframe',
'img',
'input',
'ins',
'kbd',
'keygen',
'label',
'legend',
'li',
'link',
'map',
'mark',
'menu',
'meta',
'meter',
'nav',
'noscript',
'object',
'ol',
'optgroup',
'option',
'output',
'p',
'param',
'pre',
'progress',
'q',
'rp',
'rt',
'ruby',
's',
'samp',
'script',
'section',
'select',
'small',
'source',
'span',
'strong',
'style',
'sub',
'summary',
'sup',
'table',
'tbody',
'td',
'textarea',
'tfoot',
'th',
'thead',
'time',
'title',
'tr',
'track',
'u',
'ul',
'var',
'video',
'wbr',
);
public $mathmlElements = array(
'maction',
'maligngroup',
'malignmark',
'math',
'menclose',
'merror',
'mfenced',
'mfrac',
'mglyph',
'mi',
'mlabeledtr',
'mlongdiv',
'mmultiscripts',
'mn',
'mo',
'mover',
'mpadded',
'mphantom',
'mroot',
'mrow',
'ms',
'mscarries',
'mscarry',
'msgroup',
'msline',
'mspace',
'msqrt',
'msrow',
'mstack',
'mstyle',
'msub',
'msup',
'msubsup',
'mtable',
'mtd',
'mtext',
'mtr',
'munder',
'munderover',
);
public $svgElements = array(
'a',
'altGlyph',
'altGlyphDef',
'altGlyphItem',
'animate',
'animateColor',
'animateMotion',
'animateTransform',
'circle',
'clipPath',
'color-profile',
'cursor',
'defs',
'desc',
'ellipse',
'feBlend',
'feColorMatrix',
'feComponentTransfer',
'feComposite',
'feConvolveMatrix',
'feDiffuseLighting',
'feDisplacementMap',
'feDistantLight',
'feFlood',
'feFuncA',
'feFuncB',
'feFuncG',
'feFuncR',
'feGaussianBlur',
'feImage',
'feMerge',
'feMergeNode',
'feMorphology',
'feOffset',
'fePointLight',
'feSpecularLighting',
'feSpotLight',
'feTile',
'feTurbulence',
'filter',
'font',
'font-face',
'font-face-format',
'font-face-name',
'font-face-src',
'font-face-uri',
'foreignObject',
'g',
'glyph',
'glyphRef',
'hkern',
'image',
'line',
'linearGradient',
'marker',
'mask',
'metadata',
'missing-glyph',
'mpath',
'path',
'pattern',
'polygon',
'polyline',
'radialGradient',
'rect',
'script',
'set',
'stop',
'style',
'svg',
'switch',
'symbol',
'text',
'textPath',
'title',
'tref',
'tspan',
'use',
'view',
'vkern',
);
public function testIsHtml5Element()
{
foreach ($this->html5Elements as $element) {
$this->assertTrue(Elements::isHtml5Element($element), 'html5 element test failed on: ' . $element);
$this->assertTrue(Elements::isHtml5Element(strtoupper($element)), 'html5 element test failed on: ' . strtoupper($element));
}
$nonhtml5 = array(
'foo',
'bar',
'baz',
);
foreach ($nonhtml5 as $element) {
$this->assertFalse(Elements::isHtml5Element($element), 'html5 element test failed on: ' . $element);
$this->assertFalse(Elements::isHtml5Element(strtoupper($element)), 'html5 element test failed on: ' . strtoupper($element));
}
}
public function testIsMathMLElement()
{
foreach ($this->mathmlElements as $element) {
$this->assertTrue(Elements::isMathMLElement($element), 'MathML element test failed on: ' . $element);
// MathML is case sensitive so these should all fail.
$this->assertFalse(Elements::isMathMLElement(strtoupper($element)), 'MathML element test failed on: ' . strtoupper($element));
}
$nonMathML = array(
'foo',
'bar',
'baz',
);
foreach ($nonMathML as $element) {
$this->assertFalse(Elements::isMathMLElement($element), 'MathML element test failed on: ' . $element);
}
}
public function testIsSvgElement()
{
foreach ($this->svgElements as $element) {
$this->assertTrue(Elements::isSvgElement($element), 'SVG element test failed on: ' . $element);
// SVG is case sensitive so these should all fail.
$this->assertFalse(Elements::isSvgElement(strtoupper($element)), 'SVG element test failed on: ' . strtoupper($element));
}
$nonSVG = array(
'foo',
'bar',
'baz',
);
foreach ($nonSVG as $element) {
$this->assertFalse(Elements::isSvgElement($element), 'SVG element test failed on: ' . $element);
}
}
public function testIsElement()
{
foreach ($this->html5Elements as $element) {
$this->assertTrue(Elements::isElement($element), 'html5 element test failed on: ' . $element);
$this->assertTrue(Elements::isElement(strtoupper($element)), 'html5 element test failed on: ' . strtoupper($element));
}
foreach ($this->mathmlElements as $element) {
$this->assertTrue(Elements::isElement($element), 'MathML element test failed on: ' . $element);
// MathML is case sensitive so these should all fail.
$this->assertFalse(Elements::isElement(strtoupper($element)), 'MathML element test failed on: ' . strtoupper($element));
}
foreach ($this->svgElements as $element) {
$this->assertTrue(Elements::isElement($element), 'SVG element test failed on: ' . $element);
// SVG is case sensitive so these should all fail. But, there is duplication
// html5 and SVG. Since html5 is case insensitive we need to make sure
// it's not a html5 element first.
if (!in_array($element, $this->html5Elements)) {
$this->assertFalse(Elements::isElement(strtoupper($element)), 'SVG element test failed on: ' . strtoupper($element));
}
}
$nonhtml5 = array(
'foo',
'bar',
'baz',
);
foreach ($nonhtml5 as $element) {
$this->assertFalse(Elements::isElement($element), 'html5 element test failed on: ' . $element);
$this->assertFalse(Elements::isElement(strtoupper($element)), 'html5 element test failed on: ' . strtoupper($element));
}
}
public function testElement()
{
foreach ($this->html5Elements as $element) {
$this->assertGreaterThan(0, Elements::element($element));
}
$nonhtml5 = array(
'foo',
'bar',
'baz',
);
foreach ($nonhtml5 as $element) {
$this->assertEquals(0, Elements::element($element));
}
}
public function testIsA()
{
$this->assertTrue(Elements::isA('script', Elements::KNOWN_ELEMENT));
$this->assertFalse(Elements::isA('scriptypoo', Elements::KNOWN_ELEMENT));
$this->assertTrue(Elements::isA('script', Elements::TEXT_RAW));
$this->assertFalse(Elements::isA('script', Elements::TEXT_RCDATA));
$voidElements = array(
'area',
'base',
'basefont',
'bgsound',
'br',
'col',
'command',
'embed',
'frame',
'hr',
'img',
);
foreach ($voidElements as $element) {
$this->assertTrue(Elements::isA($element, Elements::VOID_TAG), 'Void element test failed on: ' . $element);
}
$nonVoid = array(
'span',
'a',
'div',
);
foreach ($nonVoid as $tag) {
$this->assertFalse(Elements::isA($tag, Elements::VOID_TAG), 'Void element test failed on: ' . $tag);
}
$blockTags = array(
'address',
'article',
'aside',
'blockquote',
'canvas',
'dd',
'div',
'dl',
'fieldset',
'figcaption',
'figure',
'footer',
'form',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'header',
'hgroup',
'hr',
'noscript',
'ol',
'output',
'p',
'pre',
'section',
'table',
'tfoot',
'ul',
'video',
);
foreach ($blockTags as $tag) {
$this->assertTrue(Elements::isA($tag, Elements::BLOCK_TAG), 'Block tag test failed on: ' . $tag);
}
$nonBlockTags = array(
'span',
'img',
'label',
);
foreach ($nonBlockTags as $tag) {
$this->assertFalse(Elements::isA($tag, Elements::BLOCK_TAG), 'Block tag test failed on: ' . $tag);
}
}
public function testNormalizeSvgElement()
{
$tests = array(
'foo' => 'foo',
'altglyph' => 'altGlyph',
'BAR' => 'bar',
'fespecularlighting' => 'feSpecularLighting',
'bAz' => 'baz',
'foreignobject' => 'foreignObject',
);
foreach ($tests as $input => $expected) {
$this->assertEquals($expected, Elements::normalizeSvgElement($input));
}
}
public function testNormalizeSvgAttribute()
{
$tests = array(
'foo' => 'foo',
'attributename' => 'attributeName',
'BAR' => 'bar',
'limitingconeangle' => 'limitingConeAngle',
'bAz' => 'baz',
'patterncontentunits' => 'patternContentUnits',
);
foreach ($tests as $input => $expected) {
$this->assertEquals($expected, Elements::normalizeSvgAttribute($input));
}
}
public function testNormalizeMathMlAttribute()
{
$tests = array(
'foo' => 'foo',
'definitionurl' => 'definitionURL',
'BAR' => 'bar',
);
foreach ($tests as $input => $expected) {
$this->assertEquals($expected, Elements::normalizeMathMlAttribute($input));
}
}
}

View file

@ -1,9 +0,0 @@
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
</head>
<body>
<p>Žťčýů</p>
</body>
</html>

View file

@ -1,9 +0,0 @@
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=windows-1252">
</head>
<body>
<p>Ž<EFBFBD>èýù</p>
</body>
</html>

View file

@ -1,10 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<p>This is a test.</p>
</body>
</html>

View file

@ -1,483 +0,0 @@
<?php
namespace Masterminds\HTML5\Tests;
use Masterminds\HTML5;
class Html5Test extends TestCase
{
/**
* @var HTML5
*/
private $html5;
public function setUp()
{
$this->html5 = $this->getInstance();
}
/**
* Parse and serialize a string.
*/
protected function cycle($html)
{
$dom = $this->html5->loadHTML('<!DOCTYPE html><html><body>' . $html . '</body></html>');
$out = $this->html5->saveHTML($dom);
return $out;
}
protected function cycleFragment($fragment)
{
$dom = $this->html5->loadHTMLFragment($fragment);
$out = $this->html5->saveHTML($dom);
return $out;
}
public function testImageTagsInSvg()
{
$html = '<!DOCTYPE html>
<html>
<head>
<title>foo</title>
</head>
<body>
<svg>
<image height="10" width="10"></image>
</svg>
</body>
</html>';
$doc = $this->html5->loadHTML($html);
$this->assertInstanceOf('DOMElement', $doc->getElementsByTagName('image')->item(0));
$this->assertEmpty($this->html5->getErrors());
}
public function testLoadOptions()
{
// doc
$dom = $this->html5->loadHTML($this->wrap('<t:tag/>'), array(
'implicitNamespaces' => array('t' => 'http://example.com'),
'xmlNamespaces' => true,
));
$this->assertInstanceOf('\DOMDocument', $dom);
$this->assertEmpty($this->html5->getErrors());
$this->assertFalse($this->html5->hasErrors());
$xpath = new \DOMXPath($dom);
$xpath->registerNamespace('t', 'http://example.com');
$this->assertEquals(1, $xpath->query('//t:tag')->length);
// doc fragment
$frag = $this->html5->loadHTMLFragment('<t:tag/>', array(
'implicitNamespaces' => array('t' => 'http://example.com'),
'xmlNamespaces' => true,
));
$this->assertInstanceOf('\DOMDocumentFragment', $frag);
$this->assertEmpty($this->html5->getErrors());
$this->assertFalse($this->html5->hasErrors());
$frag->ownerDocument->appendChild($frag);
$xpath = new \DOMXPath($frag->ownerDocument);
$xpath->registerNamespace('t', 'http://example.com');
$this->assertEquals(1, $xpath->query('//t:tag', $frag)->length);
}
public function testEncodingUtf8()
{
$dom = $this->html5->load(__DIR__ . '/Fixtures/encoding/utf-8.html');
$this->assertInstanceOf('\DOMDocument', $dom);
$this->assertEmpty($this->html5->getErrors());
$this->assertFalse($this->html5->hasErrors());
$this->assertContains('Žťčýů', $dom->saveHTML());
}
public function testEncodingWindows1252()
{
$dom = $this->html5->load(__DIR__ . '/Fixtures/encoding/windows-1252.html', array(
'encoding' => 'Windows-1252',
));
$this->assertInstanceOf('\DOMDocument', $dom);
$this->assertEmpty($this->html5->getErrors());
$this->assertFalse($this->html5->hasErrors());
$dumpedAsUtf8 = mb_convert_encoding($dom->saveHTML(), 'UTF-8', 'Windows-1252');
$this->assertNotFalse(mb_strpos($dumpedAsUtf8, 'Ž'));
$this->assertNotFalse(mb_strpos($dumpedAsUtf8, 'è'));
$this->assertNotFalse(mb_strpos($dumpedAsUtf8, 'ý'));
$this->assertNotFalse(mb_strpos($dumpedAsUtf8, 'ù'));
}
public function testErrors()
{
$dom = $this->html5->loadHTML('<xx as>');
$this->assertInstanceOf('\DOMDocument', $dom);
$this->assertNotEmpty($this->html5->getErrors());
$this->assertTrue($this->html5->hasErrors());
}
public function testLoad()
{
$dom = $this->html5->load(__DIR__ . '/Html5Test.html');
$this->assertInstanceOf('\DOMDocument', $dom);
$this->assertEmpty($this->html5->getErrors());
$this->assertFalse($this->html5->hasErrors());
$file = fopen(__DIR__ . '/Html5Test.html', 'r');
$dom = $this->html5->load($file);
$this->assertInstanceOf('\DOMDocument', $dom);
$this->assertEmpty($this->html5->getErrors());
$dom = $this->html5->loadHTMLFile(__DIR__ . '/Html5Test.html');
$this->assertInstanceOf('\DOMDocument', $dom);
$this->assertEmpty($this->html5->getErrors());
}
public function testLoadHTML()
{
$contents = file_get_contents(__DIR__ . '/Html5Test.html');
$dom = $this->html5->loadHTML($contents);
$this->assertInstanceOf('\DOMDocument', $dom);
$this->assertEmpty($this->html5->getErrors());
}
public function testLoadHTMLWithComments()
{
$contents = '<!--[if lte IE 8]> <html class="no-js lt-ie9" lang="en"> <![endif]-->
<!--[if gt IE 8]> <!--><html class="no-js" lang="en"><!--<![endif]-->
</html>';
$dom = $this->html5->loadHTML($contents);
$this->assertInstanceOf('\DOMDocument', $dom);
$expected = '<!DOCTYPE html>
<!--[if lte IE 8]> <html class="no-js lt-ie9" lang="en"> <![endif]--><!--[if gt IE 8]> <!--><html class="no-js" lang="en"><!--<![endif]--></html>
';
$this->assertEquals($expected, $this->html5->saveHTML($dom));
}
public function testLoadHTMLFragment()
{
$fragment = '<section id="Foo"><div class="Bar">Baz</div></section>';
$dom = $this->html5->loadHTMLFragment($fragment);
$this->assertInstanceOf('\DOMDocumentFragment', $dom);
$this->assertEmpty($this->html5->getErrors());
}
public function testSaveHTML()
{
$dom = $this->html5->load(__DIR__ . '/Html5Test.html');
$this->assertInstanceOf('\DOMDocument', $dom);
$this->assertEmpty($this->html5->getErrors());
$saved = $this->html5->saveHTML($dom);
$this->assertRegExp('|<p>This is a test.</p>|', $saved);
}
public function testSaveHTMLFragment()
{
$fragment = '<section id="Foo"><div class="Bar">Baz</div></section>';
$dom = $this->html5->loadHTMLFragment($fragment);
$string = $this->html5->saveHTML($dom);
$this->assertEquals($fragment, $string);
}
public function testSave()
{
$dom = $this->html5->load(__DIR__ . '/Html5Test.html');
$this->assertInstanceOf('\DOMDocument', $dom);
$this->assertEmpty($this->html5->getErrors());
// Test resource
$file = fopen('php://temp', 'w');
$this->html5->save($dom, $file);
$content = stream_get_contents($file, -1, 0);
$this->assertRegExp('|<p>This is a test.</p>|', $content);
// Test file
$tmpfname = tempnam(sys_get_temp_dir(), 'html5-php');
$this->html5->save($dom, $tmpfname);
$content = file_get_contents($tmpfname);
$this->assertRegExp('|<p>This is a test.</p>|', $content);
unlink($tmpfname);
}
// This test reads a document into a dom, turn the dom into a document,
// then tries to read that document again. This makes sure we are reading,
// and generating a document that works at a high level.
public function testItWorks()
{
$dom = $this->html5->load(__DIR__ . '/Html5Test.html');
$this->assertInstanceOf('\DOMDocument', $dom);
$this->assertEmpty($this->html5->getErrors());
$saved = $this->html5->saveHTML($dom);
$dom2 = $this->html5->loadHTML($saved);
$this->assertInstanceOf('\DOMDocument', $dom2);
$this->assertEmpty($this->html5->getErrors());
}
public function testConfig()
{
$html5 = $this->getInstance();
$options = $html5->getOptions();
$this->assertEquals(false, $options['encode_entities']);
$html5 = $this->getInstance(array(
'foo' => 'bar',
'encode_entities' => true,
));
$options = $html5->getOptions();
$this->assertEquals('bar', $options['foo']);
$this->assertEquals(true, $options['encode_entities']);
// Need to reset to original so future tests pass as expected.
// $this->getInstance()->setOption('encode_entities', false);
}
public function testSvg()
{
$dom = $this->html5->loadHTML(
'<!doctype html>
<html lang="en">
<body>
<div id="foo" class="bar baz">foo bar baz</div>
<svg width="150" height="100" viewBox="0 0 3 2">
<rect width="1" height="2" x="0" fill="#008d46" />
<rect width="1" height="2" x="1" fill="#ffffff" />
<rect width="1" height="2" x="2" fill="#d2232c" />
<text font-family="Verdana" font-size="32">
<textPath xlink:href="#Foo">
Test Text.
</textPath>
</text>
</svg>
</body>
</html>');
$this->assertEmpty($this->html5->getErrors());
// Test a mixed case attribute.
$list = $dom->getElementsByTagName('svg');
$this->assertNotEmpty($list->length);
$svg = $list->item(0);
$this->assertEquals('0 0 3 2', $svg->getAttribute('viewBox'));
$this->assertFalse($svg->hasAttribute('viewbox'));
// Test a mixed case tag.
// Note: getElementsByTagName is not case sensitive.
$list = $dom->getElementsByTagName('textPath');
$this->assertNotEmpty($list->length);
$textPath = $list->item(0);
$this->assertEquals('textPath', $textPath->tagName);
$this->assertNotEquals('textpath', $textPath->tagName);
$html = $this->html5->saveHTML($dom);
$this->assertRegExp('|<svg width="150" height="100" viewBox="0 0 3 2">|', $html);
$this->assertRegExp('|<rect width="1" height="2" x="0" fill="#008d46" />|', $html);
}
public function testMathMl()
{
$dom = $this->html5->loadHTML(
'<!doctype html>
<html lang="en">
<body>
<div id="foo" class="bar baz" definitionURL="http://example.com">foo bar baz</div>
<math>
<mi>x</mi>
<csymbol definitionURL="http://www.example.com/mathops/multiops.html#plusminus">
<mo>&PlusMinus;</mo>
</csymbol>
<mi>y</mi>
</math>
</body>
</html>');
$this->assertEmpty($this->html5->getErrors());
$list = $dom->getElementsByTagName('math');
$this->assertNotEmpty($list->length);
$list = $dom->getElementsByTagName('div');
$this->assertNotEmpty($list->length);
$div = $list->item(0);
$this->assertEquals('http://example.com', $div->getAttribute('definitionurl'));
$this->assertFalse($div->hasAttribute('definitionURL'));
$list = $dom->getElementsByTagName('csymbol');
$csymbol = $list->item(0);
$this->assertEquals('http://www.example.com/mathops/multiops.html#plusminus', $csymbol->getAttribute('definitionURL'));
$this->assertFalse($csymbol->hasAttribute('definitionurl'));
$html = $this->html5->saveHTML($dom);
$this->assertRegExp('|<csymbol definitionURL="http://www.example.com/mathops/multiops.html#plusminus">|', $html);
$this->assertRegExp('|<mi>y</mi>|', $html);
}
public function testUnknownElements()
{
// The : should not have special handling accourding to section 2.9 of the
// spec. This is differenant than XML. Since we don't know these elements
// they are handled as normal elements. Note, to do this is really
// an invalid example and you should not embed prefixed xml in html5.
$dom = $this->html5->loadHTMLFragment(
'<f:rug>
<f:name>Big rectangle thing</f:name>
<f:width>40</f:width>
<f:length>80</f:length>
</f:rug>
<sarcasm>um, yeah</sarcasm>');
$this->assertEmpty($this->html5->getErrors());
$markup = $this->html5->saveHTML($dom);
$this->assertRegExp('|<f:name>Big rectangle thing</f:name>|', $markup);
$this->assertRegExp('|<sarcasm>um, yeah</sarcasm>|', $markup);
}
public function testElements()
{
// Should have content.
$res = $this->cycle('<div>FOO</div>');
$this->assertRegExp('|<div>FOO</div>|', $res);
// Should be empty
$res = $this->cycle('<span></span>');
$this->assertRegExp('|<span></span>|', $res);
// Should have content.
$res = $this->cycleFragment('<div>FOO</div>');
$this->assertRegExp('|<div>FOO</div>|', $res);
// Should be empty
$res = $this->cycleFragment('<span></span>');
$this->assertRegExp('|<span></span>|', $res);
// Elements with dashes and underscores
$res = $this->cycleFragment('<sp-an></sp-an>');
$this->assertRegExp('|<sp-an></sp-an>|', $res);
$res = $this->cycleFragment('<sp_an></sp_an>');
$this->assertRegExp('|<sp_an></sp_an>|', $res);
// Should have no closing tag.
$res = $this->cycle('<hr>');
$this->assertRegExp('|<hr></body>|', $res);
}
public function testAttributes()
{
$res = $this->cycle('<use xlink:href="#svg-track" xmlns:xlink="http://www.w3.org/1999/xlink"></use>');
$this->assertContains('<use xlink:href="#svg-track" xmlns:xlink="http://www.w3.org/1999/xlink"></use>', $res);
$res = $this->cycle('<div attr="val">FOO</div>');
$this->assertRegExp('|<div attr="val">FOO</div>|', $res);
// XXX: Note that spec does NOT require attrs in the same order.
$res = $this->cycle('<div attr="val" class="even">FOO</div>');
$this->assertRegExp('|<div attr="val" class="even">FOO</div>|', $res);
$res = $this->cycle('<div xmlns:foo="http://example.com">FOO</div>');
$this->assertRegExp('|<div xmlns:foo="http://example.com">FOO</div>|', $res);
$res = $this->cycleFragment('<div attr="val">FOO</div>');
$this->assertRegExp('|<div attr="val">FOO</div>|', $res);
// XXX: Note that spec does NOT require attrs in the same order.
$res = $this->cycleFragment('<div attr="val" class="even">FOO</div>');
$this->assertRegExp('|<div attr="val" class="even">FOO</div>|', $res);
$res = $this->cycleFragment('<div xmlns:foo="http://example.com">FOO</div>');
$this->assertRegExp('|<div xmlns:foo="http://example.com">FOO</div>|', $res);
}
public function testPCData()
{
$res = $this->cycle('<a>This is a test.</a>');
$this->assertRegExp('|This is a test.|', $res);
$res = $this->cycleFragment('<a>This is a test.</a>');
$this->assertRegExp('|This is a test.|', $res);
$res = $this->cycle('This
is
a
test.');
// Check that newlines are there, but don't count spaces.
$this->assertRegExp('|This\n\s*is\n\s*a\n\s*test.|', $res);
$res = $this->cycleFragment('This
is
a
test.');
// Check that newlines are there, but don't count spaces.
$this->assertRegExp('|This\n\s*is\n\s*a\n\s*test.|', $res);
$res = $this->cycle('<a>This <em>is</em> a test.</a>');
$this->assertRegExp('|This <em>is</em> a test.|', $res);
$res = $this->cycleFragment('<a>This <em>is</em> a test.</a>');
$this->assertRegExp('|This <em>is</em> a test.|', $res);
}
public function testUnescaped()
{
$res = $this->cycle('<script>2 < 1</script>');
$this->assertRegExp('|2 < 1|', $res);
$res = $this->cycle('<style>div>div>div</style>');
$this->assertRegExp('|div>div>div|', $res);
$res = $this->cycleFragment('<script>2 < 1</script>');
$this->assertRegExp('|2 < 1|', $res);
$res = $this->cycleFragment('<style>div>div>div</style>');
$this->assertRegExp('|div>div>div|', $res);
}
public function testEntities()
{
$res = $this->cycle('<a>Apples &amp; bananas.</a>');
$this->assertRegExp('|Apples &amp; bananas.|', $res);
$res = $this->cycleFragment('<a>Apples &amp; bananas.</a>');
$this->assertRegExp('|Apples &amp; bananas.|', $res);
$res = $this->cycleFragment('<p>R&D</p>');
$this->assertRegExp('|R&amp;D|', $res);
}
public function testCaseSensitiveTags()
{
$dom = $this->html5->loadHTML(
'<html><body><Button color="red">Error</Button></body></html>',
array(
'xmlNamespaces' => true,
)
);
$out = $this->html5->saveHTML($dom);
$this->assertRegExp('|<html><body><Button color="red">Error</Button></body></html>|', $out);
}
public function testComment()
{
$res = $this->cycle('a<!-- This is a test. -->b');
$this->assertRegExp('|<!-- This is a test. -->|', $res);
$res = $this->cycleFragment('a<!-- This is a test. -->b');
$this->assertRegExp('|<!-- This is a test. -->|', $res);
}
public function testCDATA()
{
$res = $this->cycle('a<![CDATA[ This <is> a test. ]]>b');
$this->assertRegExp('|<!\[CDATA\[ This <is> a test\. \]\]>|', $res);
$res = $this->cycleFragment('a<![CDATA[ This <is> a test. ]]>b');
$this->assertRegExp('|<!\[CDATA\[ This <is> a test\. \]\]>|', $res);
}
}

View file

@ -1,44 +0,0 @@
<?php
/**
* @file
* Test the Scanner. This requires the InputStream tests are all good.
*/
namespace Masterminds\HTML5\Tests\Parser;
use Masterminds\HTML5\Parser\CharacterReference;
class CharacterReferenceTest extends \Masterminds\HTML5\Tests\TestCase
{
public function testLookupName()
{
$this->assertEquals('&', CharacterReference::lookupName('amp'));
$this->assertEquals('<', CharacterReference::lookupName('lt'));
$this->assertEquals('>', CharacterReference::lookupName('gt'));
$this->assertEquals('"', CharacterReference::lookupName('quot'));
$this->assertEquals('∌', CharacterReference::lookupName('NotReverseElement'));
$this->assertNull(CharacterReference::lookupName('StinkyCheese'));
}
public function testLookupHex()
{
$this->assertEquals('<', CharacterReference::lookupHex('3c'));
$this->assertEquals('<', CharacterReference::lookupHex('003c'));
$this->assertEquals('&', CharacterReference::lookupHex('26'));
$this->assertEquals('}', CharacterReference::lookupHex('7d'));
$this->assertEquals('Σ', CharacterReference::lookupHex('3A3'));
$this->assertEquals('Σ', CharacterReference::lookupHex('03A3'));
$this->assertEquals('Σ', CharacterReference::lookupHex('3a3'));
$this->assertEquals('Σ', CharacterReference::lookupHex('03a3'));
}
public function testLookupDecimal()
{
$this->assertEquals('&', CharacterReference::lookupDecimal(38));
$this->assertEquals('&', CharacterReference::lookupDecimal('38'));
$this->assertEquals('<', CharacterReference::lookupDecimal(60));
$this->assertEquals('Σ', CharacterReference::lookupDecimal(931));
$this->assertEquals('Σ', CharacterReference::lookupDecimal('0931'));
}
}

View file

@ -1,743 +0,0 @@
<?php
/**
* @file
* Test the Tree Builder.
*/
namespace Masterminds\HTML5\Tests\Parser;
use Masterminds\HTML5\Parser\Scanner;
use Masterminds\HTML5\Parser\Tokenizer;
use Masterminds\HTML5\Parser\DOMTreeBuilder;
/**
* These tests are functional, not necessarily unit tests.
*/
class DOMTreeBuilderTest extends \Masterminds\HTML5\Tests\TestCase
{
protected $errors = array();
/**
* Convenience function for parsing.
*/
protected function parse($string, array $options = array())
{
$treeBuilder = new DOMTreeBuilder(false, $options);
$scanner = new Scanner($string);
$parser = new Tokenizer($scanner, $treeBuilder);
$parser->parse();
$this->errors = $treeBuilder->getErrors();
return $treeBuilder->document();
}
/**
* Utility function for parsing a fragment of HTML5.
*/
protected function parseFragment($string)
{
$treeBuilder = new DOMTreeBuilder(true);
$scanner = new Scanner($string);
$parser = new Tokenizer($scanner, $treeBuilder);
$parser->parse();
$this->errors = $treeBuilder->getErrors();
return $treeBuilder->fragment();
}
public function testDocument()
{
$html = '<!DOCTYPE html><html></html>';
$doc = $this->parse($html);
$this->assertEquals('UTF-8', $doc->encoding);
$this->assertInstanceOf('\DOMDocument', $doc);
$this->assertEquals('html', $doc->documentElement->tagName);
$this->assertEquals('http://www.w3.org/1999/xhtml', $doc->documentElement->namespaceURI);
}
public function testBareAmpersand()
{
$html = "<!doctype html>
<html>
<body>
<img src='a&b' />
<img src='a&=' />
<img src='a&=c' />
<img src='a&=9' />
</body>
</html>";
$doc = $this->parse($html);
$this->assertEmpty($this->errors);
$this->assertXmlStringEqualsXmlString('
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"><body>
<img src="a&amp;b"/>
<img src="a&amp;="/>
<img src="a&amp;=c"/>
<img src="a&amp;=9"/>
</body>
</html>', $doc->saveXML());
}
public function testBareAmpersandNotAllowedInAttributes()
{
$html = "<!doctype html>
<html>
<body>
<img src='a&' />
<img src='a&+' />
</body>
</html>";
$doc = $this->parse($html);
$this->assertCount(2, $this->errors);
$this->assertXmlStringEqualsXmlString('
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"><body>
<img src="a&amp;"/>
<img src="a&amp;+"/>
</body>
</html>', $doc->saveXML());
}
public function testBareAmpersandNotAllowedInBody()
{
$html = '<!doctype html>
<html>
<body>
a&b
a&=
a&=c
a&=9
a&+
a& -- valid
</body>
</html>';
$doc = $this->parse($html);
$this->assertCount(5, $this->errors);
$this->assertXmlStringEqualsXmlString('
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"><body>
a&amp;b
a&amp;=
a&amp;=c
a&amp;=9
a&amp;+
a&amp; -- valid
</body>
</html>', $doc->saveXML());
}
public function testEntityAtEndOfFile()
{
$fragment = $this->parseFragment('&#');
$this->assertInstanceOf('DOMDocumentFragment', $fragment);
$this->assertSame('&#', $fragment->textContent);
$this->assertEquals('Line 1, Col 2: Expected &#DEC; &#HEX;, got EOF', $this->errors[0]);
}
public function testStrangeCapitalization()
{
$html = '<!doctype html>
<html>
<head>
<Title>Hello, world!</TitlE>
</head>
<body>TheBody<script>foo</script></body>
</html>';
$doc = $this->parse($html);
$this->assertInstanceOf('\DOMDocument', $doc);
$this->assertEquals('html', $doc->documentElement->tagName);
$xpath = new \DOMXPath($doc);
$xpath->registerNamespace('x', 'http://www.w3.org/1999/xhtml');
$this->assertEquals('Hello, world!', $xpath->query('//x:title')->item(0)->nodeValue);
$this->assertEquals('foo', $xpath->query('//x:script')->item(0)->nodeValue);
}
public function testDocumentWithDisabledNamespaces()
{
$html = '<!DOCTYPE html><html></html>';
$doc = $this->parse($html, array('disable_html_ns' => true));
$this->assertInstanceOf('\DOMDocument', $doc);
$this->assertEquals('html', $doc->documentElement->tagName);
$this->assertNull($doc->documentElement->namespaceURI);
}
public function testDocumentWithATargetDocument()
{
$targetDom = new \DOMDocument();
$html = '<!DOCTYPE html><html></html>';
$doc = $this->parse($html, array('target_document' => $targetDom));
$this->assertInstanceOf('\DOMDocument', $doc);
$this->assertSame($doc, $targetDom);
$this->assertEquals('html', $doc->documentElement->tagName);
}
public function testDocumentFakeAttrAbsence()
{
$html = '<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><body>foo</body></html>';
$doc = $this->parse($html, array('xmlNamespaces' => true));
$xp = new \DOMXPath($doc);
$this->assertEquals(0, $xp->query('//@html5-php-fake-id-attribute')->length);
}
public function testFragment()
{
$html = '<div>test</div><span>test2</span>';
$doc = $this->parseFragment($html);
$this->assertInstanceOf('\DOMDocumentFragment', $doc);
$this->assertTrue($doc->hasChildNodes());
$this->assertEquals('div', $doc->childNodes->item(0)->tagName);
$this->assertEquals('test', $doc->childNodes->item(0)->textContent);
$this->assertEquals('span', $doc->childNodes->item(1)->tagName);
$this->assertEquals('test2', $doc->childNodes->item(1)->textContent);
}
public function testElements()
{
$html = '<!DOCTYPE html><html><head><title></title></head><body></body></html>';
$doc = $this->parse($html);
$root = $doc->documentElement;
$this->assertEquals('html', $root->tagName);
$this->assertEquals('html', $root->localName);
$this->assertEquals('html', $root->nodeName);
$this->assertEquals(2, $root->childNodes->length);
$kids = $root->childNodes;
$this->assertEquals('head', $kids->item(0)->tagName);
$this->assertEquals('body', $kids->item(1)->tagName);
$head = $kids->item(0);
$this->assertEquals(1, $head->childNodes->length);
$this->assertEquals('title', $head->childNodes->item(0)->tagName);
}
public function testImplicitNamespaces()
{
$dom = $this->parse('<!DOCTYPE html><html><body><a xlink:href="bar">foo</a></body></html>');
$a = $dom->getElementsByTagName('a')->item(0);
$attr = $a->getAttributeNode('xlink:href');
$this->assertEquals('http://www.w3.org/1999/xlink', $attr->namespaceURI);
$dom = $this->parse('<!DOCTYPE html><html><body><a xml:base="bar">foo</a></body></html>');
$a = $dom->getElementsByTagName('a')->item(0);
$attr = $a->getAttributeNode('xml:base');
$this->assertEquals('http://www.w3.org/XML/1998/namespace', $attr->namespaceURI);
}
public function testCustomImplicitNamespaces()
{
$dom = $this->parse('<!DOCTYPE html><html><body><a t:href="bar">foo</a></body></html>', array(
'implicitNamespaces' => array(
't' => 'http://www.example.com',
),
));
$a = $dom->getElementsByTagName('a')->item(0);
$attr = $a->getAttributeNode('t:href');
$this->assertEquals('http://www.example.com', $attr->namespaceURI);
$dom = $this->parse('<!DOCTYPE html><html><body><t:a>foo</t:a></body></html>', array(
'implicitNamespaces' => array(
't' => 'http://www.example.com',
),
));
$list = $dom->getElementsByTagNameNS('http://www.example.com', 'a');
$this->assertEquals(1, $list->length);
}
public function testXmlNamespaces()
{
$dom = $this->parse(
'<!DOCTYPE html><html>
<t:body xmlns:t="http://www.example.com">
<a t:href="bar">foo</a>
</body>
<div>foo</div>
</html>', array(
'xmlNamespaces' => true,
));
$a = $dom->getElementsByTagName('a')->item(0);
$attr = $a->getAttributeNode('t:href');
$this->assertEquals('http://www.example.com', $attr->namespaceURI);
$list = $dom->getElementsByTagNameNS('http://www.example.com', 'body');
$this->assertEquals(1, $list->length);
}
public function testXmlNamespaceNesting()
{
$dom = $this->parse(
'<!DOCTYPE html><html>
<body xmlns:x="http://www.prefixed.com" id="body">
<a id="bar1" xmlns="http://www.prefixed.com/bar1">
<b id="bar4" xmlns="http://www.prefixed.com/bar4"><x:prefixed id="prefixed"/></b>
</a>
<svg id="svg"></svg>
<c id="bar2" xmlns="http://www.prefixed.com/bar2"></c>
<div id="div"></div>
<d id="bar3"></d>
<xn:d xmlns:xn="http://www.prefixed.com/xn" xmlns="http://www.prefixed.com/bar5_x" id="bar5"><x id="bar5_x"/></xn:d>
</body>
</html>', array(
'xmlNamespaces' => true,
));
$this->assertEmpty($this->errors);
$div = $dom->getElementById('div');
$this->assertEquals('http://www.w3.org/1999/xhtml', $div->namespaceURI);
$body = $dom->getElementById('body');
$this->assertEquals('http://www.w3.org/1999/xhtml', $body->namespaceURI);
$bar1 = $dom->getElementById('bar1');
$this->assertEquals('http://www.prefixed.com/bar1', $bar1->namespaceURI);
$bar2 = $dom->getElementById('bar2');
$this->assertEquals('http://www.prefixed.com/bar2', $bar2->namespaceURI);
$bar3 = $dom->getElementById('bar3');
$this->assertEquals('http://www.w3.org/1999/xhtml', $bar3->namespaceURI);
$bar4 = $dom->getElementById('bar4');
$this->assertEquals('http://www.prefixed.com/bar4', $bar4->namespaceURI);
$svg = $dom->getElementById('svg');
$this->assertEquals('http://www.w3.org/2000/svg', $svg->namespaceURI);
$prefixed = $dom->getElementById('prefixed');
$this->assertEquals('http://www.prefixed.com', $prefixed->namespaceURI);
$prefixed = $dom->getElementById('bar5');
$this->assertEquals('http://www.prefixed.com/xn', $prefixed->namespaceURI);
$prefixed = $dom->getElementById('bar5_x');
$this->assertEquals('http://www.prefixed.com/bar5_x', $prefixed->namespaceURI);
}
public function testMoveNonInlineElements()
{
$doc = $this->parse('<p>line1<br/><hr/>line2</p>');
$this->assertEquals('<html xmlns="http://www.w3.org/1999/xhtml"><p>line1<br/></p><hr/>line2</html>', $doc->saveXML($doc->documentElement), 'Move non-inline elements outside of inline containers.');
$doc = $this->parse('<p>line1<div>line2</div></p>');
$this->assertEquals('<html xmlns="http://www.w3.org/1999/xhtml"><p>line1</p><div>line2</div></html>', $doc->saveXML($doc->documentElement), 'Move non-inline elements outside of inline containers.');
}
public function testAttributes()
{
$html = "<!DOCTYPE html>
<html>
<head><title></title></head>
<body id='a' class='b c'></body>
</html>";
$doc = $this->parse($html);
$root = $doc->documentElement;
$body = $root->GetElementsByTagName('body')->item(0);
$this->assertEquals('body', $body->tagName);
$this->assertTrue($body->hasAttributes());
$this->assertEquals('a', $body->getAttribute('id'));
$this->assertEquals('b c', $body->getAttribute('class'));
$body2 = $doc->getElementById('a');
$this->assertEquals('body', $body2->tagName);
$this->assertEquals('a', $body2->getAttribute('id'));
}
public function testSVGAttributes()
{
$html = "<!DOCTYPE html>
<html><body>
<svg width='150' viewbox='2'>
<rect textlength='2'/>
<animatecolor>foo</animatecolor>
</svg>
</body></html>";
$doc = $this->parse($html);
$root = $doc->documentElement;
$svg = $root->getElementsByTagName('svg')->item(0);
$this->assertTrue($svg->hasAttribute('viewBox'));
$rect = $root->getElementsByTagName('rect')->item(0);
$this->assertTrue($rect->hasAttribute('textLength'));
$ac = $root->getElementsByTagName('animateColor');
$this->assertEquals(1, $ac->length);
}
public function testMathMLAttribute()
{
$html = '<!doctype html>
<html lang="en">
<body>
<math>
<mi>x</mi>
<csymbol definitionurl="http://www.example.com/mathops/multiops.html#plusminus">
<mo>&PlusMinus;</mo>
</csymbol>
<mi>y</mi>
</math>
</body>
</html>';
$doc = $this->parse($html);
$root = $doc->documentElement;
$csymbol = $root->getElementsByTagName('csymbol')->item(0);
$this->assertTrue($csymbol->hasAttribute('definitionURL'));
}
public function testMissingHtmlTag()
{
$html = '<!DOCTYPE html><title>test</title>';
$doc = $this->parse($html);
$this->assertEquals('html', $doc->documentElement->tagName);
$this->assertEquals('title', $doc->documentElement->childNodes->item(0)->tagName);
}
public function testComment()
{
$html = '<html><!--Hello World.--></html>';
$doc = $this->parse($html);
$comment = $doc->documentElement->childNodes->item(0);
$this->assertEquals(XML_COMMENT_NODE, $comment->nodeType);
$this->assertEquals('Hello World.', $comment->data);
$html = '<!--Hello World.--><html></html>';
$doc = $this->parse($html);
$comment = $doc->childNodes->item(1);
$this->assertEquals(XML_COMMENT_NODE, $comment->nodeType);
$this->assertEquals('Hello World.', $comment->data);
$comment = $doc->childNodes->item(2);
$this->assertEquals(XML_ELEMENT_NODE, $comment->nodeType);
$this->assertEquals('html', $comment->tagName);
}
public function testCDATA()
{
$html = '<!DOCTYPE html><html><math><![CDATA[test]]></math></html>';
$doc = $this->parse($html);
$wrapper = $doc->getElementsByTagName('math')->item(0);
$this->assertEquals(1, $wrapper->childNodes->length);
$cdata = $wrapper->childNodes->item(0);
$this->assertEquals(XML_CDATA_SECTION_NODE, $cdata->nodeType);
$this->assertEquals('test', $cdata->data);
}
public function testText()
{
$html = '<!DOCTYPE html><html><head></head><body><math>test</math></body></html>';
$doc = $this->parse($html);
$wrapper = $doc->getElementsByTagName('math')->item(0);
$this->assertEquals(1, $wrapper->childNodes->length);
$data = $wrapper->childNodes->item(0);
$this->assertEquals(XML_TEXT_NODE, $data->nodeType);
$this->assertEquals('test', $data->data);
// The DomTreeBuilder has special handling for text when in before head mode.
$html = '<!DOCTYPE html><html>
Foo<head></head><body></body></html>';
$doc = $this->parse($html);
$this->assertEquals('Line 0, Col 0: Unexpected text. Ignoring: Foo', $this->errors[0]);
$headElement = $doc->documentElement->firstChild;
$this->assertEquals('head', $headElement->tagName);
}
public function testParseErrors()
{
$html = '<!DOCTYPE html><html><math><![CDATA[test';
$doc = $this->parse($html);
// We're JUST testing that we can access errors. Actual testing of
// error messages happen in the Tokenizer's tests.
$this->assertGreaterThan(0, count($this->errors));
$this->assertTrue(is_string($this->errors[0]));
}
public function testProcessingInstruction()
{
// Test the simple case, which is where PIs are inserted into the DOM.
$doc = $this->parse('<!DOCTYPE html><html><?foo bar?>');
$this->assertEquals(1, $doc->documentElement->childNodes->length);
$pi = $doc->documentElement->firstChild;
$this->assertInstanceOf('\DOMProcessingInstruction', $pi);
$this->assertEquals('foo', $pi->nodeName);
$this->assertEquals('bar', $pi->data);
// Leading xml PIs should be ignored.
$doc = $this->parse('<?xml version="1.0"?><!DOCTYPE html><html><head></head></html>');
$this->assertEquals(2, $doc->childNodes->length);
$this->assertInstanceOf('\DOMDocumentType', $doc->childNodes->item(0));
$this->assertInstanceOf('\DOMElement', $doc->childNodes->item(1));
}
public function testAutocloseP()
{
$html = '<!DOCTYPE html><html><body><p><figure></body></html>';
$doc = $this->parse($html);
$p = $doc->getElementsByTagName('p')->item(0);
$this->assertEquals(0, $p->childNodes->length);
$this->assertEquals('figure', $p->nextSibling->tagName);
}
public function testAutocloseLI()
{
$html = '<!doctype html>
<html lang="en">
<body>
<ul><li>Foo<li>Bar<li>Baz</ul>
</body>
</html>';
$doc = $this->parse($html);
$length = $doc->getElementsByTagName('ul')->item(0)->childNodes->length;
$this->assertEquals(3, $length);
}
public function testMathML()
{
$html = '<!doctype html>
<html lang="en">
<body>
<math xmlns="http://www.w3.org/1998/Math/MathML">
<mi>x</mi>
<csymbol definitionurl="http://www.example.com/mathops/multiops.html#plusminus">
<mo>&PlusMinus;</mo>
</csymbol>
<mi>y</mi>
</math>
</body>
</html>';
$doc = $this->parse($html);
$math = $doc->getElementsByTagName('math')->item(0);
$this->assertEquals('math', $math->tagName);
$this->assertEquals('math', $math->nodeName);
$this->assertEquals('math', $math->localName);
$this->assertEquals('http://www.w3.org/1998/Math/MathML', $math->namespaceURI);
}
public function testSVG()
{
$html = '<!doctype html>
<html lang="en">
<body>
<svg width="150" height="100" viewBox="0 0 3 2" xmlns="http://www.w3.org/2000/svg">
<rect width="1" height="2" x="2" fill="#d2232c" />
<text font-family="Verdana" font-size="32">
<textpath xlink:href="#Foo">
Test Text.
</textPath>
</text>
</svg>
</body>
</html>';
$doc = $this->parse($html);
$svg = $doc->getElementsByTagName('svg')->item(0);
$this->assertEquals('svg', $svg->tagName);
$this->assertEquals('svg', $svg->nodeName);
$this->assertEquals('svg', $svg->localName);
$this->assertEquals('http://www.w3.org/2000/svg', $svg->namespaceURI);
$textPath = $doc->getElementsByTagName('textPath')->item(0);
$this->assertEquals('textPath', $textPath->tagName);
}
public function testNoScript()
{
$html = '<!DOCTYPE html><html><head><noscript>No JS</noscript></head></html>';
$doc = $this->parse($html);
$this->assertEmpty($this->errors);
$noscript = $doc->getElementsByTagName('noscript')->item(0);
$this->assertEquals('noscript', $noscript->tagName);
$html = '<!DOCTYPE html><html><body><noscript><p>No JS</p></noscript></body></html>';
$doc = $this->parse($html);
$this->assertEmpty($this->errors);
$p = $doc->getElementsByTagName('p')->item(0);
$this->assertEquals('p', $p->tagName);
}
/**
* Regression for issue #13.
*/
public function testRegressionHTMLNoBody()
{
$html = '<!DOCTYPE html><html><span id="test">Test</span></html>';
$doc = $this->parse($html);
$span = $doc->getElementById('test');
$this->assertEmpty($this->errors);
$this->assertEquals('span', $span->tagName);
$this->assertEquals('Test', $span->textContent);
}
public function testInstructionProcessor()
{
$string = '<!DOCTYPE html><html><?foo bar ?></html>';
$treeBuilder = new DOMTreeBuilder();
$is = new InstructionProcessorMock();
$treeBuilder->setInstructionProcessor($is);
$scanner = new Scanner($string);
$parser = new Tokenizer($scanner, $treeBuilder);
$parser->parse();
$dom = $treeBuilder->document();
$div = $dom->getElementsByTagName('div')->item(0);
$this->assertEquals(1, $is->count);
$this->assertEquals('foo', $is->name);
$this->assertEquals('bar ', $is->data);
$this->assertEquals('div', $div->tagName);
$this->assertEquals('foo', $div->textContent);
}
public function testSelectGroupedOptions()
{
$html = <<<EOM
<!DOCTYPE html>
<html>
<head>
<title>testSelectGroupedOptions</title>
</head>
<body>
<select>
<optgroup id="first" label="first">
<option value="foo">foo</option>
<option value="bar">bar</option>
<option value="baz">baz</option>
</optgroup>
<optgroup id="second" label="second">
<option value="lorem">lorem</option>
<option value="ipsum">ipsum</option>
</optgroup>
</select>
</body>
</html>
EOM;
$dom = $this->parse($html);
$this->assertSame(3, $dom->getElementById('first')->getElementsByTagName('option')->length);
$this->assertSame(2, $dom->getElementById('second')->getElementsByTagName('option')->length);
}
public function testVoidTag()
{
$html = <<<EOM
<!DOCTYPE html>
<html>
<head>
<title>testVoidTag</title>
<meta>
<meta>
</head>
<body></body>
</html>
EOM;
$dom = $this->parse($html);
$this->assertSame(2, $dom->getElementsByTagName('meta')->length);
$this->assertSame(0, $dom->getElementsByTagName('meta')->item(0)->childNodes->length);
$this->assertSame(0, $dom->getElementsByTagName('meta')->item(1)->childNodes->length);
}
public function testIgnoreSelfClosingTag()
{
$html = <<<EOM
<!DOCTYPE html>
<html>
<head>
<title>testIllegalSelfClosingTag</title>
</head>
<body>
<div /><span>Hello, World!</span></div>
</body>
</html>
EOM;
$dom = $this->parse($html);
$this->assertSame(1, $dom->getElementsByTagName('div')->item(0)->childNodes->length);
}
public function testIAudioInParagraph()
{
$html = <<<EOM
<!DOCTYPE html>
<html>
<head>
<title>testIllegalSelfClosingTag</title>
</head>
<body>
<p>
<audio preload="none" controls="controls">
<source src="https://example.com/test.mp3" type="audio/mpeg" />
Your browser does not support the audio element.
</audio>
</p>
</body>
</html>>
</html>
EOM;
$dom = $this->parse($html);
$audio = $dom->getElementsByTagName('audio')->item(0);
$this->assertSame('p', $audio->parentNode->nodeName);
$this->assertSame(3, $audio->childNodes->length);
}
public function testClosingBr()
{
$html = <<<EOM
<!DOCTYPE html>
<html>
<head>
<title>testClosingBr</title>
</head>
<body>
<p>
This line ends with a normal line break <br class="attribute-should-be-retained">
This line ends with a line break marked up as a closing tag </br class="attribute-should-be-discarded">
</p>
</body>
</html>>
</html>
EOM;
$dom = $this->parse($html);
$this->assertSame(2, $dom->getElementsByTagName('br')->length);
$this->assertSame(1, $dom->getElementsByTagName('br')->item(0)->attributes->length);
$this->assertSame(0, $dom->getElementsByTagName('br')->item(1)->attributes->length);
}
}

View file

@ -1,116 +0,0 @@
<?php
namespace Masterminds\HTML5\Tests\Parser;
use Masterminds\HTML5\Elements;
use Masterminds\HTML5\Parser\EventHandler;
/**
* This testing class gathers events from a parser and builds a stack of events.
* It is useful for checking the output of a tokenizer.
*
* IMPORTANT:
*
* The startTag event also kicks the parser into TEXT_RAW when it encounters
* script or pre tags. This is to match the behavior required by the HTML5 spec,
* which says that the tree builder must tell the tokenizer when to switch states.
*/
class EventStack implements EventHandler
{
protected $stack;
public function __construct()
{
$this->stack = array();
}
/**
* Get the event stack.
*/
public function events()
{
return $this->stack;
}
public function depth()
{
return count($this->stack);
}
public function get($index)
{
return $this->stack[$index];
}
protected function store($event, $data = null)
{
$this->stack[] = array(
'name' => $event,
'data' => $data,
);
}
public function doctype($name, $type = 0, $id = null, $quirks = false)
{
$args = array(
$name,
$type,
$id,
$quirks,
);
$this->store('doctype', $args);
}
public function startTag($name, $attributes = array(), $selfClosing = false)
{
$args = func_get_args();
$this->store('startTag', $args);
if ('pre' == $name || 'script' == $name) {
return Elements::TEXT_RAW;
}
}
public function endTag($name)
{
$this->store('endTag', array(
$name,
));
}
public function comment($cdata)
{
$this->store('comment', array(
$cdata,
));
}
public function cdata($data)
{
$this->store('cdata', func_get_args());
}
public function text($cdata)
{
// fprintf(STDOUT, "Received TEXT event with: " . $cdata);
$this->store('text', array(
$cdata,
));
}
public function eof()
{
$this->store('eof');
}
public function parseError($msg, $line, $col)
{
// throw new EventStackParseError(sprintf("%s (line %d, col %d)", $msg, $line, $col));
// $this->store(sprintf("%s (line %d, col %d)", $msg, $line, $col));
$this->store('error', func_get_args());
}
public function processingInstruction($name, $data = null)
{
$this->store('pi', func_get_args());
}
}

View file

@ -1,7 +0,0 @@
<?php
namespace Masterminds\HTML5\Tests\Parser;
class EventStackError extends \Exception
{
}

View file

@ -1,26 +0,0 @@
<?php
namespace Masterminds\HTML5\Tests\Parser;
class InstructionProcessorMock implements \Masterminds\HTML5\InstructionProcessor
{
public $name = null;
public $data = null;
public $count = 0;
public function process(\DOMElement $element, $name, $data)
{
$this->name = $name;
$this->data = $data;
++$this->count;
$div = $element->ownerDocument->createElement('div');
$div->nodeValue = 'foo';
$element->appendChild($div);
return $div;
}
}

View file

@ -1,184 +0,0 @@
<?php
/**
* @file
* Test the Scanner. This requires the InputStream tests are all good.
*/
namespace Masterminds\HTML5\Tests\Parser;
use Masterminds\HTML5\Parser\StringInputStream;
use Masterminds\HTML5\Parser\Scanner;
class ScannerTest extends \Masterminds\HTML5\Tests\TestCase
{
/**
* A canary test to make sure the basics are setup and working.
*/
public function testConstructDeprecated()
{
$is = new StringInputStream('abc');
$s = new Scanner($is);
$this->assertInstanceOf('\Masterminds\HTML5\Parser\Scanner', $s);
}
public function testConstruct()
{
$this->assertInstanceOf('\Masterminds\HTML5\Parser\Scanner', new Scanner('abc'));
}
public function testNextDeprecated()
{
$s = new Scanner(new StringInputStream('abc'));
$this->assertEquals('b', $s->next());
$this->assertEquals('c', $s->next());
}
public function testNext()
{
$s = new Scanner('abc');
$this->assertEquals('b', $s->next());
$this->assertEquals('c', $s->next());
}
public function testPosition()
{
$s = new Scanner('abc');
$this->assertEquals(0, $s->position());
$s->next();
$this->assertEquals(1, $s->position());
}
public function testPeek()
{
$s = new Scanner('abc');
$this->assertEquals('b', $s->peek());
$s->next();
$this->assertEquals('c', $s->peek());
}
public function testCurrent()
{
$s = new Scanner('abc');
// Before scanning the string begins the current is empty.
$this->assertEquals('a', $s->current());
$c = $s->next();
$this->assertEquals('b', $s->current());
// Test movement through the string.
$c = $s->next();
$this->assertEquals('c', $s->current());
}
public function testUnconsume()
{
$s = new Scanner('abcdefghijklmnopqrst');
// Get initial position.
$s->next();
$start = $s->position();
// Move forward a bunch of positions.
$amount = 7;
for ($i = 0; $i < $amount; ++$i) {
$s->next();
}
// Roll back the amount we moved forward.
$s->unconsume($amount);
$this->assertEquals($start, $s->position());
}
public function testGetHex()
{
$s = new Scanner('ab13ck45DE*');
$this->assertEquals('ab13c', $s->getHex());
$s->next();
$this->assertEquals('45DE', $s->getHex());
}
public function testGetAsciiAlpha()
{
$s = new Scanner('abcdef1%mnop*');
$this->assertEquals('abcdef', $s->getAsciiAlpha());
// Move past the 1% to scan the next group of text.
$s->next();
$s->next();
$this->assertEquals('mnop', $s->getAsciiAlpha());
}
public function testGetAsciiAlphaNum()
{
$s = new Scanner('abcdef1ghpo#mn94op');
$this->assertEquals('abcdef1ghpo', $s->getAsciiAlphaNum());
// Move past the # to scan the next group of text.
$s->next();
$this->assertEquals('mn94op', $s->getAsciiAlphaNum());
}
public function testGetNumeric()
{
$s = new Scanner('1784a 45 9867 #');
$this->assertEquals('1784', $s->getNumeric());
// Move past the 'a ' to scan the next group of text.
$s->next();
$s->next();
$this->assertEquals('45', $s->getNumeric());
}
public function testCurrentLine()
{
$s = new Scanner("1784a\n45\n9867 #\nThis is a test.");
$this->assertEquals(1, $s->currentLine());
// Move to the next line.
$s->getAsciiAlphaNum();
$s->next();
$this->assertEquals(2, $s->currentLine());
}
public function testColumnOffset()
{
$s = new Scanner("1784a a\n45 9867 #\nThis is a test.");
// Move the pointer to the space.
$s->getAsciiAlphaNum();
$this->assertEquals(5, $s->columnOffset());
// We move the pointer ahead. There must be a better way to do this.
$s->next();
$s->next();
$s->next();
$s->next();
$s->next();
$s->next();
$this->assertEquals(3, $s->columnOffset());
}
public function testRemainingChars()
{
$string = "\n45\n9867 #\nThis is a test.";
$s = new Scanner("1784a\n45\n9867 #\nThis is a test.");
$s->getAsciiAlphaNum();
$this->assertEquals($string, $s->remainingChars());
}
}

View file

@ -1,981 +0,0 @@
<?php
namespace Masterminds\HTML5\Tests\Parser;
use Masterminds\HTML5\Parser\UTF8Utils;
use Masterminds\HTML5\Parser\Scanner;
use Masterminds\HTML5\Parser\Tokenizer;
class TokenizerTest extends \Masterminds\HTML5\Tests\TestCase
{
// ================================================================
// Additional assertions.
// ================================================================
/**
* Tests that an event matches both the event type and the expected value.
*
* @param string $type
* Expected event type
* @param string $expects
* The value expected in $event['data'][0]
*/
public function assertEventEquals($type, $expects, $event)
{
$this->assertEquals($type, $event['name'], "Event $type for " . print_r($event, true));
if (is_array($expects)) {
$this->assertEquals($expects, $event['data'], "Event $type should equal " . print_r($expects, true) . ': ' . print_r($event, true));
} else {
$d = (is_array($event['data']) ? $event['data'][0] : null);
$this->assertEquals($expects, $d, "Event $type should equal $expects: " . print_r($event, true));
}
}
/**
* Assert that a given event is 'error'.
*/
public function assertEventError($event)
{
$this->assertEquals('error', $event['name'], 'Expected error for event: ' . print_r($event, true));
}
/**
* Asserts that all of the tests are good.
*
* This loops through a map of tests/expectations and runs a few assertions on each test.
*
* Checks:
* - depth (if depth is > 0)
* - event name
* - matches on event 0.
*/
protected function isAllGood($name, $depth, $tests, $debug = false)
{
foreach ($tests as $try => $expects) {
if ($debug) {
fprintf(STDOUT, "%s expects %s\n", $try, print_r($expects, true));
}
$e = $this->parse($try);
if ($depth > 0) {
$this->assertEquals($depth, $e->depth(), "Expected depth $depth for test $try." . print_r($e, true));
}
$this->assertEventEquals($name, $expects, $e->get(0));
}
}
// ================================================================
// Utility functions.
// ================================================================
public function testParse()
{
list($tok, $events) = $this->createTokenizer('');
$tok->parse();
$e1 = $events->get(0);
$this->assertEquals(1, $events->Depth());
$this->assertEquals('eof', $e1['name']);
}
public function testWhitespace()
{
$spaces = ' ';
list($tok, $events) = $this->createTokenizer($spaces);
$tok->parse();
$this->assertEquals(2, $events->depth());
$e1 = $events->get(0);
$this->assertEquals('text', $e1['name']);
$this->assertEquals($spaces, $e1['data'][0]);
}
public function testCharacterReference()
{
$good = array(
'&amp;' => '&',
'&#x0003c;' => '<',
'&#38;' => '&',
'&' => '&',
);
$this->isAllGood('text', 2, $good);
// Test with broken charref
$str = '&foo';
$events = $this->parse($str);
$e1 = $events->get(0);
$this->assertEquals('error', $e1['name']);
$str = '&#xfoo';
$events = $this->parse($str);
$e1 = $events->get(0);
$this->assertEquals('error', $e1['name']);
$str = '&#foo';
$events = $this->parse($str);
$e1 = $events->get(0);
$this->assertEquals('error', $e1['name']);
// FIXME: Once the text processor is done, need to verify that the
// tokens are transformed correctly into text.
}
public function testBogusComment()
{
$bogus = array(
'</+this is a bogus comment. +>',
'<!+this is a bogus comment. !>',
'<!D OCTYPE foo bar>',
'<!DOCTYEP foo bar>',
'<![CADATA[ TEST ]]>',
'<![CDATA Hello ]]>',
'<![CDATA[ Hello [[>',
'<!CDATA[[ test ]]>',
'<![CDATA[',
'<![CDATA[hellooooo hello',
'<? Hello World ?>',
'<? Hello World',
);
foreach ($bogus as $str) {
$events = $this->parse($str);
$this->assertEventError($events->get(0));
$this->assertEventEquals('comment', $str, $events->get(1));
}
}
public function testEndTag()
{
$succeed = array(
'</a>' => 'a',
'</test>' => 'test',
'</test
>' => 'test',
'</thisIsTheTagThatDoesntEndItJustGoesOnAndOnMyFriend>' => 'thisisthetagthatdoesntenditjustgoesonandonmyfriend',
// See 8.2.4.10, which requires this and does not say error.
'</a<b>' => 'a<b',
);
$this->isAllGood('endTag', 2, $succeed);
// Recoverable failures
$fail = array(
'</a class="monkey">' => 'a',
'</a <b>' => 'a',
'</a <b <c>' => 'a',
'</a is the loneliest letter>' => 'a',
'</a' => 'a',
);
foreach ($fail as $test => $result) {
$events = $this->parse($test);
$this->assertEquals(3, $events->depth());
// Should have triggered an error.
$this->assertEventError($events->get(0));
// Should have tried to parse anyway.
$this->assertEventEquals('endTag', $result, $events->get(1));
}
// BogoComments
$comments = array(
'</>' => '</>',
'</ >' => '</ >',
'</ a>' => '</ a>',
);
foreach ($comments as $test => $result) {
$events = $this->parse($test);
$this->assertEquals(3, $events->depth());
// Should have triggered an error.
$this->assertEventError($events->get(0));
// Should have tried to parse anyway.
$this->assertEventEquals('comment', $result, $events->get(1));
}
}
public function testComment()
{
$good = array(
'<!--easy-->' => 'easy',
'<!-- 1 > 0 -->' => ' 1 > 0 ',
'<!-- --$i -->' => ' --$i ',
'<!----$i-->' => '--$i',
"<!--\nHello World.\na-->" => "\nHello World.\na",
'<!-- <!-- -->' => ' <!-- ',
);
foreach ($good as $test => $expected) {
$events = $this->parse($test);
$this->assertEventEquals('comment', $expected, $events->get(0));
}
$fail = array(
'<!-->' => '',
'<!--Hello' => 'Hello',
"<!--\0Hello" => UTF8Utils::FFFD . 'Hello',
'<!--' => '',
);
foreach ($fail as $test => $expected) {
$events = $this->parse($test);
$this->assertEquals(3, $events->depth());
$this->assertEventError($events->get(0));
$this->assertEventEquals('comment', $expected, $events->get(1));
}
}
public function testCDATASection()
{
$good = array(
'<![CDATA[ This is a test. ]]>' => ' This is a test. ',
'<![CDATA[CDATA]]>' => 'CDATA',
'<![CDATA[ ]] > ]]>' => ' ]] > ',
'<![CDATA[ ]]>' => ' ',
);
$this->isAllGood('cdata', 2, $good);
}
public function testDoctype()
{
$good = array(
'<!DOCTYPE html>' => array(
'html',
0,
null,
false,
),
'<!doctype html>' => array(
'html',
0,
null,
false,
),
'<!DocType html>' => array(
'html',
0,
null,
false,
),
"<!DOCTYPE\nhtml>" => array(
'html',
0,
null,
false,
),
"<!DOCTYPE\fhtml>" => array(
'html',
0,
null,
false,
),
'<!DOCTYPE html PUBLIC "foo bar">' => array(
'html',
EventStack::DOCTYPE_PUBLIC,
'foo bar',
false,
),
"<!DOCTYPE html PUBLIC 'foo bar'>" => array(
'html',
EventStack::DOCTYPE_PUBLIC,
'foo bar',
false,
),
'<!DOCTYPE html PUBLIC "foo bar" >' => array(
'html',
EventStack::DOCTYPE_PUBLIC,
'foo bar',
false,
),
"<!DOCTYPE html \nPUBLIC\n'foo bar'>" => array(
'html',
EventStack::DOCTYPE_PUBLIC,
'foo bar',
false,
),
'<!DOCTYPE html SYSTEM "foo bar">' => array(
'html',
EventStack::DOCTYPE_SYSTEM,
'foo bar',
false,
),
"<!DOCTYPE html SYSTEM 'foo bar'>" => array(
'html',
EventStack::DOCTYPE_SYSTEM,
'foo bar',
false,
),
'<!DOCTYPE html SYSTEM "foo/bar" >' => array(
'html',
EventStack::DOCTYPE_SYSTEM,
'foo/bar',
false,
),
"<!DOCTYPE html \nSYSTEM\n'foo bar'>" => array(
'html',
EventStack::DOCTYPE_SYSTEM,
'foo bar',
false,
),
);
$this->isAllGood('doctype', 2, $good);
$bad = array(
'<!DOCTYPE>' => array(
null,
EventStack::DOCTYPE_NONE,
null,
true,
),
'<!DOCTYPE >' => array(
null,
EventStack::DOCTYPE_NONE,
null,
true,
),
'<!DOCTYPE foo' => array(
'foo',
EventStack::DOCTYPE_NONE,
null,
true,
),
'<!DOCTYPE foo PUB' => array(
'foo',
EventStack::DOCTYPE_NONE,
null,
true,
),
'<!DOCTYPE foo PUB>' => array(
'foo',
EventStack::DOCTYPE_NONE,
null,
true,
),
'<!DOCTYPE foo PUB "Looks good">' => array(
'foo',
EventStack::DOCTYPE_NONE,
null,
true,
),
'<!DOCTYPE foo SYSTME "Looks good"' => array(
'foo',
EventStack::DOCTYPE_NONE,
null,
true,
),
// Can't tell whether these are ids or ID types, since the context is chopped.
'<!DOCTYPE foo PUBLIC' => array(
'foo',
EventStack::DOCTYPE_NONE,
null,
true,
),
'<!DOCTYPE foo PUBLIC>' => array(
'foo',
EventStack::DOCTYPE_NONE,
null,
true,
),
'<!DOCTYPE foo SYSTEM' => array(
'foo',
EventStack::DOCTYPE_NONE,
null,
true,
),
'<!DOCTYPE foo SYSTEM>' => array(
'foo',
EventStack::DOCTYPE_NONE,
null,
true,
),
'<!DOCTYPE html SYSTEM "foo bar"' => array(
'html',
EventStack::DOCTYPE_SYSTEM,
'foo bar',
true,
),
'<!DOCTYPE html SYSTEM "foo bar" more stuff>' => array(
'html',
EventStack::DOCTYPE_SYSTEM,
'foo bar',
true,
),
);
foreach ($bad as $test => $expects) {
$events = $this->parse($test);
// fprintf(STDOUT, $test . PHP_EOL);
$this->assertEquals(3, $events->depth(), "Counting events for '$test': " . print_r($events, true));
$this->assertEventError($events->get(0));
$this->assertEventEquals('doctype', $expects, $events->get(1));
}
}
public function testProcessorInstruction()
{
$good = array(
'<?hph ?>' => 'hph',
'<?hph echo "Hello World"; ?>' => array(
'hph',
'echo "Hello World"; ',
),
"<?hph \necho 'Hello World';\n?>" => array(
'hph',
"echo 'Hello World';\n",
),
);
$this->isAllGood('pi', 2, $good);
}
/**
* This tests just simple tags.
*/
public function testSimpleTags()
{
$open = array(
'<foo>' => 'foo',
'<FOO>' => 'foo',
'<fOO>' => 'foo',
'<foo >' => 'foo',
"<foo\n\n\n\n>" => 'foo',
'<foo:bar>' => 'foo:bar',
);
$this->isAllGood('startTag', 2, $open);
$selfClose = array(
'<foo/>' => 'foo',
'<FOO/>' => 'foo',
'<foo />' => 'foo',
"<foo\n\n\n\n/>" => 'foo',
'<foo:bar/>' => 'foo:bar',
);
foreach ($selfClose as $test => $expects) {
$events = $this->parse($test);
$this->assertEquals(2, $events->depth(), "Counting events for '$test'" . print_r($events, true));
$this->assertEventEquals('startTag', $expects, $events->get(0));
$event = $events->get(0);
$this->assertTrue($event['data'][2]);
}
$bad = array(
'<foo' => 'foo',
'<foo ' => 'foo',
'<foo/' => 'foo',
'<foo /' => 'foo',
);
foreach ($bad as $test => $expects) {
$events = $this->parse($test);
$this->assertEquals(3, $events->depth(), "Counting events for '$test': " . print_r($events, true));
$this->assertEventError($events->get(0));
$this->assertEventEquals('startTag', $expects, $events->get(1));
}
}
public function testTagsWithAttributeAndMissingName()
{
$cases = array(
'<id="top_featured">' => 'id',
'<color="white">' => 'color',
"<class='neaktivni_stranka'>" => 'class',
'<bgcolor="white">' => 'bgcolor',
'<class="nom">' => 'class',
);
foreach ($cases as $html => $expected) {
$events = $this->parse($html);
$this->assertEventError($events->get(0));
$this->assertEventError($events->get(1));
$this->assertEventError($events->get(2));
$this->assertEventEquals('startTag', $expected, $events->get(3));
$this->assertEventEquals('eof', null, $events->get(4));
}
}
public function testTagNotClosedAfterTagName()
{
$cases = array(
'<noscript<img>' => array(
'noscript',
'img',
),
'<center<a>' => array(
'center',
'a',
),
'<br<br>' => array(
'br',
'br',
),
);
foreach ($cases as $html => $expected) {
$events = $this->parse($html);
$this->assertEventError($events->get(0));
$this->assertEventEquals('startTag', $expected[0], $events->get(1));
$this->assertEventEquals('startTag', $expected[1], $events->get(2));
$this->assertEventEquals('eof', null, $events->get(3));
}
$events = $this->parse('<span<>02</span>');
$this->assertEventError($events->get(0));
$this->assertEventEquals('startTag', 'span', $events->get(1));
$this->assertEventError($events->get(2));
$this->assertEventEquals('text', '>02', $events->get(3));
$this->assertEventEquals('endTag', 'span', $events->get(4));
$this->assertEventEquals('eof', null, $events->get(5));
$events = $this->parse('<p</p>');
$this->assertEventError($events->get(0));
$this->assertEventEquals('startTag', 'p', $events->get(1));
$this->assertEventEquals('endTag', 'p', $events->get(2));
$this->assertEventEquals('eof', null, $events->get(3));
$events = $this->parse('<strong><WordPress</strong>');
$this->assertEventEquals('startTag', 'strong', $events->get(0));
$this->assertEventError($events->get(1));
$this->assertEventEquals('startTag', 'wordpress', $events->get(2));
$this->assertEventEquals('endTag', 'strong', $events->get(3));
$this->assertEventEquals('eof', null, $events->get(4));
$events = $this->parse('<src=<a>');
$this->assertEventError($events->get(0));
$this->assertEventError($events->get(1));
$this->assertEventError($events->get(2));
$this->assertEventEquals('startTag', 'src', $events->get(3));
$this->assertEventEquals('startTag', 'a', $events->get(4));
$this->assertEventEquals('eof', null, $events->get(5));
$events = $this->parse('<br...<a>');
$this->assertEventError($events->get(0));
$this->assertEventEquals('startTag', 'br', $events->get(1));
$this->assertEventEquals('eof', null, $events->get(2));
}
public function testIllegalTagNames()
{
$cases = array(
'<li">' => 'li',
'<p">' => 'p',
'<b&nbsp; >' => 'b',
'<static*all>' => 'static',
'<h*0720/>' => 'h',
'<st*ATTRIBUTE />' => 'st',
);
foreach ($cases as $html => $expected) {
$events = $this->parse($html);
$this->assertEventError($events->get(0));
$this->assertEventEquals('startTag', $expected, $events->get(1));
}
}
/**
* @depends testCharacterReference
*/
public function testTagAttributes()
{
// Opening tags.
$good = array(
'<foo bar="baz">' => array(
'foo',
array(
'bar' => 'baz',
),
false,
),
'<foo bar=" baz ">' => array(
'foo',
array(
'bar' => ' baz ',
),
false,
),
"<foo bar=\"\nbaz\n\">" => array(
'foo',
array(
'bar' => "\nbaz\n",
),
false,
),
"<foo bar='baz'>" => array(
'foo',
array(
'bar' => 'baz',
),
false,
),
'<foo bar="A full sentence.">' => array(
'foo',
array(
'bar' => 'A full sentence.',
),
false,
),
"<foo a='1' b=\"2\">" => array(
'foo',
array(
'a' => '1',
'b' => '2',
),
false,
),
"<foo ns:bar='baz'>" => array(
'foo',
array(
'ns:bar' => 'baz',
),
false,
),
"<foo a='blue&red'>" => array(
'foo',
array(
'a' => 'blue&red',
),
false,
),
"<foo a='blue&amp;red'>" => array(
'foo',
array(
'a' => 'blue&red',
),
false,
),
"<foo a='blue&&amp;&red'>" => array(
'foo',
array(
'a' => 'blue&&&red',
),
false,
),
"<foo a='blue&&amp;red'>" => array(
'foo',
array(
'a' => 'blue&&red',
),
false,
),
"<foo\nbar='baz'\n>" => array(
'foo',
array(
'bar' => 'baz',
),
false,
),
'<doe a deer>' => array(
'doe',
array(
'a' => null,
'deer' => null,
),
false,
),
'<foo bar=baz>' => array(
'foo',
array(
'bar' => 'baz',
),
false,
),
// Updated for 8.1.2.3
'<foo bar = "baz" >' => array(
'foo',
array(
'bar' => 'baz',
),
false,
),
// The spec allows an unquoted value '/'. This will not be a closing
// tag.
'<foo bar=/>' => array(
'foo',
array(
'bar' => '/',
),
false,
),
'<foo bar=baz/>' => array(
'foo',
array(
'bar' => 'baz/',
),
false,
),
);
$this->isAllGood('startTag', 2, $good);
// Self-closing tags.
$withEnd = array(
'<foo bar="baz"/>' => array(
'foo',
array(
'bar' => 'baz',
),
true,
),
'<foo BAR="baz"/>' => array(
'foo',
array(
'bar' => 'baz',
),
true,
),
'<foo BAR="BAZ"/>' => array(
'foo',
array(
'bar' => 'BAZ',
),
true,
),
"<foo a='1' b=\"2\" c=3 d/>" => array(
'foo',
array(
'a' => '1',
'b' => '2',
'c' => '3',
'd' => null,
),
true,
),
);
$this->isAllGood('startTag', 2, $withEnd);
// Cause a parse error.
$bad = array(
// This will emit an entity lookup failure for &+dark.
"<foo a='blue&+dark'>" => array(
'foo',
array(
'a' => 'blue&+dark',
),
false,
),
'<foo bar=>' => array(
'foo',
array(
'bar' => null,
),
false,
),
'<foo bar="oh' => array(
'foo',
array(
'bar' => 'oh',
),
false,
),
'<foo bar=oh">' => array(
'foo',
array(
'bar' => 'oh"',
),
false,
),
// these attributes are ignored because of current implementation
// of method "DOMElement::setAttribute"
// see issue #23: https://github.com/Masterminds/html5-php/issues/23
'<foo b"="baz">' => array(
'foo',
array(),
false,
),
'<foo 2abc="baz">' => array(
'foo',
array(),
false,
),
'<foo ?="baz">' => array(
'foo',
array(),
false,
),
'<foo foo?bar="baz">' => array(
'foo',
array(),
false,
),
)
;
foreach ($bad as $test => $expects) {
$events = $this->parse($test);
$this->assertEquals(3, $events->depth(), "Counting events for '$test': " . print_r($events, true));
$this->assertEventError($events->get(0));
$this->assertEventEquals('startTag', $expects, $events->get(1));
}
// Cause multiple parse errors.
$reallyBad = array(
'<foo ="bar">' => array(
'foo',
array(
'=' => null,
'"bar"' => null,
),
false,
),
'<foo////>' => array(
'foo',
array(),
true,
),
// character "&" in unquoted attribute shouldn't cause an infinite loop
'<foo bar=index.php?str=1&amp;id=29>' => array(
'foo',
array(
'bar' => 'index.php?str=1&id=29',
),
false,
),
);
foreach ($reallyBad as $test => $expects) {
$events = $this->parse($test);
// fprintf(STDOUT, $test . print_r($events, true));
$this->assertEventError($events->get(0));
$this->assertEventError($events->get(1));
// $this->assertEventEquals('startTag', $expects, $events->get(1));
}
// Regression: Malformed elements should be detected.
// '<foo baz="1" <bar></foo>' => array('foo', array('baz' => '1'), false),
$events = $this->parse('<foo baz="1" <bar></foo>');
$this->assertEventError($events->get(0));
$this->assertEventEquals('startTag', array(
'foo',
array(
'baz' => '1',
),
false,
), $events->get(1));
$this->assertEventEquals('startTag', array(
'bar',
array(),
false,
), $events->get(2));
$this->assertEventEquals('endTag', array(
'foo',
), $events->get(3));
}
public function testRawText()
{
$good = array(
'<script>abcd efg hijk lmnop</script> ' => 'abcd efg hijk lmnop',
'<script><not/><the/><tag></script>' => '<not/><the/><tag>',
'<script><<<<<<<<</script>' => '<<<<<<<<',
'<script>hello</script</script>' => 'hello</script',
"<script>\nhello</script\n</script>" => "\nhello</script\n",
'<script>&amp;</script>' => '&amp;',
'<script><!--not a comment--></script>' => '<!--not a comment-->',
'<script><![CDATA[not a comment]]></script>' => '<![CDATA[not a comment]]>',
);
foreach ($good as $test => $expects) {
$events = $this->parse($test);
$this->assertEventEquals('startTag', 'script', $events->get(0));
$this->assertEventEquals('text', $expects, $events->get(1));
$this->assertEventEquals('endTag', 'script', $events->get(2));
}
$bad = array(
'<script>&amp;</script' => '&amp;</script',
'<script>Hello world' => 'Hello world',
);
foreach ($bad as $test => $expects) {
$events = $this->parse($test);
$this->assertEquals(4, $events->depth(), "Counting events for '$test': " . print_r($events, true));
$this->assertEventEquals('startTag', 'script', $events->get(0));
$this->assertEventError($events->get(1));
$this->assertEventEquals('text', $expects, $events->get(2));
}
// Testing case sensitivity
$events = $this->parse('<TITLE>a test</TITLE>');
$this->assertEventEquals('startTag', 'title', $events->get(0));
$this->assertEventEquals('text', 'a test', $events->get(1));
$this->assertEventEquals('endTag', 'title', $events->get(2));
// Testing end tags with whitespaces
$events = $this->parse('<title>Whitespaces are tasty</title >');
$this->assertEventEquals('startTag', 'title', $events->get(0));
$this->assertEventEquals('text', 'Whitespaces are tasty', $events->get(1));
$this->assertEventEquals('endTag', 'title', $events->get(2));
}
public function testRcdata()
{
list($tok, $events) = $this->createTokenizer('<title>&#x27;<!-- not a comment --></TITLE>');
$tok->setTextMode(\Masterminds\HTML5\Elements::TEXT_RCDATA, 'title');
$tok->parse();
$this->assertEventEquals('text', "'<!-- not a comment -->", $events->get(1));
}
public function testText()
{
$events = $this->parse('a<br>b');
$this->assertEquals(4, $events->depth(), 'Events: ' . print_r($events, true));
$this->assertEventEquals('text', 'a', $events->get(0));
$this->assertEventEquals('startTag', 'br', $events->get(1));
$this->assertEventEquals('text', 'b', $events->get(2));
$events = $this->parse('<a>Test</a>');
$this->assertEquals(4, $events->depth(), 'Events: ' . print_r($events, true));
$this->assertEventEquals('startTag', 'a', $events->get(0));
$this->assertEventEquals('text', 'Test', $events->get(1));
$this->assertEventEquals('endTag', 'a', $events->get(2));
$events = $this->parse('<p>0</p><p>1</p>');
$this->assertEquals(7, $events->depth(), 'Events: ' . print_r($events, true));
$this->assertEventEquals('startTag', 'p', $events->get(0));
$this->assertEventEquals('text', '0', $events->get(1));
$this->assertEventEquals('endTag', 'p', $events->get(2));
$this->assertEventEquals('startTag', 'p', $events->get(3));
$this->assertEventEquals('text', '1', $events->get(4));
$this->assertEventEquals('endTag', 'p', $events->get(5));
$events = $this->parse('a<![CDATA[test]]>b');
$this->assertEquals(4, $events->depth(), 'Events: ' . print_r($events, true));
$this->assertEventEquals('text', 'a', $events->get(0));
$this->assertEventEquals('cdata', 'test', $events->get(1));
$this->assertEventEquals('text', 'b', $events->get(2));
$events = $this->parse('a<!--test-->b');
$this->assertEquals(4, $events->depth(), 'Events: ' . print_r($events, true));
$this->assertEventEquals('text', 'a', $events->get(0));
$this->assertEventEquals('comment', 'test', $events->get(1));
$this->assertEventEquals('text', 'b', $events->get(2));
$events = $this->parse('a&amp;b');
$this->assertEquals(2, $events->depth(), 'Events: ' . print_r($events, true));
$this->assertEventEquals('text', 'a&b', $events->get(0));
$events = $this->parse('a&sup2;b');
$this->assertEquals(2, $events->depth(), 'Events: ' . print_r($events, true));
$this->assertEventEquals('text', 'a²b', $events->get(0));
}
// ================================================================
// Utility functions.
// ================================================================
protected function createTokenizer($string, $debug = false)
{
$eventHandler = new EventStack();
$scanner = new Scanner($string);
$scanner->debug = $debug;
return array(
new Tokenizer($scanner, $eventHandler),
$eventHandler,
);
}
public function parse($string, $debug = false)
{
list($tok, $events) = $this->createTokenizer($string, $debug);
$tok->parse();
return $events;
}
}

View file

@ -1,118 +0,0 @@
<?php
/**
* @file
* Test the Tree Builder's special-case rules.
*/
namespace Masterminds\HTML5\Tests\Parser;
use Masterminds\HTML5\Parser\TreeBuildingRules;
use Masterminds\HTML5\Parser\Tokenizer;
use Masterminds\HTML5\Parser\Scanner;
use Masterminds\HTML5\Parser\DOMTreeBuilder;
/**
* These tests are functional, not necessarily unit tests.
*/
class TreeBuildingRulesTest extends \Masterminds\HTML5\Tests\TestCase
{
const HTML_STUB = '<!DOCTYPE html><html><head><title>test</title></head><body>%s</body></html>';
/**
* Convenience function for parsing.
*/
protected function parse($string)
{
$treeBuilder = new DOMTreeBuilder();
$scanner = new Scanner($string);
$parser = new Tokenizer($scanner, $treeBuilder);
$parser->parse();
return $treeBuilder->document();
}
/**
* Convenience function for parsing fragments.
*/
protected function parseFragment($string)
{
$events = new DOMTreeBuilder(true);
$scanner = new Scanner($string);
$parser = new Tokenizer($scanner, $events);
$parser->parse();
return $events->fragment();
}
public function testTDFragment()
{
$frag = $this->parseFragment('<td>This is a test of the HTML5 parser</td>');
$td = $frag->childNodes->item(0);
$this->assertEquals(1, $frag->childNodes->length);
$this->assertEquals('td', $td->tagName);
$this->assertEquals('This is a test of the HTML5 parser', $td->nodeValue);
}
public function testHasRules()
{
$doc = new \DOMDocument('1.0');
$engine = new TreeBuildingRules($doc);
$this->assertTrue($engine->hasRules('li'));
$this->assertFalse($engine->hasRules('imaginary'));
}
public function testHandleLI()
{
$html = sprintf(self::HTML_STUB, '<ul id="a"><li>test<li>test2</ul><a></a>');
$doc = $this->parse($html);
$list = $doc->getElementById('a');
$this->assertEquals(2, $list->childNodes->length);
foreach ($list->childNodes as $ele) {
$this->assertEquals('li', $ele->tagName);
}
}
public function testHandleDT()
{
$html = sprintf(self::HTML_STUB, '<dl id="a"><dt>Hello<dd>Hi</dl><a></a>');
$doc = $this->parse($html);
$list = $doc->getElementById('a');
$this->assertEquals(2, $list->childNodes->length);
$this->assertEquals('dt', $list->firstChild->tagName);
$this->assertEquals('dd', $list->lastChild->tagName);
}
public function testHandleOptionGroupAndOption()
{
$html = sprintf(self::HTML_STUB, '<optgroup id="foo" label="foo" ><option value="foo">bar</option></optgroup>');
$doc = $this->parse($html);
$list = $doc->getElementById('foo');
$this->assertEquals(1, $list->childNodes->length);
$option = $list->childNodes->item(0);
$this->assertEquals('option', $option->tagName);
}
public function testTable()
{
$html = sprintf(self::HTML_STUB, '<table><thead id="a"><th>foo<td>bar<td>baz');
$doc = $this->parse($html);
$list = $doc->getElementById('a');
$this->assertEquals(3, $list->childNodes->length);
$this->assertEquals('th', $list->firstChild->tagName);
$this->assertEquals('td', $list->lastChild->tagName);
}
}

View file

@ -1,28 +0,0 @@
<?php
namespace Masterminds\HTML5\Tests\Parser;
use Masterminds\HTML5\Parser\UTF8Utils;
class UTF8UtilsTest extends \Masterminds\HTML5\Tests\TestCase
{
public function testConvertToUTF8()
{
$out = UTF8Utils::convertToUTF8('éàa', 'ISO-8859-1');
$this->assertEquals('éàa', $out);
}
/**
* @todo add tests for invalid codepoints
*/
public function testCheckForIllegalCodepoints()
{
$smoke = 'Smoke test';
$err = UTF8Utils::checkForIllegalCodepoints($smoke);
$this->assertEmpty($err);
$data = "Foo Bar \0 Baz";
$errors = UTF8Utils::checkForIllegalCodepoints($data);
$this->assertContains('null-character', $errors);
}
}

View file

@ -1,652 +0,0 @@
<?php
namespace Masterminds\HTML5\Tests\Serializer;
use Masterminds\HTML5\Serializer\OutputRules;
use Masterminds\HTML5\Serializer\Traverser;
use Masterminds\HTML5;
class OutputRulesTest extends \Masterminds\HTML5\Tests\TestCase
{
protected $markup = '<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<p>This is a test.</p>
</body>
</html>';
/**
* @var HTML5
*/
protected $html5;
public function setUp()
{
$this->html5 = $this->getInstance();
}
/**
* Using reflection we make a protected method accessible for testing.
*
* @param string $name
* The name of the method on the Traverser class to test
*
* @return \ReflectionMethod for the specified method
*/
public function getProtectedMethod($name)
{
$class = new \ReflectionClass('\Masterminds\HTML5\Serializer\OutputRules');
$method = $class->getMethod($name);
$method->setAccessible(true);
return $method;
}
public function getTraverserProtectedProperty($name)
{
$class = new \ReflectionClass('\Masterminds\HTML5\Serializer\Traverser');
$property = $class->getProperty($name);
$property->setAccessible(true);
return $property;
}
public function getOutputRules($options = array())
{
$options = $options + $this->html5->getOptions();
$stream = fopen('php://temp', 'w');
$dom = $this->html5->loadHTML($this->markup);
$r = new OutputRules($stream, $options);
$t = new Traverser($dom, $stream, $r, $options);
return array(
$r,
$stream,
);
}
public function testDocument()
{
$dom = $this->html5->loadHTML('<!doctype html><html lang="en"><body>foo</body></html>');
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$r->document($dom);
$expected = '<!DOCTYPE html>' . PHP_EOL . '<html lang="en"><body>foo</body></html>' . PHP_EOL;
$this->assertEquals($expected, stream_get_contents($stream, -1, 0));
}
public function testEmptyDocument()
{
$dom = $this->html5->loadHTML('');
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$r->document($dom);
$expected = '<!DOCTYPE html>' . PHP_EOL;
$this->assertEquals($expected, stream_get_contents($stream, -1, 0));
}
public function testDoctype()
{
$dom = $this->html5->loadHTML('<!doctype html><html lang="en"><body>foo</body></html>');
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$m = $this->getProtectedMethod('doctype');
$m->invoke($r, 'foo');
$this->assertEquals('<!DOCTYPE html>' . PHP_EOL, stream_get_contents($stream, -1, 0));
}
public function testElement()
{
$dom = $this->html5->loadHTML(
'<!doctype html>
<html lang="en">
<body>
<div id="foo" class="bar baz">foo bar baz</div>
<svg width="150" height="100" viewBox="0 0 3 2">
<rect width="1" height="2" x="0" fill="#008d46" />
<rect width="1" height="2" x="1" fill="#ffffff" />
<rect width="1" height="2" x="2" fill="#d2232c" />
</svg>
</body>
</html>');
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$list = $dom->getElementsByTagName('div');
$r->element($list->item(0));
$this->assertEquals('<div id="foo" class="bar baz">foo bar baz</div>', stream_get_contents($stream, -1, 0));
}
public function testSerializeWithNamespaces()
{
$this->html5 = $this->getInstance(array(
'xmlNamespaces' => true,
));
$source = '
<!DOCTYPE html>
<html><body id="body" xmlns:x="http://www.prefixed.com">
<a id="bar1" xmlns="http://www.prefixed.com/bar1">
<b id="bar4" xmlns="http://www.prefixed.com/bar4"><x:prefixed id="prefixed">xy</x:prefixed></b>
</a>
<svg id="svg">svg</svg>
<c id="bar2" xmlns="http://www.prefixed.com/bar2"></c>
<div id="div"></div>
<d id="bar3"></d>
<xn:d id="bar5" xmlns:xn="http://www.prefixed.com/xn" xmlns="http://www.prefixed.com/bar5_x"><x id="bar5_x">y</x></xn:d>
</body>
</html>';
$dom = $this->html5->loadHTML($source, array(
'xmlNamespaces' => true,
));
$this->assertFalse($this->html5->hasErrors(), print_r($this->html5->getErrors(), 1));
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$t->walk();
$rendered = stream_get_contents($stream, -1, 0);
$clear = function ($s) {
return trim(preg_replace('/[\s]+/', ' ', $s));
};
$this->assertEquals($clear($source), $clear($rendered));
}
public function testElementWithScript()
{
$dom = $this->html5->loadHTML(
'<!doctype html>
<html lang="en">
<head>
<script>
var $jQ = jQuery.noConflict();
// Use jQuery via $jQ(...)
$jQ(document).ready(function () {
$jQ("#mktFrmSubmit").wrap("<div class=\'buttonSubmit\'></div>");
$jQ(".buttonSubmit").prepend("<span></span>");
});
</script>
</head>
<body>
<div id="foo" class="bar baz">foo bar baz</div>
</body>
</html>');
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$script = $dom->getElementsByTagName('script');
$r->element($script->item(0));
$this->assertEquals(
'<script>
var $jQ = jQuery.noConflict();
// Use jQuery via $jQ(...)
$jQ(document).ready(function () {
$jQ("#mktFrmSubmit").wrap("<div class=\'buttonSubmit\'></div>");
$jQ(".buttonSubmit").prepend("<span></span>");
});
</script>', stream_get_contents($stream, -1, 0));
}
public function testElementWithStyle()
{
$dom = $this->html5->loadHTML(
'<!doctype html>
<html lang="en">
<head>
<style>
body > .bar {
display: none;
}
</style>
</head>
<body>
<div id="foo" class="bar baz">foo bar baz</div>
</body>
</html>');
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$style = $dom->getElementsByTagName('style');
$r->element($style->item(0));
$this->assertEquals('<style>
body > .bar {
display: none;
}
</style>', stream_get_contents($stream, -1, 0));
}
public function testOpenTag()
{
$dom = $this->html5->loadHTML('<!doctype html>
<html lang="en">
<body>
<div id="foo" class="bar baz">foo bar baz</div>
</body>
</html>');
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$list = $dom->getElementsByTagName('div');
$m = $this->getProtectedMethod('openTag');
$m->invoke($r, $list->item(0));
$this->assertEquals('<div id="foo" class="bar baz">', stream_get_contents($stream, -1, 0));
}
public function testCData()
{
$dom = $this->html5->loadHTML('<!doctype html>
<html lang="en">
<body>
<div><![CDATA[bar]]></div>
</body>
</html>');
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$list = $dom->getElementsByTagName('div');
$r->cdata($list->item(0)->childNodes->item(0));
$this->assertEquals('<![CDATA[bar]]>', stream_get_contents($stream, -1, 0));
$dom = $this->html5->loadHTML('<!doctype html>
<html lang="en">
<body>
<div id="foo"></div>
</body>
</html>');
$dom->getElementById('foo')->appendChild(new \DOMCdataSection(']]>Foo<[![CDATA test ]]>'));
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$list = $dom->getElementsByTagName('div');
$r->cdata($list->item(0)->childNodes->item(0));
$this->assertEquals('<![CDATA[]]]]><![CDATA[>Foo<[![CDATA test ]]]]><![CDATA[>]]>', stream_get_contents($stream, -1, 0));
}
public function testComment()
{
$dom = $this->html5->loadHTML('<!doctype html>
<html lang="en">
<body>
<div><!-- foo --></div>
</body>
</html>');
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$list = $dom->getElementsByTagName('div');
$r->comment($list->item(0)->childNodes->item(0));
$this->assertEquals('<!-- foo -->', stream_get_contents($stream, -1, 0));
$dom = $this->html5->loadHTML('<!doctype html>
<html lang="en">
<body>
<div id="foo"></div>
</body>
</html>');
$dom->getElementById('foo')->appendChild(new \DOMComment('<!-- --> --> Foo -->'));
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$list = $dom->getElementsByTagName('div');
$r->comment($list->item(0)->childNodes->item(0));
// Could not find more definitive guidelines on what this should be. Went with
// what the HTML5 spec says and what \DOMDocument::saveXML() produces.
$this->assertEquals('<!--<!-- --> --> Foo -->-->', stream_get_contents($stream, -1, 0));
}
public function testText()
{
$dom = $this->html5->loadHTML('<!doctype html>
<html lang="en">
<head>
<script>baz();</script>
</head>
</html>');
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$list = $dom->getElementsByTagName('script');
$r->text($list->item(0)->childNodes->item(0));
$this->assertEquals('baz();', stream_get_contents($stream, -1, 0));
$dom = $this->html5->loadHTML('<!doctype html>
<html lang="en">
<head id="foo"></head>
</html>');
$foo = $dom->getElementById('foo');
$foo->appendChild(new \DOMText('<script>alert("hi");</script>'));
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$r->text($foo->firstChild);
$this->assertEquals('&lt;script&gt;alert("hi");&lt;/script&gt;', stream_get_contents($stream, -1, 0));
}
public function testNl()
{
list($o, $s) = $this->getOutputRules();
$m = $this->getProtectedMethod('nl');
$m->invoke($o);
$this->assertEquals(PHP_EOL, stream_get_contents($s, -1, 0));
}
public function testWr()
{
list($o, $s) = $this->getOutputRules();
$m = $this->getProtectedMethod('wr');
$m->invoke($o, 'foo');
$this->assertEquals('foo', stream_get_contents($s, -1, 0));
}
public function getEncData()
{
return array(
array(
false,
'&\'<>"',
'&amp;\'&lt;&gt;"',
'&amp;&apos;&lt;&gt;&quot;',
),
array(
false,
'This + is. a < test',
'This + is. a &lt; test',
'This &plus; is&period; a &lt; test',
),
array(
false,
'.+#',
'.+#',
'&period;&plus;&num;',
),
array(
true,
'.+#\'',
'.+#\'',
'&period;&plus;&num;&apos;',
),
array(
true,
'&".<',
'&amp;&quot;.<',
'&amp;&quot;&period;&lt;',
),
array(
true,
'&\'<>"',
'&amp;\'<>&quot;',
'&amp;&apos;&lt;&gt;&quot;',
),
array(
true,
"\xc2\xa0\"'",
'&nbsp;&quot;\'',
'&nbsp;&quot;&apos;',
),
);
}
/**
* Test basic encoding of text.
*
* @dataProvider getEncData
*/
public function testEnc($isAttribute, $test, $expected, $expectedEncoded)
{
list($o, $s) = $this->getOutputRules();
$m = $this->getProtectedMethod('enc');
$this->assertEquals($expected, $m->invoke($o, $test, $isAttribute));
list($o, $s) = $this->getOutputRules(array(
'encode_entities' => true,
));
$m = $this->getProtectedMethod('enc');
$this->assertEquals($expectedEncoded, $m->invoke($o, $test, $isAttribute));
}
/**
* Test basic encoding of text.
*
* @dataProvider getEncData
*/
public function testEscape($isAttribute, $test, $expected, $expectedEncoded)
{
list($o, $s) = $this->getOutputRules();
$m = $this->getProtectedMethod('escape');
$this->assertEquals($expected, $m->invoke($o, $test, $isAttribute));
}
public function booleanAttributes()
{
return array(
array('<img alt="" ismap>'),
array('<img alt="">'),
array('<input type="radio" readonly>'),
array('<input type="radio" checked disabled>'),
array('<input type="checkbox" checked disabled>'),
array('<input type="radio" value="" checked disabled>'),
array('<div data-value=""></div>'),
array('<select disabled></select>'),
array('<div ng-app></div>'),
array('<script defer></script>'),
);
}
/**
* @dataProvider booleanAttributes
*/
public function testBooleanAttrs($html)
{
$dom = $this->html5->loadHTML('<!doctype html><html lang="en"><body>' . $html . '</body></html>');
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$node = $dom->getElementsByTagName('body')->item(0)->firstChild;
$m = $this->getProtectedMethod('attrs');
$m->invoke($r, $node);
$content = stream_get_contents($stream, -1, 0);
$html = preg_replace('~<[a-z]+(.*)></[a-z]+>~', '\1', $html);
$html = preg_replace('~<[a-z]+(.*)/?>~', '\1', $html);
$this->assertEquals($content, $html);
}
public function testAttrs()
{
$dom = $this->html5->loadHTML('<!doctype html>
<html lang="en">
<body>
<div id="foo" class="bar baz">foo bar baz</div>
</body>
</html>');
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$list = $dom->getElementsByTagName('div');
$m = $this->getProtectedMethod('attrs');
$m->invoke($r, $list->item(0));
$content = stream_get_contents($stream, -1, 0);
$this->assertEquals(' id="foo" class="bar baz"', $content);
}
public function testSvg()
{
$dom = $this->html5->loadHTML(
'<!doctype html>
<html lang="en">
<body>
<div id="foo" class="bar baz">foo bar baz</div>
<svg width="150" height="100" viewBox="0 0 3 2">
<rect width="1" height="2" x="0" fill="#008d46" />
<rect width="1" height="2" x="1" fill="#ffffff" />
<rect width="1" height="2" x="2" fill="#d2232c" />
<rect id="Bar" x="300" y="100" width="300" height="100" fill="rgb(255,255,0)">
<animate attributeName="x" attributeType="XML" begin="0s" dur="9s" fill="freeze" from="300" to="0" />
</rect>
</svg>
</body>
</html>');
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$list = $dom->getElementsByTagName('svg');
$r->element($list->item(0));
$contents = stream_get_contents($stream, -1, 0);
$this->assertRegExp('|<svg width="150" height="100" viewBox="0 0 3 2">|', $contents);
$this->assertRegExp('|<rect width="1" height="2" x="0" fill="#008d46" />|', $contents);
$this->assertRegExp('|<rect id="Bar" x="300" y="100" width="300" height="100" fill="rgb\(255,255,0\)">|', $contents);
}
public function testMath()
{
$dom = $this->html5->loadHTML(
'<!doctype html>
<html lang="en">
<body>
<div id="foo" class="bar baz">foo bar baz</div>
<math>
<mi>x</mi>
<csymbol definitionURL="http://www.example.com/mathops/multiops.html#plusminus">
<mo>&PlusMinus;</mo>
</csymbol>
<mi>y</mi>
</math>
</body>
</html>');
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$list = $dom->getElementsByTagName('math');
$r->element($list->item(0));
$content = stream_get_contents($stream, -1, 0);
$this->assertRegExp('|<math>|', $content);
$this->assertRegExp('|<csymbol definitionURL="http://www.example.com/mathops/multiops.html#plusminus">|', $content);
}
public function testProcessorInstruction()
{
$dom = $this->html5->loadHTMLFragment('<?foo bar ?>');
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$r->processorInstruction($dom->firstChild);
$content = stream_get_contents($stream, -1, 0);
$this->assertRegExp('|<\?foo bar \?>|', $content);
}
public function testAddressTag()
{
$dom = $this->html5->loadHTML(
'<!doctype html>
<html lang="en">
<body>
<address>
<a href="../People/Raggett/">Dave Raggett</a>,
<a href="../People/Arnaud/">Arnaud Le Hors</a>,
contact persons for the <a href="Activity">W3C HTML Activity</a>
</address>
</body>
</html>');
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$list = $dom->getElementsByTagName('address');
$r->element($list->item(0));
$contents = stream_get_contents($stream, -1, 0);
$this->assertRegExp('|<address>|', $contents);
$this->assertRegExp('|<a href="../People/Raggett/">Dave Raggett</a>,|', $contents);
$this->assertRegExp('|<a href="../People/Arnaud/">Arnaud Le Hors</a>,|', $contents);
$this->assertRegExp('|contact persons for the <a href="Activity">W3C HTML Activity</a>|', $contents);
$this->assertRegExp('|</address>|', $contents);
}
/**
* Ensure direct DOM manipulation doesn't break TEXT_RAW elements (iframe, script, etc...).
*/
public function testHandlingInvalidRawContent()
{
$dom = $this->html5->loadHTML(
'<!doctype html>
<html lang="en" id="base">
<body>
<script id="template" type="x-tmpl-mustache">
<h1>Hello!</h1>
</script>
</body>
</html>');
$badNode = $dom->createElement('p', 'Bar');
// modify the content of the TEXT_RAW element: <script id="template"> appending dom nodes
$styleElement = $dom->getElementById('template');
$styleElement->appendChild($badNode);
$contents = $this->html5->saveHTML($dom);
$this->assertTrue(false !== strpos($contents, '<script id="template" type="x-tmpl-mustache">
<h1>Hello!</h1>
<p>Bar</p></script>'));
}
}

View file

@ -1,136 +0,0 @@
<?php
namespace Masterminds\HTML5\Tests\Serializer;
use Masterminds\HTML5\Serializer\OutputRules;
use Masterminds\HTML5\Serializer\Traverser;
class TraverserTest extends \Masterminds\HTML5\Tests\TestCase
{
protected $markup = '<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<p>This is a test.</p>
</body>
</html>';
public function setUp()
{
$this->html5 = $this->getInstance();
}
/**
* Using reflection we make a protected method accessible for testing.
*
* @param string $name
* The name of the method on the Traverser class to test
*
* @return \ReflectionMethod \ReflectionMethod for the specified method
*/
public function getProtectedMethod($name)
{
$class = new \ReflectionClass('\Masterminds\HTML5\Serializer\Traverser');
$method = $class->getMethod($name);
$method->setAccessible(true);
return $method;
}
public function getTraverser()
{
$stream = fopen('php://temp', 'w');
$dom = $this->html5->loadHTML($this->markup);
$t = new Traverser($dom, $stream, $html5->getOptions());
// We return both the traverser and stream so we can pull from it.
return array(
$t,
$stream,
);
}
public function testConstruct()
{
// The traverser needs a place to write the output to. In our case we
// use a stream in temp space.
$stream = fopen('php://temp', 'w');
$html5 = $this->getInstance();
$r = new OutputRules($stream, $this->html5->getOptions());
$dom = $this->html5->loadHTML($this->markup);
$t = new Traverser($dom, $stream, $r, $html5->getOptions());
$this->assertInstanceOf('\Masterminds\HTML5\Serializer\Traverser', $t);
}
public function testFragmentDeprecated()
{
$html = '<span class="bar">foo</span><span></span><div>bar</div>';
$input = new \Masterminds\HTML5\Parser\StringInputStream($html);
$dom = $this->html5->parseFragment($input);
$this->assertInstanceOf('\DOMDocumentFragment', $dom);
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$t->walk();
$this->assertEquals($html, stream_get_contents($stream, -1, 0));
}
public function testFragment()
{
$html = '<span class="bar">foo</span><span></span><div>bar</div>';
$dom = $this->html5->parseFragment($html);
$this->assertInstanceOf('\DOMDocumentFragment', $dom);
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$t->walk();
$this->assertEquals($html, stream_get_contents($stream, -1, 0));
}
public function testProcessorInstructionDeprecated()
{
$html = '<?foo bar ?>';
$input = new \Masterminds\HTML5\Parser\StringInputStream($html);
$dom = $this->html5->parseFragment($input);
$this->assertInstanceOf('\DOMDocumentFragment', $dom);
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$t->walk();
$this->assertEquals($html, stream_get_contents($stream, -1, 0));
}
public function testProcessorInstruction()
{
$html = '<?foo bar ?>';
$dom = $this->html5->parseFragment($html);
$this->assertInstanceOf('\DOMDocumentFragment', $dom);
$stream = fopen('php://temp', 'w');
$r = new OutputRules($stream, $this->html5->getOptions());
$t = new Traverser($dom, $stream, $r, $this->html5->getOptions());
$t->walk();
$this->assertEquals($html, stream_get_contents($stream, -1, 0));
}
}

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