code:
<?php

declare(strict_types=1);

namespace Enqueue\Dsn;

class Dsn
{
    /**
     * @var string
     */
    private $scheme;

    /**
     * @var string
     */
    private $schemeProtocol;

    /**
     * @var string[]
     */
    private $schemeExtensions;

    /**
     * @var string|null
     */
    private $user;

    /**
     * @var string|null
     */
    private $password;

    /**
     * @var string|null
     */
    private $host;

    /**
     * @var int|null
     */
    private $port;

    /**
     * @var string|null
     */
    private $path;

    /**
     * @var string|null
     */
    private $queryString;

    /**
     * @var QueryBag
     */
    private $queryBag;

    public function __construct(
        string $scheme,
        string $schemeProtocol,
        array $schemeExtensions,
        ?string $user,
        ?string $password,
        ?string $host,
        ?int $port,
        ?string $path,
        ?string $queryString,
        array $query
    ) {
        $this->scheme = $scheme;
        $this->schemeProtocol = $schemeProtocol;
        $this->schemeExtensions = $schemeExtensions;
        $this->user = $user;
        $this->password = $password;
        $this->host = $host;
        $this->port = $port;
        $this->path = $path;
        $this->queryString = $queryString;
        $this->queryBag = new QueryBag($query);
    }

    public function getScheme(): string
    {
        return $this->scheme;
    }

    public function getSchemeProtocol(): string
    {
        return $this->schemeProtocol;
    }

    public function getSchemeExtensions(): array
    {
        return $this->schemeExtensions;
    }

    public function hasSchemeExtension(string $extension): bool
    {
        return in_array($extension, $this->schemeExtensions, true);
    }

    public function getUser(): ?string
    {
        return $this->user;
    }

    public function getPassword(): ?string
    {
        return $this->password;
    }

    public function getHost(): ?string
    {
        return $this->host;
    }

    public function getPort(): ?int
    {
        return $this->port;
    }

    public function getPath(): ?string
    {
        return $this->path;
    }

    public function getQueryString(): ?string
    {
        return $this->queryString;
    }

    public function getQueryBag(): QueryBag
    {
        return $this->queryBag;
    }

    public function getQuery(): array
    {
        return $this->queryBag->toArray();
    }

    public function getString(string $name, string $default = null): ?string
    {
        return $this->queryBag->getString($name, $default);
    }

    public function getDecimal(string $name, int $default = null): ?int
    {
        return $this->queryBag->getDecimal($name, $default);
    }

    public function getOctal(string $name, int $default = null): ?int
    {
        return $this->queryBag->getOctal($name, $default);
    }

    public function getFloat(string $name, float $default = null): ?float
    {
        return $this->queryBag->getFloat($name, $default);
    }

    public function getBool(string $name, bool $default = null): ?bool
    {
        return $this->queryBag->getBool($name, $default);
    }

    public function getArray(string $name, array $default = []): QueryBag
    {
        return $this->queryBag->getArray($name, $default);
    }

    public function toArray()
    {
        return [
            'scheme' => $this->scheme,
            'schemeProtocol' => $this->schemeProtocol,
            'schemeExtensions' => $this->schemeExtensions,
            'user' => $this->user,
            'password' => $this->password,
            'host' => $this->host,
            'port' => $this->port,
            'path' => $this->path,
            'queryString' => $this->queryString,
            'query' => $this->queryBag->toArray(),
        ];
    }

    public static function parseFirst(string $dsn): ?self
    {
        return self::parse($dsn)[0];
    }

    /**
     * @param string $dsn
     *
     * @return Dsn[]
     */
    public static function parse(string $dsn): array
    {
        if (false === strpos($dsn, ':')) {
            throw new \LogicException(sprintf('The DSN is invalid. It does not have scheme separator ":".'));
        }

        list($scheme, $dsnWithoutScheme) = explode(':', $dsn, 2);

        $scheme = strtolower($scheme);
        if (false == preg_match('/^[a-z\d+-.]*$/', $scheme)) {
            throw new \LogicException('The DSN is invalid. Scheme contains illegal symbols.');
        }

        $schemeParts = explode('+', $scheme);
        $schemeProtocol = $schemeParts[0];

        unset($schemeParts[0]);
        $schemeExtensions = array_values($schemeParts);

        $user = parse_url($dsn, PHP_URL_USER) ?: null;
        if (is_string($user)) {
            $user = rawurldecode($user);
        }

        $password = parse_url($dsn, PHP_URL_PASS) ?: null;
        if (is_string($password)) {
            $password = rawurldecode($password);
        }

        $path = parse_url($dsn, PHP_URL_PATH) ?: null;
        if ($path) {
            $path = rawurldecode($path);
        }

        $query = [];
        $queryString = parse_url($dsn, PHP_URL_QUERY) ?: null;
        if (is_string($queryString)) {
            $query = self::httpParseQuery($queryString, '&', PHP_QUERY_RFC3986);
        }
        $hostsPorts = '';
        if (0 === strpos($dsnWithoutScheme, '//')) {
            $dsnWithoutScheme = substr($dsnWithoutScheme, 2);
            $dsnWithoutUserPassword = explode('@', $dsnWithoutScheme, 2);
            $dsnWithoutUserPassword = 2 === count($dsnWithoutUserPassword) ?
                $dsnWithoutUserPassword[1] :
                $dsnWithoutUserPassword[0]
            ;

            list($hostsPorts) = explode('#', $dsnWithoutUserPassword, 2);
            list($hostsPorts) = explode('?', $hostsPorts, 2);
            list($hostsPorts) = explode('/', $hostsPorts, 2);
        }

        if (empty($hostsPorts)) {
            return [
                new self(
                    $scheme,
                    $schemeProtocol,
                    $schemeExtensions,
                    null,
                    null,
                    null,
                    null,
                    $path,
                    $queryString,
                    $query
                ),
            ];
        }

        $dsns = [];
        $hostParts = explode(',', $hostsPorts);
        foreach ($hostParts as $key => $hostPart) {
            unset($hostParts[$key]);

            $parts = explode(':', $hostPart, 2);
            $host = $parts[0];

            $port = null;
            if (isset($parts[1])) {
                $port = (int) $parts[1];
            }

            $dsns[] = new self(
                $scheme,
                $schemeProtocol,
                $schemeExtensions,
                $user,
                $password,
                $host,
                $port,
                $path,
                $queryString,
                $query
            );
        }

        return $dsns;
    }

    /**
     * based on http://php.net/manual/en/function.parse-str.php#119484 with some slight modifications.
     */
    private static function httpParseQuery(string $queryString, string $argSeparator = '&', int $decType = PHP_QUERY_RFC1738): array
    {
        $result = [];
        $parts = explode($argSeparator, $queryString);

        foreach ($parts as $part) {
            list($paramName, $paramValue) = explode('=', $part, 2);

            switch ($decType) {
                case PHP_QUERY_RFC3986:
                    $paramName = rawurldecode($paramName);
                    $paramValue = rawurldecode($paramValue);
                    break;
                case PHP_QUERY_RFC1738:
                default:
                    $paramName = urldecode($paramName);
                    $paramValue = urldecode($paramValue);
                    break;
            }

            if (preg_match_all('/\[([^\]]*)\]/m', $paramName, $matches)) {
                $paramName = substr($paramName, 0, strpos($paramName, '['));
                $keys = array_merge([$paramName], $matches[1]);
            } else {
                $keys = [$paramName];
            }

            $target = &$result;

            foreach ($keys as $index) {
                if ('' === $index) {
                    if (is_array($target)) {
                        $intKeys = array_filter(array_keys($target), 'is_int');
                        $index = count($intKeys) ? max($intKeys) + 1 : 0;
                    } else {
                        $target = [$target];
                        $index = 1;
                    }
                } elseif (isset($target[$index]) && !is_array($target[$index])) {
                    $target[$index] = [$target[$index]];
                }

                $target = &$target[$index];
            }

            if (is_array($target)) {
                $target[] = $paramValue;
            } else {
                $target = $paramValue;
            }
        }

        return $result;
    }
}

repo_name: spookie/gnu

license: mit


code:
<?php

namespace Enqueue\Dsn;

final class InvalidQueryParameterTypeException extends \LogicException
{
    public static function create(string $name, string $expectedType): self
    {
        return new static(sprintf('The query parameter "%s" has invalid type. It must be "%s"', $name, $expectedType));
    }
}


code:
<?php

declare(strict_types=1);

namespace Enqueue\Dsn;

class QueryBag
{
    /**
     * @var array
     */
    private $query;

    public function __construct(array $query)
    {
        $this->query = $query;
    }

    public function toArray(): array
    {
        return $this->query;
    }

    public function getString(string $name, string $default = null): ?string
    {
        return array_key_exists($name, $this->query) ? $this->query[$name] : $default;
    }

    public function getDecimal(string $name, int $default = null): ?int
    {
        $value = $this->getString($name);
        if (null === $value) {
            return $default;
        }

        if (false == preg_match('/^[\+\-]?[0-9]*$/', $value)) {
            throw InvalidQueryParameterTypeException::create($name, 'decimal');
        }

        return (int) $value;
    }

    public function getOctal(string $name, int $default = null): ?int
    {
        $value = $this->getString($name);
        if (null === $value) {
            return $default;
        }

        if (false == preg_match('/^0[\+\-]?[0-7]*$/', $value)) {
            throw InvalidQueryParameterTypeException::create($name, 'octal');
        }

        return intval($value, 8);
    }

    public function getFloat(string $name, float $default = null): ?float
    {
        $value = $this->getString($name);
        if (null === $value) {
            return $default;
        }

        if (false == is_numeric($value)) {
            throw InvalidQueryParameterTypeException::create($name, 'float');
        }

        return (float) $value;
    }

    public function getBool(string $name, bool $default = null): ?bool
    {
        $value = $this->getString($name);
        if (null === $value) {
            return $default;
        }

        if (in_array($value, ['', '0', 'false'], true)) {
            return false;
        }

        if (in_array($value, ['1', 'true'], true)) {
            return true;
        }

        throw InvalidQueryParameterTypeException::create($name, 'bool');
    }

    public function getArray(string $name, array $default = []): self
    {
        if (false == array_key_exists($name, $this->query)) {
            return new self($default);
        }

        $value = $this->query[$name];

        if (is_array($value)) {
            return new self($value);
        }

        throw InvalidQueryParameterTypeException::create($name, 'array');
    }
}


code:
Supporting Enqueue

Enqueue is an MIT-licensed open source project with its ongoing development made possible entirely by the support of community and our customers. If you'd like to join them, please consider:

- [Become a sponsor](https://www.patreon.com/makasim)
- [Become our client](http://forma-pro.com/)

---

# Enqueue. Parse DSN class

## Resources

* [Site](https://enqueue.forma-pro.com/)
* [Documentation](https://github.com/php-enqueue/enqueue-dev/blob/master/docs/dsn.md)
* [Questions](https://gitter.im/php-enqueue/Lobby)
* [Issue Tracker](https://github.com/php-enqueue/enqueue-dev/issues)

## Developed by Forma-Pro

Forma-Pro is a full stack development company which interests also spread to open source development.
Being a team of strong professionals we have an aim an ability to help community by developing cutting edge solutions in the areas of e-commerce, docker & microservice oriented architecture where we have accumulated a huge many-years experience.
Our main specialization is Symfony framework based solution, but we are always looking to the technologies that allow us to do our job the best way. We are committed to creating solutions that revolutionize the way how things are developed in aspects of architecture & scalability.

If you have any questions and inquires about our open source development, this product particularly or any other matter feel free to contact at opensource@forma-pro.com

## License

It is released under the [MIT License](LICENSE).


enqueue/dsn

type: library

description: Parse DSN

keywords: dsn parse

license: MIT

minimum-stability: dev


code:
<?php

namespace Enqueue\Stomp;

use Stomp\Client;
use Stomp\Transport\Frame;

class BufferedStompClient extends Client
{
    /**
     * [
     *   'subscriptionId' => Frame[],
     * ].
     *
     * @var array
     */
    private $buffer;

    /**
     * @var int int
     */
    private $bufferSize;

    /**
     * @var int
     */
    private $currentBufferSize;

    /**
     * @param \Stomp\Network\Connection|string $broker
     * @param int                              $bufferSize
     */
    public function __construct($broker, $bufferSize = 1000)
    {
        parent::__construct($broker);

        $this->bufferSize = $bufferSize;
        $this->buffer = [];
        $this->currentBufferSize = 0;
    }

    /**
     * @return int
     */
    public function getBufferSize()
    {
        return $this->bufferSize;
    }

    /**
     * Timeout is in milliseconds.
     */
    public function readMessageFrame(string $subscriptionId, int $timeout): ?Frame
    {
        // pop up frame from the buffer
        if (isset($this->buffer[$subscriptionId]) && ($frame = array_shift($this->buffer[$subscriptionId]))) {
            --$this->currentBufferSize;

            return $frame;
        }

        // do nothing when buffer is full
        if ($this->currentBufferSize >= $this->bufferSize) {
            return null;
        }

        $startTime = microtime(true);
        $remainingTimeout = $timeout * 1000;

        while (true) {
            $this->getConnection()->setReadTimeout(0, $remainingTimeout);

            // there is nothing to read
            if (false === $frame = $this->readFrame()) {
                return null;
            }

            if ('MESSAGE' !== $frame->getCommand()) {
                throw new \LogicException(sprintf('Unexpected frame was received: "%s"', $frame->getCommand()));
            }

            $headers = $frame->getHeaders();

            if (false == isset($headers['subscription'])) {
                throw new \LogicException('Got message frame with missing subscription header');
            }

            // frame belongs to another subscription
            if ($headers['subscription'] !== $subscriptionId) {
                $this->buffer[$headers['subscription']][] = $frame;
                ++$this->currentBufferSize;

                $remainingTimeout -= (microtime(true) - $startTime) * 1000000;

                if ($remainingTimeout <= 0) {
                    return null;
                }

                continue;
            }

            return $frame;
        }
    }

    /**
     * {@inheritdoc}
     */
    public function disconnect($sync = false)
    {
        parent::disconnect($sync);

        $this->buffer = [];
        $this->currentBufferSize = 0;
    }
}


code:
Supporting Enqueue

Enqueue is an MIT-licensed open source project with its ongoing development made possible entirely by the support of community and our customers. If you'd like to join them, please consider:

- [Become a sponsor](https://www.patreon.com/makasim)
- [Become our client](http://forma-pro.com/)

---

# STOMP Transport

[![Gitter](https://badges.gitter.im/php-enqueue/Lobby.svg)](https://gitter.im/php-enqueue/Lobby)
[![Build Status](https://travis-ci.org/php-enqueue/stomp.png?branch=master)](https://travis-ci.org/php-enqueue/stomp)
[![Total Downloads](https://poser.pugx.org/enqueue/stomp/d/total.png)](https://packagist.org/packages/enqueue/stomp)
[![Latest Stable Version](https://poser.pugx.org/enqueue/stomp/version.png)](https://packagist.org/packages/enqueue/stomp)

This is an implementation of Queue Interop specification. It allows you to send and consume message via STOMP protocol.

## Resources

* [Site](https://enqueue.forma-pro.com/)
* [Documentation](https://php-enqueue.github.io/transport/stomp/)
* [Questions](https://gitter.im/php-enqueue/Lobby)
* [Issue Tracker](https://github.com/php-enqueue/enqueue-dev/issues)

## Developed by Forma-Pro

Forma-Pro is a full stack development company which interests also spread to open source development.
Being a team of strong professionals we have an aim an ability to help community by developing cutting edge solutions in the areas of e-commerce, docker & microservice oriented architecture where we have accumulated a huge many-years experience.
Our main specialization is Symfony framework based solution, but we are always looking to the technologies that allow us to do our job the best way. We are committed to creating solutions that revolutionize the way how things are developed in aspects of architecture & scalability.

If you have any questions and inquires about our open source development, this product particularly or any other matter feel free to contact at opensource@forma-pro.com

## License

It is released under the [MIT License](LICENSE).


code:
<?php

declare(strict_types=1);

namespace Enqueue\Stomp;

use Enqueue\Dsn\Dsn;
use Interop\Queue\ConnectionFactory;
use Interop\Queue\Context;
use Stomp\Network\Connection;

class StompConnectionFactory implements ConnectionFactory
{
    const SCHEME_EXT_ACTIVEMQ = 'activemq';
    const SCHEME_EXT_RABBITMQ = 'rabbitmq';

    /**
     * @var array
     */
    private $config;

    /**
     * @var BufferedStompClient
     */
    private $stomp;

    /**
     * $config = [
     * 'host' => null,
     * 'port' => null,
     * 'login' => null,
     * 'password' => null,
     * 'vhost' => null,
     * 'buffer_size' => 1000,
     * 'connection_timeout' => 1,
     * 'sync' => false,
     * 'lazy' => true,
     * 'ssl_on' => false,
     * ].
     *
     * or
     *
     * stomp:
     * stomp:?buffer_size=100
     *
     * @param array|string|null $config
     */
    public function __construct($config = 'stomp:')
    {
        if (empty($config) || 'stomp:' === $config) {
            $config = [];
        } elseif (is_string($config)) {
            $config = $this->parseDsn($config);
        } elseif (is_array($config)) {
            if (array_key_exists('dsn', $config)) {
                $config = array_replace($config, $this->parseDsn($config['dsn']));

                unset($config['dsn']);
            }
        } else {
            throw new \LogicException('The config must be either an array of options, a DSN string or null');
        }

        $this->config = array_replace($this->defaultConfig(), $config);
    }

    /**
     * @return StompContext
     */
    public function createContext(): Context
    {
        $useExchangePrefix = self::SCHEME_EXT_RABBITMQ === $this->config['target'] ? true : false;

        if ($this->config['lazy']) {
            return new StompContext(function () {
                return $this->establishConnection();
            }, $useExchangePrefix);
        }

        return new StompContext($this->establishConnection(), $useExchangePrefix);
    }

    private function establishConnection(): BufferedStompClient
    {
        if (false == $this->stomp) {
            $config = $this->config;

            $scheme = (true === $config['ssl_on']) ? 'ssl' : 'tcp';
            $uri = $scheme.'://'.$config['host'].':'.$config['port'];
            $connection = new Connection($uri, $config['connection_timeout']);

            $this->stomp = new BufferedStompClient($connection, $config['buffer_size']);
            $this->stomp->setLogin($config['login'], $config['password']);
            $this->stomp->setVhostname($config['vhost']);
            $this->stomp->setSync($config['sync']);

            $this->stomp->connect();
        }

        return $this->stomp;
    }

    private function parseDsn(string $dsn): array
    {
        $dsn = Dsn::parseFirst($dsn);

        if ('stomp' !== $dsn->getSchemeProtocol()) {
            throw new \LogicException(sprintf('The given DSN is not supported. Must start with "stomp:".'));
        }

        $schemeExtension = current($dsn->getSchemeExtensions());
        if (false === $schemeExtension) {
            $schemeExtension = self::SCHEME_EXT_RABBITMQ;
        }
        if (self::SCHEME_EXT_ACTIVEMQ !== $schemeExtension && self::SCHEME_EXT_RABBITMQ !== $schemeExtension) {
            throw new \LogicException(sprintf('The given DSN is not supported. The scheme extension "%s" provided is invalid. It must be one of "%s" or "%s".', $schemeExtension, self::SCHEME_EXT_ACTIVEMQ, self::SCHEME_EXT_RABBITMQ));
        }

        return array_filter(array_replace($dsn->getQuery(), [
            'target' => $schemeExtension,
            'host' => $dsn->getHost(),
            'port' => $dsn->getPort(),
            'login' => $dsn->getUser(),
            'password' => $dsn->getPassword(),
            'vhost' => null !== $dsn->getPath() ? ltrim($dsn->getPath(), '/') : null,
            'buffer_size' => $dsn->getDecimal('buffer_size'),
            'connection_timeout' => $dsn->getDecimal('connection_timeout'),
            'sync' => $dsn->getBool('sync'),
            'lazy' => $dsn->getBool('lazy'),
            'ssl_on' => $dsn->getBool('ssl_on'),
        ]), function ($value) { return null !== $value; });
    }

    private function defaultConfig(): array
    {
        return [
            'target' => self::SCHEME_EXT_RABBITMQ,
            'host' => 'localhost',
            'port' => 61613,
            'login' => 'guest',
            'password' => 'guest',
            'vhost' => '/',
            'buffer_size' => 1000,
            'connection_timeout' => 1,
            'sync' => false,
            'lazy' => true,
            'ssl_on' => false,
        ];
    }
}


code:
<?php

declare(strict_types=1);

namespace Enqueue\Stomp;

use Interop\Queue\Consumer;
use Interop\Queue\Exception\InvalidMessageException;
use Interop\Queue\Message;
use Interop\Queue\Queue;
use Stomp\Client;
use Stomp\Transport\Frame;

class StompConsumer implements Consumer
{
    const ACK_AUTO = 'auto';
    const ACK_CLIENT = 'client';
    const ACK_CLIENT_INDIVIDUAL = 'client-individual';

    /**
     * @var StompDestination
     */
    private $queue;

    /**
     * @var Client
     */
    private $stomp;

    /**
     * @var bool
     */
    private $isSubscribed;

    /**
     * @var string
     */
    private $ackMode;

    /**
     * @var int
     */
    private $prefetchCount;

    /**
     * @var string
     */
    private $subscriptionId;

    public function __construct(BufferedStompClient $stomp, StompDestination $queue)
    {
        $this->stomp = $stomp;
        $this->queue = $queue;
        $this->isSubscribed = false;
        $this->ackMode = self::ACK_CLIENT_INDIVIDUAL;
        $this->prefetchCount = 1;
        $this->subscriptionId = StompDestination::TYPE_TEMP_QUEUE == $queue->getType() ?
            $queue->getQueueName() :
            uniqid('', true)
        ;
    }

    public function setAckMode(string $mode): void
    {
        if (false === in_array($mode, [self::ACK_AUTO, self::ACK_CLIENT, self::ACK_CLIENT_INDIVIDUAL], true)) {
            throw new \LogicException(sprintf('Ack mode is not valid: "%s"', $mode));
        }

        $this->ackMode = $mode;
    }

    public function getAckMode(): string
    {
        return $this->ackMode;
    }

    public function getPrefetchCount(): int
    {
        return $this->prefetchCount;
    }

    public function setPrefetchCount(int $prefetchCount): void
    {
        $this->prefetchCount = $prefetchCount;
    }

    /**
     * @return StompDestination
     */
    public function getQueue(): Queue
    {
        return $this->queue;
    }

    public function receive(int $timeout = 0): ?Message
    {
        $this->subscribe();

        if (0 === $timeout) {
            while (true) {
                if ($message = $this->stomp->readMessageFrame($this->subscriptionId, 100)) {
                    return $this->convertMessage($message);
                }
            }
        } else {
            if ($message = $this->stomp->readMessageFrame($this->subscriptionId, $timeout)) {
                return $this->convertMessage($message);
            }
        }

        return null;
    }

    public function receiveNoWait(): ?Message
    {
        $this->subscribe();

        if ($message = $this->stomp->readMessageFrame($this->subscriptionId, 0)) {
            return $this->convertMessage($message);
        }

        return null;
    }

    /**
     * @param StompMessage $message
     */
    public function acknowledge(Message $message): void
    {
        InvalidMessageException::assertMessageInstanceOf($message, StompMessage::class);

        $this->stomp->sendFrame(
            $this->stomp->getProtocol()->getAckFrame($message->getFrame())
        );
    }

    /**
     * @param StompMessage $message
     */
    public function reject(Message $message, bool $requeue = false): void
    {
        InvalidMessageException::assertMessageInstanceOf($message, StompMessage::class);

        $nackFrame = $this->stomp->getProtocol()->getNackFrame($message->getFrame());

        // rabbitmq STOMP protocol extension
        $nackFrame->addHeaders([
            'requeue' => $requeue ? 'true' : 'false',
        ]);

        $this->stomp->sendFrame($nackFrame);
    }

    private function subscribe(): void
    {
        if (StompDestination::TYPE_TEMP_QUEUE == $this->queue->getType()) {
            $this->isSubscribed = true;

            return;
        }

        if (false == $this->isSubscribed) {
            $this->isSubscribed = true;

            $frame = $this->stomp->getProtocol()->getSubscribeFrame(
                $this->queue->getQueueName(),
                $this->subscriptionId,
                $this->ackMode
            );

            // rabbitmq STOMP protocol extension
            $headers = $this->queue->getHeaders();
            $headers['prefetch-count'] = $this->prefetchCount;
            $headers = StompHeadersEncoder::encode($headers);

            foreach ($headers as $key => $value) {
                $frame[$key] = $value;
            }

            $this->stomp->sendFrame($frame);
        }
    }

    private function convertMessage(Frame $frame): StompMessage
    {
        if ('MESSAGE' !== $frame->getCommand()) {
            throw new \LogicException(sprintf('Frame is not MESSAGE frame but: "%s"', $frame->getCommand()));
        }

        list($headers, $properties) = StompHeadersEncoder::decode($frame->getHeaders());

        $redelivered = isset($headers['redelivered']) && 'true' === $headers['redelivered'];

        unset(
            $headers['redelivered'],
            $headers['destination'],
            $headers['message-id'],
            $headers['ack'],
            $headers['receipt'],
            $headers['subscription'],
            $headers['content-length']
        );

        $message = new StompMessage((string) $frame->getBody(), $properties, $headers);
        $message->setRedelivered($redelivered);
        $message->setFrame($frame);

        return $message;
    }
}


code:
<?php

declare(strict_types=1);

namespace Enqueue\Stomp;

use Interop\Queue\Consumer;
use Interop\Queue\Context;
use Interop\Queue\Destination;
use Interop\Queue\Exception\InvalidDestinationException;
use Interop\Queue\Exception\PurgeQueueNotSupportedException;
use Interop\Queue\Exception\SubscriptionConsumerNotSupportedException;
use Interop\Queue\Message;
use Interop\Queue\Producer;
use Interop\Queue\Queue;
use Interop\Queue\SubscriptionConsumer;
use Interop\Queue\Topic;

class StompContext implements Context
{
    /**
     * @var BufferedStompClient
     */
    private $stomp;

    /**
     * @var bool
     */
    private $useExchangePrefix;

    /**
     * @var callable
     */
    private $stompFactory;

    /**
     * @param BufferedStompClient|callable $stomp
     * @param bool                         $useExchangePrefix
     */
    public function __construct($stomp, $useExchangePrefix = true)
    {
        if ($stomp instanceof BufferedStompClient) {
            $this->stomp = $stomp;
        } elseif (is_callable($stomp)) {
            $this->stompFactory = $stomp;
        } else {
            throw new \InvalidArgumentException('The stomp argument must be either BufferedStompClient or callable that return BufferedStompClient.');
        }

        $this->useExchangePrefix = $useExchangePrefix;
    }

    /**
     * @return StompMessage
     */
    public function createMessage(string $body = '', array $properties = [], array $headers = []): Message
    {
        return new StompMessage($body, $properties, $headers);
    }

    /**
     * @return StompDestination
     */
    public function createQueue(string $name): Queue
    {
        if (0 !== strpos($name, '/')) {
            $destination = new StompDestination();
            $destination->setType(StompDestination::TYPE_QUEUE);
            $destination->setStompName($name);

            return $destination;
        }

        return $this->createDestination($name);
    }

    /**
     * @return StompDestination
     */
    public function createTemporaryQueue(): Queue
    {
        $queue = $this->createQueue(uniqid('', true));
        $queue->setType(StompDestination::TYPE_TEMP_QUEUE);

        return $queue;
    }

    /**
     * @return StompDestination
     */
    public function createTopic(string $name): Topic
    {
        if (0 !== strpos($name, '/')) {
            $destination = new StompDestination();
            $destination->setType($this->useExchangePrefix ? StompDestination::TYPE_EXCHANGE : StompDestination::TYPE_TOPIC);
            $destination->setStompName($name);

            return $destination;
        }

        return $this->createDestination($name);
    }

    public function createDestination(string $destination): StompDestination
    {
        $types = [
            StompDestination::TYPE_TOPIC,
            StompDestination::TYPE_EXCHANGE,
            StompDestination::TYPE_QUEUE,
            StompDestination::TYPE_AMQ_QUEUE,
            StompDestination::TYPE_TEMP_QUEUE,
            StompDestination::TYPE_REPLY_QUEUE,
        ];

        $dest = $destination;
        $type = null;
        $name = null;
        $routingKey = null;

        foreach ($types as $_type) {
            $typePrefix = '/'.$_type.'/';
            if (0 === strpos($dest, $typePrefix)) {
                $type = $_type;
                $dest = substr($dest, strlen($typePrefix));

                break;
            }
        }

        if (null === $type) {
            throw new \LogicException(sprintf('Destination name is invalid, cant find type: "%s"', $destination));
        }

        $pieces = explode('/', $dest);

        if (count($pieces) > 2) {
            throw new \LogicException(sprintf('Destination name is invalid, found extra / char: "%s"', $destination));
        }

        if (empty($pieces[0])) {
            throw new \LogicException(sprintf('Destination name is invalid, name is empty: "%s"', $destination));
        }

        $name = $pieces[0];

        if (isset($pieces[1])) {
            if (empty($pieces[1])) {
                throw new \LogicException(sprintf('Destination name is invalid, routing key is empty: "%s"', $destination));
            }

            $routingKey = $pieces[1];
        }

        $destination = new StompDestination();
        $destination->setType($type);
        $destination->setStompName($name);
        $destination->setRoutingKey($routingKey);

        return $destination;
    }

    /**
     * @param StompDestination $destination
     *
     * @return StompConsumer
     */
    public function createConsumer(Destination $destination): Consumer
    {
        InvalidDestinationException::assertDestinationInstanceOf($destination, StompDestination::class);

        return new StompConsumer($this->getStomp(), $destination);
    }

    /**
     * @return StompProducer
     */
    public function createProducer(): Producer
    {
        return new StompProducer($this->getStomp());
    }

    public function close(): void
    {
        $this->getStomp()->disconnect();
    }

    public function createSubscriptionConsumer(): SubscriptionConsumer
    {
        throw SubscriptionConsumerNotSupportedException::providerDoestNotSupportIt();
    }

    public function purgeQueue(Queue $queue): void
    {
        throw PurgeQueueNotSupportedException::providerDoestNotSupportIt();
    }

    public function getStomp(): BufferedStompClient
    {
        if (false == $this->stomp) {
            $stomp = call_user_func($this->stompFactory);
            if (false == $stomp instanceof BufferedStompClient) {
                throw new \LogicException(sprintf(
                    'The factory must return instance of BufferedStompClient. It returns %s',
                    is_object($stomp) ? get_class($stomp) : gettype($stomp)
                ));
            }

            $this->stomp = $stomp;
        }

        return $this->stomp;
    }
}


code:
<?php

declare(strict_types=1);

namespace Enqueue\Stomp;

use Interop\Queue\Queue;
use Interop\Queue\Topic;

class StompDestination implements Topic, Queue
{
    const TYPE_TOPIC = 'topic';
    const TYPE_EXCHANGE = 'exchange';
    const TYPE_QUEUE = 'queue';
    const TYPE_AMQ_QUEUE = 'amq/queue';
    const TYPE_TEMP_QUEUE = 'temp-queue';
    const TYPE_REPLY_QUEUE = 'reply-queue';

    const HEADER_DURABLE = 'durable';
    const HEADER_AUTO_DELETE = 'auto-delete';
    const HEADER_EXCLUSIVE = 'exclusive';

    /**
     * @var string
     */
    private $name;

    /**
     * @var string
     */
    private $type;

    /**
     * @var string
     */
    private $routingKey;

    /**
     * @var array
     */
    private $headers;

    public function __construct()
    {
        $this->headers = [
            self::HEADER_DURABLE => false,
            self::HEADER_AUTO_DELETE => true,
            self::HEADER_EXCLUSIVE => false,
        ];
    }

    public function getStompName(): string
    {
        return $this->name;
    }

    public function setStompName(string $name): void
    {
        $this->name = $name;
    }

    public function getQueueName(): string
    {
        if (empty($this->getStompName())) {
            throw new \LogicException('Destination name is not set');
        }

        $name = '/'.$this->getType().'/'.$this->getStompName();

        if ($this->getRoutingKey()) {
            $name .= '/'.$this->getRoutingKey();
        }

        return $name;
    }

    public function getTopicName(): string
    {
        return $this->getQueueName();
    }

    public function getType(): string
    {
        return $this->type;
    }

    public function setType(string $type): void
    {
        $types = [
            self::TYPE_TOPIC,
            self::TYPE_EXCHANGE,
            self::TYPE_QUEUE,
            self::TYPE_AMQ_QUEUE,
            self::TYPE_TEMP_QUEUE,
            self::TYPE_REPLY_QUEUE,
        ];

        if (false == in_array($type, $types, true)) {
            throw new \LogicException(sprintf('Invalid destination type: "%s"', $type));
        }

        $this->type = $type;
    }

    public function getRoutingKey(): ?string
    {
        return $this->routingKey;
    }

    public function setRoutingKey(string $routingKey = null): void
    {
        $this->routingKey = $routingKey;
    }

    public function isDurable(): bool
    {
        return $this->getHeader(self::HEADER_DURABLE, false);
    }

    public function setDurable(bool $durable): void
    {
        $this->setHeader(self::HEADER_DURABLE, $durable);
    }

    public function isAutoDelete(): bool
    {
        return $this->getHeader(self::HEADER_AUTO_DELETE, false);
    }

    public function setAutoDelete(bool $autoDelete): void
    {
        $this->setHeader(self::HEADER_AUTO_DELETE, $autoDelete);
    }

    public function isExclusive(): bool
    {
        return $this->getHeader(self::HEADER_EXCLUSIVE, false);
    }

    public function setExclusive(bool $exclusive): void
    {
        $this->setHeader(self::HEADER_EXCLUSIVE, $exclusive);
    }

    public function setHeaders(array $headers): void
    {
        $this->headers = $headers;
    }

    public function getHeaders(): array
    {
        return $this->headers;
    }

    public function getHeader(string $name, $default = null)
    {
        return array_key_exists($name, $this->headers) ? $this->headers[$name] : $default;
    }

    public function setHeader(string $name, $value): void
    {
        $this->headers[$name] = $value;
    }
}


code:
<?php

declare(strict_types=1);

namespace Enqueue\Stomp;

class StompHeadersEncoder
{
    const PROPERTY_PREFIX = '_property_';
    const TYPE_PREFIX = '_type_';
    const TYPE_STRING = 's';
    const TYPE_INT = 'i';
    const TYPE_FLOAT = 'f';
    const TYPE_BOOL = 'b';
    const TYPE_NULL = 'n';

    public static function encode(array $headers = [], array $properties = []): array
    {
        $encodedHeaders = self::doEncode($headers);

        foreach (self::doEncode($properties) as $key => $value) {
            $encodedHeaders[self::PROPERTY_PREFIX.$key] = $value;
        }

        return $encodedHeaders;
    }

    /**
     * Returns array [[headers], [properties]].
     */
    public static function decode(array $headers = []): array
    {
        $encodedHeaders = [];
        $encodedProperties = [];
        $prefixLength = strlen(self::PROPERTY_PREFIX);

        // separate headers/properties
        foreach ($headers as $key => $value) {
            if (0 === strpos($key, self::PROPERTY_PREFIX)) {
                $encodedProperties[substr($key, $prefixLength)] = $value;
            } else {
                $encodedHeaders[$key] = $value;
            }
        }

        $decodedHeaders = self::doDecode($encodedHeaders);
        $decodedProperties = self::doDecode($encodedProperties);

        return [$decodedHeaders, $decodedProperties];
    }

    private static function doEncode(array $headers = []): array
    {
        $encoded = [];

        foreach ($headers as $key => $value) {
            switch ($type = gettype($value)) {
                case 'string':
                    $encoded[$key] = (string) $value;
                    $encoded[self::TYPE_PREFIX.$key] = self::TYPE_STRING;

                    break;
                case 'integer':
                    $encoded[$key] = (string) $value;
                    $encoded[self::TYPE_PREFIX.$key] = self::TYPE_INT;

                    break;
                case 'double':
                    $encoded[$key] = (string) $value;
                    $encoded[self::TYPE_PREFIX.$key] = self::TYPE_FLOAT;

                    break;
                case 'NULL':
                    $encoded[$key] = '';
                    $encoded[self::TYPE_PREFIX.$key] = self::TYPE_NULL;

                    break;
                case 'boolean':
                    $encoded[$key] = $value ? 'true' : 'false';
                    $encoded[self::TYPE_PREFIX.$key] = self::TYPE_BOOL;

                    break;
                default:
                    throw new \LogicException(sprintf('Value type is not valid: "%s"', $type));
            }
        }

        return $encoded;
    }

    private static function doDecode(array $headers = []): array
    {
        $decoded = [];

        foreach ($headers as $key => $value) {
            // skip type header
            if (0 === strpos($key, self::TYPE_PREFIX)) {
                continue;
            }

            // copy value as is if here is no type header
            if (false == array_key_exists(self::TYPE_PREFIX.$key, $headers)) {
                $decoded[$key] = $value;

                continue;
            }

            switch ($headers[self::TYPE_PREFIX.$key]) {
                case self::TYPE_STRING:
                    $decoded[$key] = (string) $value;

                    break;
                case self::TYPE_INT:
                    $decoded[$key] = (int) $value;

                    break;
                case self::TYPE_FLOAT:
                    $decoded[$key] = (float) $value;

                    break;
                case self::TYPE_NULL:
                    $decoded[$key] = null;

                    break;
                case self::TYPE_BOOL:
                    $decoded[$key] = 'true' === $value;

                    break;
                default:
                    throw new \LogicException(sprintf('Type is invalid: "%s"', $value));
            }
        }

        return $decoded;
    }
}


code:
<?php

declare(strict_types=1);

namespace Enqueue\Stomp;

use Interop\Queue\Message;
use Stomp\Transport\Frame;

class StompMessage implements Message
{
    /**
     * @var string
     */
    private $body;

    /**
     * @var array
     */
    private $properties;

    /**
     * @var array
     */
    private $headers;

    /**
     * @var bool
     */
    private $redelivered;

    /**
     * @var Frame
     */
    private $frame;

    public function __construct(string $body = '', array $properties = [], array $headers = [])
    {
        $this->body = $body;
        $this->properties = $properties;
        $this->headers = $headers;
        $this->redelivered = false;
    }

    public function setBody(string $body): void
    {
        $this->body = $body;
    }

    public function getBody(): string
    {
        return $this->body;
    }

    public function setProperties(array $properties): void
    {
        $this->properties = $properties;
    }

    public function getProperties(): array
    {
        return $this->properties;
    }

    public function setProperty(string $name, $value): void
    {
        if (null === $value) {
            unset($this->properties[$name]);
        } else {
            $this->properties[$name] = $value;
        }
    }

    public function getProperty(string $name, $default = null)
    {
        return array_key_exists($name, $this->properties) ? $this->properties[$name] : $default;
    }

    public function setHeaders(array $headers): void
    {
        $this->headers = $headers;
    }

    public function getHeaders(): array
    {
        return $this->headers;
    }

    public function setHeader(string $name, $value): void
    {
        if (null === $value) {
            unset($this->headers[$name]);
        } else {
            $this->headers[$name] = $value;
        }
    }

    public function getHeader(string $name, $default = null)
    {
        return array_key_exists($name, $this->headers) ? $this->headers[$name] : $default;
    }

    public function isPersistent(): bool
    {
        return $this->getHeader('persistent', false);
    }

    public function setPersistent(bool $persistent): void
    {
        $this->setHeader('persistent', $persistent);
    }

    public function isRedelivered(): bool
    {
        return $this->redelivered;
    }

    public function setRedelivered(bool $redelivered): void
    {
        $this->redelivered = $redelivered;
    }

    public function setCorrelationId(string $correlationId = null): void
    {
        $this->setHeader('correlation_id', (string) $correlationId);
    }

    public function getCorrelationId(): ?string
    {
        return $this->getHeader('correlation_id');
    }

    public function setMessageId(string $messageId = null): void
    {
        $this->setHeader('message_id', (string) $messageId);
    }

    public function getMessageId(): ?string
    {
        return $this->getHeader('message_id');
    }

    public function getTimestamp(): ?int
    {
        $value = $this->getHeader('timestamp');

        return null === $value ? null : (int) $value;
    }

    public function setTimestamp(int $timestamp = null): void
    {
        $this->setHeader('timestamp', $timestamp);
    }

    public function getFrame(): ?Frame
    {
        return $this->frame;
    }

    public function setFrame(Frame $frame = null): void
    {
        $this->frame = $frame;
    }

    public function setReplyTo(string $replyTo = null): void
    {
        $this->setHeader('reply-to', $replyTo);
    }

    public function getReplyTo(): ?string
    {
        return $this->getHeader('reply-to');
    }
}


code:
<?php

declare(strict_types=1);

namespace Enqueue\Stomp;

use Interop\Queue\Destination;
use Interop\Queue\Exception\InvalidDestinationException;
use Interop\Queue\Exception\InvalidMessageException;
use Interop\Queue\Exception\PriorityNotSupportedException;
use Interop\Queue\Message;
use Interop\Queue\Producer;
use Stomp\Client;
use Stomp\Transport\Message as StompLibMessage;

class StompProducer implements Producer
{
    /**
     * @var Client
     */
    private $stomp;

    /**
     * @param Client $stomp
     */
    public function __construct(Client $stomp)
    {
        $this->stomp = $stomp;
    }

    /**
     * @param StompDestination $destination
     * @param StompMessage     $message
     */
    public function send(Destination $destination, Message $message): void
    {
        InvalidDestinationException::assertDestinationInstanceOf($destination, StompDestination::class);
        InvalidMessageException::assertMessageInstanceOf($message, StompMessage::class);

        $headers = array_merge($message->getHeaders(), $destination->getHeaders());
        $headers = StompHeadersEncoder::encode($headers, $message->getProperties());

        $stompMessage = new StompLibMessage($message->getBody(), $headers);

        $this->stomp->send($destination->getQueueName(), $stompMessage);
    }

    public function setDeliveryDelay(int $deliveryDelay = null): Producer
    {
        if (null === $deliveryDelay) {
            return $this;
        }

        throw new \LogicException('Not implemented');
    }

    public function getDeliveryDelay(): ?int
    {
        return null;
    }

    public function setPriority(int $priority = null): Producer
    {
        if (null === $priority) {
            return $this;
        }

        throw PriorityNotSupportedException::providerDoestNotSupportIt();
    }

    public function getPriority(): ?int
    {
        return null;
    }

    public function setTimeToLive(int $timeToLive = null): Producer
    {
        if (null === $timeToLive) {
            return $this;
        }

        throw new \LogicException('Not implemented');
    }

    public function getTimeToLive(): ?int
    {
        return null;
    }
}


enqueue/stomp

description: Message Queue Stomp Transport

keywords: messaging queue stomp


code:
CREDITS

Almost everything written by Edward Z. Yang (Ambush Commander).  Lots of thanks
to the DevNetwork Community for their help (see docs/ref-devnetwork.html for
more details), Feyd especially (namely IPv6 and optimization).  Thanks to RSnake
for letting me package his fantastic XSS cheatsheet for a smoketest.

    vim: et sw=4 sts=4

path: vendor/ezyang/htmlpurifier/CREDITS


code:
Install
    How to install HTML Purifier

HTML Purifier is designed to run out of the box, so actually using the
library is extremely easy.  (Although... if you were looking for a
step-by-step installation GUI, you've downloaded the wrong software!)

While the impatient can get going immediately with some of the sample
code at the bottom of this library, it's well worth reading this entire
document--most of the other documentation assumes that you are familiar
with these contents.

---------------------------------------------------------------------------
1.  Compatibility

HTML Purifier is PHP 5 and PHP 7, and is actively tested from PHP 5.3
and up. It has no core dependencies with other libraries.

These optional extensions can enhance the capabilities of HTML Purifier:

    * iconv  : Converts text to and from non-UTF-8 encodings
    * bcmath : Used for unit conversion and imagecrash protection
    * tidy   : Used for pretty-printing HTML

These optional libraries can enhance the capabilities of HTML Purifier:

    * CSSTidy : Clean CSS stylesheets using %Core.ExtractStyleBlocks
        Note: You should use the modernized fork of CSSTidy available
        at https://github.com/Cerdic/CSSTidy
    * Net_IDNA2 (PEAR) : IRI support using %Core.EnableIDNA
        Note: This is not necessary for PHP 5.3 or later

---------------------------------------------------------------------------
2.  Reconnaissance

A big plus of HTML Purifier is its inerrant support of standards, so
your web-pages should be standards-compliant.  (They should also use
semantic markup, but that's another issue altogether, one HTML Purifier
cannot fix without reading your mind.)

HTML Purifier can process these doctypes:

* XHTML 1.0 Transitional (default)
* XHTML 1.0 Strict
* HTML 4.01 Transitional
* HTML 4.01 Strict
* XHTML 1.1

...and these character encodings:

* UTF-8 (default)
* Any encoding iconv supports (with crippled internationalization support)

These defaults reflect what my choices would be if I were authoring an
HTML document, however, what you choose depends on the nature of your
codebase.  If you don't know what doctype you are using, you can determine
the doctype from this identifier at the top of your source code:

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

...and the character encoding from this code:

    <meta http-equiv="Content-type" content="text/html;charset=ENCODING">

If the character encoding declaration is missing, STOP NOW, and
read 'docs/enduser-utf8.html' (web accessible at
http://htmlpurifier.org/docs/enduser-utf8.html).  In fact, even if it is
present, read this document anyway, as many websites specify their
document's character encoding incorrectly.

---------------------------------------------------------------------------
3.  Including the library

The procedure is quite simple:

    require_once '/path/to/library/HTMLPurifier.auto.php';

This will setup an autoloader, so the library's files are only included
when you use them.

Only the contents in the library/ folder are necessary, so you can remove
everything else when using HTML Purifier in a production environment.

If you installed HTML Purifier via PEAR, all you need to do is:

    require_once 'HTMLPurifier.auto.php';

Please note that the usual PEAR practice of including just the classes you
want will not work with HTML Purifier's autoloading scheme.

Advanced users, read on; other users can skip to section 4.

Autoload compatibility
----------------------

    HTML Purifier attempts to be as smart as possible when registering an
    autoloader, but there are some cases where you will need to change
    your own code to accomodate HTML Purifier. These are those cases:

    AN __autoload FUNCTION IS DECLARED AFTER OUR AUTOLOADER IS REGISTERED
        spl_autoload_register() has the curious behavior of disabling
        the existing __autoload() handler. Users need to explicitly
        spl_autoload_register('__autoload'). Because we use SPL when it
        is available, __autoload() will ALWAYS be disabled. If __autoload()
        is declared before HTML Purifier is loaded, this is not a problem:
        HTML Purifier will register the function for you. But if it is
        declared afterwards, it will mysteriously not work. This
        snippet of code (after your autoloader is defined) will fix it:

            spl_autoload_register('__autoload')

For better performance
----------------------

    Opcode caches, which greatly speed up PHP initialization for scripts
    with large amounts of code (HTML Purifier included), don't like
    autoloaders. We offer an include file that includes all of HTML Purifier's
    files in one go in an opcode cache friendly manner:

        // If /path/to/library isn't already in your include path, uncomment
        // the below line:
        // require '/path/to/library/HTMLPurifier.path.php';

        require 'HTMLPurifier.includes.php';

    Optional components still need to be included--you'll know if you try to
    use a feature and you get a class doesn't exists error! The autoloader
    can be used in conjunction with this approach to catch classes that are
    missing. Simply add this afterwards:

        require 'HTMLPurifier.autoload.php';

Standalone version
------------------

    HTML Purifier has a standalone distribution; you can also generate
    a standalone file from the full version by running the script
    maintenance/generate-standalone.php . The standalone version has the
    benefit of having most of its code in one file, so parsing is much
    faster and the library is easier to manage.

    If HTMLPurifier.standalone.php exists in the library directory, you
    can use it like this:

        require '/path/to/HTMLPurifier.standalone.php';

    This is equivalent to including HTMLPurifier.includes.php, except that
    the contents of standalone/ will be added to your path. To override this
    behavior, specify a new HTMLPURIFIER_PREFIX where standalone files can
    be found (usually, this will be one directory up, the "true" library
    directory in full distributions). Don't forget to set your path too!

    The autoloader can be added to the end to ensure the classes are
    loaded when necessary; otherwise you can manually include them.
    To use the autoloader, use this:

        require 'HTMLPurifier.autoload.php';

For advanced users
------------------

    HTMLPurifier.auto.php performs a number of operations that can be done
    individually. These are:

        HTMLPurifier.path.php
            Puts /path/to/library in the include path. For high performance,
            this should be done in php.ini.

        HTMLPurifier.autoload.php
            Registers our autoload handler HTMLPurifier_Bootstrap::autoload($class).

    You can do these operations by yourself, if you like.

---------------------------------------------------------------------------
4. Configuration

HTML Purifier is designed to run out-of-the-box, but occasionally HTML
Purifier needs to be told what to do.  If you answer no to any of these
questions, read on; otherwise, you can skip to the next section (or, if you're
into configuring things just for the heck of it, skip to 4.3).

* Am I using UTF-8?
* Am I using XHTML 1.0 Transitional?

If you answered no to any of these questions, instantiate a configuration
object and read on:

    $config = HTMLPurifier_Config::createDefault();

4.1. Setting a different character encoding

You really shouldn't use any other encoding except UTF-8, especially if you
plan to support multilingual websites (read section three for more details).
However, switching to UTF-8 is not always immediately feasible, so we can
adapt.

HTML Purifier uses iconv to support other character encodings, as such,
any encoding that iconv supports <http://www.gnu.org/software/libiconv/>
HTML Purifier supports with this code:

    $config->set('Core.Encoding', /* put your encoding here */);

An example usage for Latin-1 websites (the most common encoding for English
websites):

    $config->set('Core.Encoding', 'ISO-8859-1');

Note that HTML Purifier's support for non-Unicode encodings is crippled by the
fact that any character not supported by that encoding will be silently
dropped, EVEN if it is ampersand escaped.  If you want to work around
this, you are welcome to read docs/enduser-utf8.html for a fix,
but please be cognizant of the issues the "solution" creates (for this
reason, I do not include the solution in this document).

4.2. Setting a different doctype

For those of you using HTML 4.01 Transitional, you can disable
XHTML output like this:

    $config->set('HTML.Doctype', 'HTML 4.01 Transitional');

Other supported doctypes include:

    * HTML 4.01 Strict
    * HTML 4.01 Transitional
    * XHTML 1.0 Strict
    * XHTML 1.0 Transitional
    * XHTML 1.1

4.3. Other settings

There are more configuration directives which can be read about
here: <http://htmlpurifier.org/live/configdoc/plain.html>  They're a bit boring,
but they can help out for those of you who like to exert maximum control over
your code.  Some of the more interesting ones are configurable at the
demo <http://htmlpurifier.org/demo.php> and are well worth looking into
for your own system.

For example, you can fine tune allowed elements and attributes, convert
relative URLs to absolute ones, and even autoparagraph input text! These
are, respectively, %HTML.Allowed, %URI.MakeAbsolute and %URI.Base, and
%AutoFormat.AutoParagraph. The %Namespace.Directive naming convention
translates to:

    $config->set('Namespace.Directive', $value);

E.g.

    $config->set('HTML.Allowed', 'p,b,a[href],i');
    $config->set('URI.Base', 'http://www.example.com');
    $config->set('URI.MakeAbsolute', true);
    $config->set('AutoFormat.AutoParagraph', true);

---------------------------------------------------------------------------
5. Caching

HTML Purifier generates some cache files (generally one or two) to speed up
its execution. For maximum performance, make sure that
library/HTMLPurifier/DefinitionCache/Serializer is writeable by the webserver.

If you are in the library/ folder of HTML Purifier, you can set the
appropriate permissions using:

    chmod -R 0755 HTMLPurifier/DefinitionCache/Serializer

If the above command doesn't work, you may need to assign write permissions
to group:

    chmod -R 0775 HTMLPurifier/DefinitionCache/Serializer

You can also chmod files via your FTP client; this option
is usually accessible by right clicking the corresponding directory and
then selecting "chmod" or "file permissions".

Starting with 2.0.1, HTML Purifier will generate friendly error messages
that will tell you exactly what you have to chmod the directory to, if in doubt,
follow its advice.

If you are unable or unwilling to give write permissions to the cache
directory, you can either disable the cache (and suffer a performance
hit):

    $config->set('Core.DefinitionCache', null);

Or move the cache directory somewhere else (no trailing slash):

    $config->set('Cache.SerializerPath', '/home/user/absolute/path');

---------------------------------------------------------------------------
6.   Using the code

The interface is mind-numbingly simple:

    $purifier = new HTMLPurifier($config);
    $clean_html = $purifier->purify( $dirty_html );

That's it!  For more examples, check out docs/examples/ (they aren't very
different though).  Also, docs/enduser-slow.html gives advice on what to
do if HTML Purifier is slowing down your application.

---------------------------------------------------------------------------
7.   Quick install

First, make sure library/HTMLPurifier/DefinitionCache/Serializer is
writable by the webserver (see Section 5: Caching above for details).
If your website is in UTF-8 and XHTML Transitional, use this code:

<?php
    require_once '/path/to/htmlpurifier/library/HTMLPurifier.auto.php';

    $config = HTMLPurifier_Config::createDefault();
    $purifier = new HTMLPurifier($config);
    $clean_html = $purifier->purify($dirty_html);
?>

If your website is in a different encoding or doctype, use this code:

<?php
    require_once '/path/to/htmlpurifier/library/HTMLPurifier.auto.php';

    $config = HTMLPurifier_Config::createDefault();
    $config->set('Core.Encoding', 'ISO-8859-1'); // replace with your encoding
    $config->set('HTML.Doctype', 'HTML 4.01 Transitional'); // replace with your doctype
    $purifier = new HTMLPurifier($config);

    $clean_html = $purifier->purify($dirty_html);
?>

    vim: et sw=4 sts=4

path: vendor/ezyang/htmlpurifier/INSTALL


code:
Installation
    Comment installer HTML Purifier

Attention : Ce document est encodé en UTF-8, si les lettres avec des accents
ne s'affichent pas, prenez un meilleur éditeur de texte.

L'installation de HTML Purifier est très simple, parce qu'il n'a pas besoin
de configuration. Pour les utilisateurs impatients, le code se trouve dans le
pied de page, mais je recommande de lire le document.

1.  Compatibilité

HTML Purifier fonctionne avec PHP 5. PHP 5.3 est la dernière version testée.
Il ne dépend pas d'autres librairies.

Les extensions optionnelles sont iconv (généralement déjà installée) et tidy
(répendue aussi). Si vous utilisez UTF-8 et que vous ne voulez pas l'indentation,
vous pouvez utiliser HTML Purifier sans ces extensions.

2.  Inclure la librairie

Quand vous devez l'utilisez, incluez le :

    require_once('/path/to/library/HTMLPurifier.auto.php');

Ne pas l'inclure si ce n'est pas nécessaire, car HTML Purifier est lourd.

HTML Purifier utilise "autoload". Si vous avez défini la fonction __autoload,
vous devez ajouter cette fonction :

    spl_autoload_register('__autoload')

Plus d'informations dans le document "INSTALL".

3.  Installation rapide

Si votre site Web est en UTF-8 et XHTML Transitional, utilisez :

<?php
    require_once('/path/to/htmlpurifier/library/HTMLPurifier.auto.php');
    $purificateur = new HTMLPurifier();
    $html_propre = $purificateur->purify($html_a_purifier);
?>

Sinon, utilisez :

<?php
    require_once('/path/to/html/purifier/library/HTMLPurifier.auto.load');
    $config = $HTMLPurifier_Config::createDefault();
    $config->set('Core', 'Encoding', 'ISO-8859-1'); //Remplacez par votre
    encodage
    $config->set('Core', 'XHTML', true); //Remplacer par false si HTML 4.01
    $purificateur = new HTMLPurifier($config);
    $html_propre = $purificateur->purify($html_a_purifier);
?>

    vim: et sw=4 sts=4


code:
NEWS ( CHANGELOG and HISTORY )                                     HTMLPurifier
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

= KEY ====================
    # Breaks back-compat
    ! Feature
    - Bugfix
      + Sub-comment
    . Internal change
==========================

4.12.0, released 2019-10-27
! PHP 7.4 is supported, thank you Witold Wasiczko, Mateuz Turcza and
  Edi Modrić
- PHPDocs for HTMLModule::addElement() and Bool attr are fixed (thanks
  Mateusz)

4.11.0, released 2019-07-14
# SafeScripting now matches case-sensitively against its whitelist (previously it was
  case-insensitive.)  Thanks Dimitri Gritsajuk <gritsajuk.dimitri@gmail.com>
  for reporting.
! New directive %Core.AllowParseManyTags which allows parsing of many nested tags.
  Thanks M. Suzuki <msuzuki1986@gmail.com> for contributing the patch.
! purifyArray now supports multidimensional arrays.  Thanks
  Sandro Miguel Marques <sandromiguel@sandromiguel.com> for contributing this patch.
! initial and inherit settings available for width, height, and the min-/max-
  versions thereof.  Thanks Michael Kliewe <info@phpgansta.de> for contributing
  this patch.
! More color names are supported.  Thanks Daijobou for contributing.
- Compatibility fixes for PHP 7.3, including new CI for PHP 7.3
  (thank you Lukas Neumann <lksnmnn@gmail.com>) and removal of
  reserved words in our constants (thanks Darko Hrgovic <darko@darkodev.com>
- Compatibility fixes for HHVM.  Thanks Mateusz Turcza for contributing
  this fix.
- HTML Purifier now never defines __autoload, fixing #196.  Thanks
  Michael Kliewe for reporting.
- In some situations, Config.php would report an undefined index: class
  error; this has been fixed.  Thanks DiLong Fa for contributing
  this fix.
- We no longer produce

path: vendor/ezyang/htmlpurifier/NEWS


code:
HTML Purifier [![Build Status](https://secure.travis-ci.org/ezyang/htmlpurifier.svg?branch=master)](http://travis-ci.org/ezyang/htmlpurifier)
=============

HTML Purifier is an HTML filtering solution that uses a unique combination
of robust whitelists and aggressive parsing to ensure that not only are
XSS attacks thwarted, but the resulting HTML is standards compliant.

HTML Purifier is oriented towards richly formatted documents from
untrusted sources that require CSS and a full tag-set.  This library can
be configured to accept a more restrictive set of tags, but it won't be
as efficient as more bare-bones parsers. It will, however, do the job
right, which may be more important.

Places to go:

* See INSTALL for a quick installation guide
* See docs/ for developer-oriented documentation, code examples and
  an in-depth installation guide.
* See WYSIWYG for information on editors like TinyMCE and FCKeditor

HTML Purifier can be found on the web at: [http://htmlpurifier.org/](http://htmlpurifier.org/)

## Installation

Package available on [Composer](https://packagist.org/packages/ezyang/htmlpurifier).

If you're using Composer to manage dependencies, you can use

    $ composer require ezyang/htmlpurifier


code:
TODO List

= KEY ====================
    # Flagship
    - Regular
    ? Maybe I'll Do It
==========================

If no interest is expressed for a feature that may require a considerable
amount of effort to implement, it may get endlessly delayed. Do not be
afraid to cast your vote for the next feature to be implemented!

Things to do as soon as possible:

 - http://htmlpurifier.org/phorum/read.php?3,5560,6307#msg-6307
 - Think about allowing explicit order of operations hooks for transforms
 - Fix "<.<" bug (trailing < is removed if not EOD)
 - Build in better internal state dumps and debugging tools for remote
   debugging
 - Allowed/Allowed* have strange interactions when both set
 ? Transform lone embeds into object tags
 - Deprecated config options that emit warnings when you set them (with'
   a way of muting the warning if you really want to)
 - Make HTML.Trusted work with Output.FlashCompat
 - HTML.Trusted and HTML.SafeObject have funny interaction; general
   problem is what to do when a module "supersedes" another
   (see also tables and basic tables.)  This is a little dicier
   because HTML.SafeObject has some extra functionality that
   trusted might find useful.  See http://htmlpurifier.org/phorum/read.php?3,5762,6100

FUTURE VERSIONS
---------------

4.9 release [OMG CONFIG PONIES]
 ! Fix Printer. It's from the old days when we didn't have decent XML classes
 ! Factor demo.php into a set of Printer classes, and then create a stub
   file for users here (inside the actual HTML Purifier library)
 - Fix error handling with form construction
 - Do encoding validation in Printers, or at least, where user data comes in
 - Config: Add examples to everything (make built-in which also automatically
   gives output)
 - Add "register" field to config schemas to eliminate dependence on
   naming conventions (try to remember why we ultimately decided on tihs)

5.0 release [HTML 5]
 # Swap out code to use html5lib tokenizer and tree-builder
 ! Allow turning off of FixNesting and required attribute insertion

5.1 release [It's All About Trust] (floating)
 # Implement untrusted, dangerous elements/attributes
 # Implement IDREF support (harder than it seems, since you cannot have
   IDREFs to non-existent IDs)
     - Implement <area> (client and server side image maps are blocking
       on IDREF support)
 # Frameset XHTML 1.0 and HTML 4.01 doctypes
 - Figure out how to simultaneously set %CSS.Trusted and %HTML.Trusted (?)

5.2 release [Error'ed]
 # Error logging for filtering/cleanup procedures
 # Additional support for poorly written HTML
    - Microsoft Word HTML cleaning (i.e. MsoNormal, but research essential!)
    - Friendly strict handling of <address> (block ->
)
 - XSS-attempt detection--certain errors are flagged XSS-like
 - Append something to duplicate IDs so they're still usable (impl. note: the
   dupe detector would also need to detect the suffix as well)

6.0 release [Beyond HTML]
 # Legit token based CSS parsing (will require revamping almost every
   AttrDef class). Probably will use CSSTidy
 # More control over allowed CSS properties using a modularization
 # IRI support (this includes IDN)
 - Standardize token armor for all areas of processing

7.0 release [To XML and Beyond]
 - Extended HTML capabilities based on namespacing and tag transforms (COMPLEX)
    - Hooks for adding custom processors to custom namespaced tags and
      attributes, offer default implementation
    - Lots of documentation and samples

Ongoing
 - More refactoring to take advantage of PHP5's facilities
 - Refactor unit tests into lots of test methods
 - Plugins for major CMSes (COMPLEX)
    - phpBB
    - Also, a FAQ for extension writers with HTML Purifier

AutoFormat
 - Smileys
 - Syntax highlighting (with GeSHi) with
 and possibly <?php
 - Look at http://drupal.org/project/Modules/category/63 for ideas

Neat feature related
 ! Support exporting configuration, so users can easily tweak settings
   in the demo, and then copy-paste into their own setup
 - Advanced URI filtering schemes (see docs/proposal-new-directives.txt)
 - Allow scoped="scoped" attribute in

path: vendor/ezyang/htmlpurifier/TODO


path: vendor/ezyang/htmlpurifier/VERSION


code:
HTML Purifier 4.12.x is a maintenance release which makes
compatibility fixes for PHP 7.4.

path: vendor/ezyang/htmlpurifier/WHATSNEW


code:
WYSIWYG - What You See Is What You Get
    HTML Purifier: A Pretty Good Fit for TinyMCE and FCKeditor

Javascript-based WYSIWYG editors, simply stated, are quite amazing.  But I've
always been wary about using them due to security issues: they handle the
client-side magic, but once you've been served a piping hot load of unfiltered
HTML, what should be done then?  In some situations, you can serve it uncleaned,
since you only offer these facilities to trusted(?) authors.

Unfortunantely, for blog comments and anonymous input, BBCode, Textile and
other markup languages still reign supreme.  Put simply: filtering HTML is
hard work, and these WYSIWYG authors don't offer anything to alleviate that
trouble.  Therein lies the solution:

HTML Purifier is perfect for filtering pure-HTML input from WYSIWYG editors.

Enough said.

    vim: et sw=4 sts=4

path: vendor/ezyang/htmlpurifier/WYSIWYG


ezyang/htmlpurifier

description: Standards compliant HTML filter written in PHP

HTMLPurifier: library/


code:
<?php

/**
 * Decorator/extender XSLT processor specifically for HTML documents.
 */
class ConfigDoc_HTMLXSLTProcessor
{

    /**
     * Instance of XSLTProcessor
     */
    protected $xsltProcessor;

    public function __construct($proc = false)
    {
        if ($proc === false) $proc = new XSLTProcessor();
        $this->xsltProcessor = $proc;
    }

    /**
     * @note Allows a string $xsl filename to be passed
     */
    public function importStylesheet($xsl)
    {
        if (is_string($xsl)) {
            $xsl_file = $xsl;
            $xsl = new DOMDocument();
            $xsl->load($xsl_file);
        }
        return $this->xsltProcessor->importStylesheet($xsl);
    }

    /**
     * Transforms an XML file into compatible XHTML based on the stylesheet
     * @param $xml XML DOM tree, or string filename
     * @return string HTML output
     * @todo Rename to transformToXHTML, as transformToHTML is misleading
     */
    public function transformToHTML($xml)
    {
        if (is_string($xml)) {
            $dom = new DOMDocument();
            $dom->load($xml);
        } else {
            $dom = $xml;
        }
        $out = $this->xsltProcessor->transformToXML($dom);

        // fudges for HTML backwards compatibility
        // assumes that document is XHTML
        $out = str_replace('/>', ' />', $out); //
 not

        $out = str_replace(' xmlns=""', '', $out); // rm unnecessary xmlns

        if (class_exists('Tidy')) {
            // cleanup output
            $config = array(
                'indent'        => true,
                'output-xhtml'  => true,
                'wrap'          => 80
            );
            $tidy = new Tidy;
            $tidy->parseString($out, $config, 'utf8');
            $tidy->cleanRepair();
            $out = (string) $tidy;
        }

        return $out;
    }

    /**
     * Bulk sets parameters for the XSL stylesheet
     * @param array $options Associative array of options to set
     */
    public function setParameters($options)
    {
        foreach ($options as $name => $value) {
            $this->xsltProcessor->setParameter('', $name, $value);
        }
    }

    /**
     * Forward any other calls to the XSLT processor
     */
    public function __call($name, $arguments)
    {
        call_user_func_array(array($this->xsltProcessor, $name), $arguments);
    }

}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Filesystem tools not provided by default; can recursively create, copy
 * and delete folders. Some template methods are provided for extensibility.
 *
 * @note This class must be instantiated to be used, although it does
 *       not maintain state.
 */
class FSTools
{

    private static $singleton;

    /**
     * Returns a global instance of FSTools
     */
    public static function singleton()
    {
        if (empty(FSTools::$singleton)) FSTools::$singleton = new FSTools();
        return FSTools::$singleton;
    }

    /**
     * Sets our global singleton to something else; useful for overloading
     * functions.
     */
    public static function setSingleton($singleton)
    {
        FSTools::$singleton = $singleton;
    }

    /**
     * Recursively creates a directory
     * @param string $folder Name of folder to create
     * @note Adapted from the PHP manual comment 76612
     */
    public function mkdirr($folder)
    {
        $folders = preg_split("#[\\\\/]#", $folder);
        $base = '';
        for($i = 0, $c = count($folders); $i < $c; $i++) {
            if(empty($folders[$i])) {
                if (!$i) {
                    // special case for root level
                    $base .= DIRECTORY_SEPARATOR;
                }
                continue;
            }
            $base .= $folders[$i];
            if(!is_dir($base)){
                $this->mkdir($base);
            }
            $base .= DIRECTORY_SEPARATOR;
        }
    }

    /**
     * Copy a file, or recursively copy a folder and its contents; modified
     * so that copied files, if PHP, have includes removed
     * @note Adapted from http://aidanlister.com/repos/v/function.copyr.php
     */
    public function copyr($source, $dest)
    {
        // Simple copy for a file
        if (is_file($source)) {
            return $this->copy($source, $dest);
        }
        // Make destination directory
        if (!is_dir($dest)) {
            $this->mkdir($dest);
        }
        // Loop through the folder
        $dir = $this->dir($source);
        while ( false !== ($entry = $dir->read()) ) {
            // Skip pointers
            if ($entry == '.' || $entry == '..') {
                continue;
            }
            if (!$this->copyable($entry)) {
                continue;
            }
            // Deep copy directories
            if ($dest !== "$source/$entry") {
                $this->copyr("$source/$entry", "$dest/$entry");
            }
        }
        // Clean up
        $dir->close();
        return true;
    }

    /**
     * Overloadable function that tests a filename for copyability. By
     * default, everything should be copied; you can restrict things to
     * ignore hidden files, unreadable files, etc. This function
     * applies to copyr().
     */
    public function copyable($file)
    {
        return true;
    }

    /**
     * Delete a file, or a folder and its contents
     * @note Adapted from http://aidanlister.com/repos/v/function.rmdirr.php
     */
    public function rmdirr($dirname)
    {
        // Sanity check
        if (!$this->file_exists($dirname)) {
            return false;
        }

        // Simple delete for a file
        if ($this->is_file($dirname) || $this->is_link($dirname)) {
            return $this->unlink($dirname);
        }

        // Loop through the folder
        $dir = $this->dir($dirname);
        while (false !== $entry = $dir->read()) {
            // Skip pointers
            if ($entry == '.' || $entry == '..') {
                continue;
            }
            // Recurse
            $this->rmdirr($dirname . DIRECTORY_SEPARATOR . $entry);
        }

        // Clean up
        $dir->close();
        return $this->rmdir($dirname);
    }

    /**
     * Recursively globs a directory.
     */
    public function globr($dir, $pattern, $flags = NULL)
    {
        $files = $this->glob("$dir/$pattern", $flags);
        if ($files === false) $files = array();
        $sub_dirs = $this->glob("$dir/*", GLOB_ONLYDIR);
        if ($sub_dirs === false) $sub_dirs = array();
        foreach ($sub_dirs as $sub_dir) {
            $sub_files = $this->globr($sub_dir, $pattern, $flags);
            $files = array_merge($files, $sub_files);
        }
        return $files;
    }

    /**
     * Allows for PHP functions to be called and be stubbed.
     * @warning This function will not work for functions that need
     *      to pass references; manually define a stub function for those.
     */
    public function __call($name, $args)
    {
        return call_user_func_array($name, $args);
    }

}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Represents a file in the filesystem
 *
 * @warning Be sure to distinguish between get() and write() versus
 *      read() and put(), the former operates on the entire file, while
 *      the latter operates on a handle.
 */
class FSTools_File
{

    /** Filename of file this object represents */
    protected $name;

    /** Handle for the file */
    protected $handle = false;

    /** Instance of FSTools for interfacing with filesystem */
    protected $fs;

    /**
     * Filename of file you wish to instantiate.
     * @note This file need not exist
     */
    public function __construct($name, $fs = false)
    {
        $this->name = $name;
        $this->fs = $fs ? $fs : FSTools::singleton();
    }

    /** Returns the filename of the file. */
    public function getName() {return $this->name;}

    /** Returns directory of the file without trailing slash */
    public function getDirectory() {return $this->fs->dirname($this->name);}

    /**
     * Retrieves the contents of a file
     * @todo Throw an exception if file doesn't exist
     */
    public function get()
    {
        return $this->fs->file_get_contents($this->name);
    }

    /** Writes contents to a file, creates new file if necessary */
    public function write($contents)
    {
        return $this->fs->file_put_contents($this->name, $contents);
    }

    /** Deletes the file */
    public function delete()
    {
        return $this->fs->unlink($this->name);
    }

    /** Returns true if file exists and is a file. */
    public function exists()
    {
        return $this->fs->is_file($this->name);
    }

    /** Returns last file modification time */
    public function getMTime()
    {
        return $this->fs->filemtime($this->name);
    }

    /**
     * Chmod a file
     * @note We ignore errors because of some weird owner trickery due
     *       to SVN duality
     */
    public function chmod($octal_code)
    {
        return @$this->fs->chmod($this->name, $octal_code);
    }

    /** Opens file's handle */
    public function open($mode)
    {
        if ($this->handle) $this->close();
        $this->handle = $this->fs->fopen($this->name, $mode);
        return true;
    }

    /** Closes file's handle */
    public function close()
    {
        if (!$this->handle) return false;
        $status = $this->fs->fclose($this->handle);
        $this->handle = false;
        return $status;
    }

    /** Retrieves a line from an open file, with optional max length $length */
    public function getLine($length = null)
    {
        if (!$this->handle) $this->open('r');
        if ($length === null) return $this->fs->fgets($this->handle);
        else return $this->fs->fgets($this->handle, $length);
    }

    /** Retrieves a character from an open file */
    public function getChar()
    {
        if (!$this->handle) $this->open('r');
        return $this->fs->fgetc($this->handle);
    }

    /** Retrieves an $length bytes of data from an open data */
    public function read($length)
    {
        if (!$this->handle) $this->open('r');
        return $this->fs->fread($this->handle, $length);
    }

    /** Writes to an open file */
    public function put($string)
    {
        if (!$this->handle) $this->open('a');
        return $this->fs->fwrite($this->handle, $string);
    }

    /** Returns TRUE if the end of the file has been reached */
    public function eof()
    {
        if (!$this->handle) return true;
        return $this->fs->feof($this->handle);
    }

    public function __destruct()
    {
        if ($this->handle) $this->close();
    }

}

// vim: et sw=4 sts=4


code:
<?php

/**
 * This is a stub include that automatically configures the include path.
 */

set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );
require_once 'HTMLPurifierExtras.php';
require_once 'HTMLPurifierExtras.autoload.php';

// vim: et sw=4 sts=4


code:
<?php

/**
 * @file
 * Legacy autoloader for systems lacking spl_autoload_register
 *
 * Must be separate to prevent deprecation warning on PHP 7.2
 */

function __autoload($class)
{
    return HTMLPurifierExtras::autoload($class);
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * @file
 * Convenience file that registers autoload handler for HTML Purifier.
 *
 * @warning
 *      This autoloader does not contain the compatibility code seen in
 *      HTMLPurifier_Bootstrap; the user is expected to make any necessary
 *      changes to use this library.
 */

if (function_exists('spl_autoload_register')) {
    spl_autoload_register(array('HTMLPurifierExtras', 'autoload'));
    if (function_exists('__autoload')) {
        // Be polite and ensure that userland autoload gets retained
        spl_autoload_register('__autoload');
    }
} elseif (!function_exists('__autoload')) {
    require dirname(__FILE__) . '/HTMLPurifierExtras.autoload-legacy.php';
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Meta-class for HTML Purifier's extra class hierarchies, similar to
 * HTMLPurifier_Bootstrap.
 */
class HTMLPurifierExtras
{

    public static function autoload($class)
    {
        $path = HTMLPurifierExtras::getPath($class);
        if (!$path) return false;
        require $path;
        return true;
    }

    public static function getPath($class)
    {
        if (
            strncmp('FSTools', $class, 7) !== 0 &&
            strncmp('ConfigDoc', $class, 9) !== 0
        ) return false;
        // Custom implementations can go here
        // Standard implementation:
        return str_replace('_', '/', $class) . '.php';
    }

}

// vim: et sw=4 sts=4


code:
HTML Purifier Extras
    The Method Behind The Madness!

The extras/ folder in HTML Purifier contains--you guessed it--extra things
for HTML Purifier.  Specifically, these are two extra libraries called
FSTools and ConfigSchema.  They're extra for a reason: you don't need them
if you're using HTML Purifier for normal usage: filtering HTML.  However,
if you're a developer, and would like to test HTML Purifier, or need to
use one of HTML Purifier's maintenance scripts, chances are they'll need
these libraries. Who knows: maybe you'll find them useful too!

Here are the libraries:

FSTools
-------

Short for File System Tools, this is a poor-man's object-oriented wrapper for
the filesystem. It currently consists of two classes:

- FSTools: This is a singleton that contains a manner of useful functions
  such as recursive glob, directory removal, etc, as well as the ability
  to call arbitrary native PHP functions through it like $FS->fopen(...).
  This makes it a lot simpler to mock these filesystem calls for unit testing.

- FSTools_File: This object represents a single file, and has almost any
  method imaginable one would need.

Check the files themselves for more information.

    vim: et sw=4 sts=4


code:
<?php

/**
 * This is a stub include that automatically configures the include path.
 */

set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );
require_once 'HTMLPurifier/Bootstrap.php';
require_once 'HTMLPurifier.autoload.php';

// vim: et sw=4 sts=4


code:
<?php

/**
 * @file
 * Legacy autoloader for systems lacking spl_autoload_register
 *
 * Must be separate to prevent deprecation warning on PHP 7.2
 */

function __autoload($class)
{
    return HTMLPurifier_Bootstrap::autoload($class);
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * @file
 * Convenience file that registers autoload handler for HTML Purifier.
 * It also does some sanity checks.
 */

if (function_exists('spl_autoload_register') && function_exists('spl_autoload_unregister')) {
    // We need unregister for our pre-registering functionality
    HTMLPurifier_Bootstrap::registerAutoload();
    if (function_exists('__autoload')) {
        // Be polite and ensure that userland autoload gets retained
        spl_autoload_register('__autoload');
    }
} elseif (!function_exists('__autoload')) {
    require dirname(__FILE__) . '/HTMLPurifier.autoload-legacy.php';
}

if (ini_get('zend.ze1_compatibility_mode')) {
    trigger_error("HTML Purifier is not compatible with zend.ze1_compatibility_mode; please turn it off", E_USER_ERROR);
}

// vim: et sw=4 sts=4


code:
<?php
if (!defined('HTMLPURIFIER_PREFIX')) {
    define('HTMLPURIFIER_PREFIX', dirname(__FILE__));
}


code:
<?php

/**
 * @file
 * Defines a function wrapper for HTML Purifier for quick use.
 * @note ''HTMLPurifier()'' is NOT the same as ''new HTMLPurifier()''
 */

/**
 * Purify HTML.
 * @param string $html String HTML to purify
 * @param mixed $config Configuration to use, can be any value accepted by
 *        HTMLPurifier_Config::create()
 * @return string
 */
function HTMLPurifier($html, $config = null)
{
    static $purifier = false;
    if (!$purifier) {
        $purifier = new HTMLPurifier();
    }
    return $purifier->purify($html, $config);
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * @file
 * This file was auto-generated by generate-includes.php and includes all of
 * the core files required by HTML Purifier. Use this if performance is a
 * primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
 * FILE, changes will be overwritten the next time the script is run.
 *
 * @version 4.12.0
 *
 * @warning
 *      You must *not* include any other HTML Purifier files before this file,
 *      because 'require' not 'require_once' is used.
 *
 * @warning
 *      This file requires that the include path contains the HTML Purifier
 *      library directory; this is not auto-set.
 */

require 'HTMLPurifier.php';
require 'HTMLPurifier/Arborize.php';
require 'HTMLPurifier/AttrCollections.php';
require 'HTMLPurifier/AttrDef.php';
require 'HTMLPurifier/AttrTransform.php';
require 'HTMLPurifier/AttrTypes.php';
require 'HTMLPurifier/AttrValidator.php';
require 'HTMLPurifier/Bootstrap.php';
require 'HTMLPurifier/Definition.php';
require 'HTMLPurifier/CSSDefinition.php';
require 'HTMLPurifier/ChildDef.php';
require 'HTMLPurifier/Config.php';
require 'HTMLPurifier/ConfigSchema.php';
require 'HTMLPurifier/ContentSets.php';
require 'HTMLPurifier/Context.php';
require 'HTMLPurifier/DefinitionCache.php';
require 'HTMLPurifier/DefinitionCacheFactory.php';
require 'HTMLPurifier/Doctype.php';
require 'HTMLPurifier/DoctypeRegistry.php';
require 'HTMLPurifier/ElementDef.php';
require 'HTMLPurifier/Encoder.php';
require 'HTMLPurifier/EntityLookup.php';
require 'HTMLPurifier/EntityParser.php';
require 'HTMLPurifier/ErrorCollector.php';
require 'HTMLPurifier/ErrorStruct.php';
require 'HTMLPurifier/Exception.php';
require 'HTMLPurifier/Filter.php';
require 'HTMLPurifier/Generator.php';
require 'HTMLPurifier/HTMLDefinition.php';
require 'HTMLPurifier/HTMLModule.php';
require 'HTMLPurifier/HTMLModuleManager.php';
require 'HTMLPurifier/IDAccumulator.php';
require 'HTMLPurifier/Injector.php';
require 'HTMLPurifier/Language.php';
require 'HTMLPurifier/LanguageFactory.php';
require 'HTMLPurifier/Length.php';
require 'HTMLPurifier/Lexer.php';
require 'HTMLPurifier/Node.php';
require 'HTMLPurifier/PercentEncoder.php';
require 'HTMLPurifier/PropertyList.php';
require 'HTMLPurifier/PropertyListIterator.php';
require 'HTMLPurifier/Queue.php';
require 'HTMLPurifier/Strategy.php';
require 'HTMLPurifier/StringHash.php';
require 'HTMLPurifier/StringHashParser.php';
require 'HTMLPurifier/TagTransform.php';
require 'HTMLPurifier/Token.php';
require 'HTMLPurifier/TokenFactory.php';
require 'HTMLPurifier/URI.php';
require 'HTMLPurifier/URIDefinition.php';
require 'HTMLPurifier/URIFilter.php';
require 'HTMLPurifier/URIParser.php';
require 'HTMLPurifier/URIScheme.php';
require 'HTMLPurifier/URISchemeRegistry.php';
require 'HTMLPurifier/UnitConverter.php';
require 'HTMLPurifier/VarParser.php';
require 'HTMLPurifier/VarParserException.php';
require 'HTMLPurifier/Zipper.php';
require 'HTMLPurifier/AttrDef/CSS.php';
require 'HTMLPurifier/AttrDef/Clone.php';
require 'HTMLPurifier/AttrDef/Enum.php';
require 'HTMLPurifier/AttrDef/Integer.php';
require 'HTMLPurifier/AttrDef/Lang.php';
require 'HTMLPurifier/AttrDef/Switch.php';
require 'HTMLPurifier/AttrDef/Text.php';
require 'HTMLPurifier/AttrDef/URI.php';
require 'HTMLPurifier/AttrDef/CSS/Number.php';
require 'HTMLPurifier/AttrDef/CSS/AlphaValue.php';
require 'HTMLPurifier/AttrDef/CSS/Background.php';
require 'HTMLPurifier/AttrDef/CSS/BackgroundPosition.php';
require 'HTMLPurifier/AttrDef/CSS/Border.php';
require 'HTMLPurifier/AttrDef/CSS/Color.php';
require 'HTMLPurifier/AttrDef/CSS/Composite.php';
require 'HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php';
require 'HTMLPurifier/AttrDef/CSS/Filter.php';
require 'HTMLPurifier/AttrDef/CSS/Font.php';
require 'HTMLPurifier/AttrDef/CSS/FontFamily.php';
require 'HTMLPurifier/AttrDef/CSS/Ident.php';
require 'HTMLPurifier/AttrDef/CSS/ImportantDecorator.php';
require 'HTMLPurifier/AttrDef/CSS/Length.php';
require 'HTMLPurifier/AttrDef/CSS/ListStyle.php';
require 'HTMLPurifier/AttrDef/CSS/Multiple.php';
require 'HTMLPurifier/AttrDef/CSS/Percentage.php';
require 'HTMLPurifier/AttrDef/CSS/TextDecoration.php';
require 'HTMLPurifier/AttrDef/CSS/URI.php';
require 'HTMLPurifier/AttrDef/HTML/Bool.php';
require 'HTMLPurifier/AttrDef/HTML/Nmtokens.php';
require 'HTMLPurifier/AttrDef/HTML/Class.php';
require 'HTMLPurifier/AttrDef/HTML/Color.php';
require 'HTMLPurifier/AttrDef/HTML/FrameTarget.php';
require 'HTMLPurifier/AttrDef/HTML/ID.php';
require 'HTMLPurifier/AttrDef/HTML/Pixels.php';
require 'HTMLPurifier/AttrDef/HTML/Length.php';
require 'HTMLPurifier/AttrDef/HTML/LinkTypes.php';
require 'HTMLPurifier/AttrDef/HTML/MultiLength.php';
require 'HTMLPurifier/AttrDef/URI/Email.php';
require 'HTMLPurifier/AttrDef/URI/Host.php';
require 'HTMLPurifier/AttrDef/URI/IPv4.php';
require 'HTMLPurifier/AttrDef/URI/IPv6.php';
require 'HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php';
require 'HTMLPurifier/AttrTransform/Background.php';
require 'HTMLPurifier/AttrTransform/BdoDir.php';
require 'HTMLPurifier/AttrTransform/BgColor.php';
require 'HTMLPurifier/AttrTransform/BoolToCSS.php';
require 'HTMLPurifier/AttrTransform/Border.php';
require 'HTMLPurifier/AttrTransform/EnumToCSS.php';
require 'HTMLPurifier/AttrTransform/ImgRequired.php';
require 'HTMLPurifier/AttrTransform/ImgSpace.php';
require 'HTMLPurifier/AttrTransform/Input.php';
require 'HTMLPurifier/AttrTransform/Lang.php';
require 'HTMLPurifier/AttrTransform/Length.php';
require 'HTMLPurifier/AttrTransform/Name.php';
require 'HTMLPurifier/AttrTransform/NameSync.php';
require 'HTMLPurifier/AttrTransform/Nofollow.php';
require 'HTMLPurifier/AttrTransform/SafeEmbed.php';
require 'HTMLPurifier/AttrTransform/SafeObject.php';
require 'HTMLPurifier/AttrTransform/SafeParam.php';
require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
require 'HTMLPurifier/AttrTransform/TargetBlank.php';
require 'HTMLPurifier/AttrTransform/TargetNoopener.php';
require 'HTMLPurifier/AttrTransform/TargetNoreferrer.php';
require 'HTMLPurifier/AttrTransform/Textarea.php';
require 'HTMLPurifier/ChildDef/Chameleon.php';
require 'HTMLPurifier/ChildDef/Custom.php';
require 'HTMLPurifier/ChildDef/Empty.php';
require 'HTMLPurifier/ChildDef/List.php';
require 'HTMLPurifier/ChildDef/Required.php';
require 'HTMLPurifier/ChildDef/Optional.php';
require 'HTMLPurifier/ChildDef/StrictBlockquote.php';
require 'HTMLPurifier/ChildDef/Table.php';
require 'HTMLPurifier/DefinitionCache/Decorator.php';
require 'HTMLPurifier/DefinitionCache/Null.php';
require 'HTMLPurifier/DefinitionCache/Serializer.php';
require 'HTMLPurifier/DefinitionCache/Decorator/Cleanup.php';
require 'HTMLPurifier/DefinitionCache/Decorator/Memory.php';
require 'HTMLPurifier/HTMLModule/Bdo.php';
require 'HTMLPurifier/HTMLModule/CommonAttributes.php';
require 'HTMLPurifier/HTMLModule/Edit.php';
require 'HTMLPurifier/HTMLModule/Forms.php';
require 'HTMLPurifier/HTMLModule/Hypertext.php';
require 'HTMLPurifier/HTMLModule/Iframe.php';
require 'HTMLPurifier/HTMLModule/Image.php';
require 'HTMLPurifier/HTMLModule/Legacy.php';
require 'HTMLPurifier/HTMLModule/List.php';
require 'HTMLPurifier/HTMLModule/Name.php';
require 'HTMLPurifier/HTMLModule/Nofollow.php';
require 'HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php';
require 'HTMLPurifier/HTMLModule/Object.php';
require 'HTMLPurifier/HTMLModule/Presentation.php';
require 'HTMLPurifier/HTMLModule/Proprietary.php';
require 'HTMLPurifier/HTMLModule/Ruby.php';
require 'HTMLPurifier/HTMLModule/SafeEmbed.php';
require 'HTMLPurifier/HTMLModule/SafeObject.php';
require 'HTMLPurifier/HTMLModule/SafeScripting.php';
require 'HTMLPurifier/HTMLModule/Scripting.php';
require 'HTMLPurifier/HTMLModule/StyleAttribute.php';
require 'HTMLPurifier/HTMLModule/Tables.php';
require 'HTMLPurifier/HTMLModule/Target.php';
require 'HTMLPurifier/HTMLModule/TargetBlank.php';
require 'HTMLPurifier/HTMLModule/TargetNoopener.php';
require 'HTMLPurifier/HTMLModule/TargetNoreferrer.php';
require 'HTMLPurifier/HTMLModule/Text.php';
require 'HTMLPurifier/HTMLModule/Tidy.php';
require 'HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
require 'HTMLPurifier/HTMLModule/Tidy/Name.php';
require 'HTMLPurifier/HTMLModule/Tidy/Proprietary.php';
require 'HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php';
require 'HTMLPurifier/HTMLModule/Tidy/Strict.php';
require 'HTMLPurifier/HTMLModule/Tidy/Transitional.php';
require 'HTMLPurifier/HTMLModule/Tidy/XHTML.php';
require 'HTMLPurifier/Injector/AutoParagraph.php';
require 'HTMLPurifier/Injector/DisplayLinkURI.php';
require 'HTMLPurifier/Injector/Linkify.php';
require 'HTMLPurifier/Injector/PurifierLinkify.php';
require 'HTMLPurifier/Injector/RemoveEmpty.php';
require 'HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php';
require 'HTMLPurifier/Injector/SafeObject.php';
require 'HTMLPurifier/Lexer/DOMLex.php';
require 'HTMLPurifier/Lexer/DirectLex.php';
require 'HTMLPurifier/Node/Comment.php';
require 'HTMLPurifier/Node/Element.php';
require 'HTMLPurifier/Node/Text.php';
require 'HTMLPurifier/Strategy/Composite.php';
require 'HTMLPurifier/Strategy/Core.php';
require 'HTMLPurifier/Strategy/FixNesting.php';
require 'HTMLPurifier/Strategy/MakeWellFormed.php';
require 'HTMLPurifier/Strategy/RemoveForeignElements.php';
require 'HTMLPurifier/Strategy/ValidateAttributes.php';
require 'HTMLPurifier/TagTransform/Font.php';
require 'HTMLPurifier/TagTransform/Simple.php';
require 'HTMLPurifier/Token/Comment.php';
require 'HTMLPurifier/Token/Tag.php';
require 'HTMLPurifier/Token/Empty.php';
require 'HTMLPurifier/Token/End.php';
require 'HTMLPurifier/Token/Start.php';
require 'HTMLPurifier/Token/Text.php';
require 'HTMLPurifier/URIFilter/DisableExternal.php';
require 'HTMLPurifier/URIFilter/DisableExternalResources.php';
require 'HTMLPurifier/URIFilter/DisableResources.php';
require 'HTMLPurifier/URIFilter/HostBlacklist.php';
require 'HTMLPurifier/URIFilter/MakeAbsolute.php';
require 'HTMLPurifier/URIFilter/Munge.php';
require 'HTMLPurifier/URIFilter/SafeIframe.php';
require 'HTMLPurifier/URIScheme/data.php';
require 'HTMLPurifier/URIScheme/file.php';
require 'HTMLPurifier/URIScheme/ftp.php';
require 'HTMLPurifier/URIScheme/http.php';
require 'HTMLPurifier/URIScheme/https.php';
require 'HTMLPurifier/URIScheme/mailto.php';
require 'HTMLPurifier/URIScheme/news.php';
require 'HTMLPurifier/URIScheme/nntp.php';
require 'HTMLPurifier/URIScheme/tel.php';
require 'HTMLPurifier/VarParser/Flexible.php';
require 'HTMLPurifier/VarParser/Native.php';


code:
<?php

/**
 * @file
 * Emulation layer for code that used kses(), substituting in HTML Purifier.
 */

require_once dirname(__FILE__) . '/HTMLPurifier.auto.php';

function kses($string, $allowed_html, $allowed_protocols = null)
{
    $config = HTMLPurifier_Config::createDefault();
    $allowed_elements = array();
    $allowed_attributes = array();
    foreach ($allowed_html as $element => $attributes) {
        $allowed_elements[$element] = true;
        foreach ($attributes as $attribute => $x) {
            $allowed_attributes["$element.$attribute"] = true;
        }
    }
    $config->set('HTML.AllowedElements', $allowed_elements);
    $config->set('HTML.AllowedAttributes', $allowed_attributes);
    if ($allowed_protocols !== null) {
        $config->set('URI.AllowedSchemes', $allowed_protocols);
    }
    $purifier = new HTMLPurifier($config);
    return $purifier->purify($string);
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * @file
 * Convenience stub file that adds HTML Purifier's library file to the path
 * without any other side-effects.
 */

set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );

// vim: et sw=4 sts=4