<?php

namespace Ekyna\Bundle\CoreBundle\Uploader;

use Ekyna\Bundle\CoreBundle\Exception\UploadException;
use Ekyna\Bundle\CoreBundle\Model\UploadableInterface;
use League\Flysystem\Adapter\AbstractFtpAdapter;
use League\Flysystem\MountManager;

/**
 * Class Uploader
 * @package Ekyna\Bundle\CoreBundle\Uploader
 * @author Étienne Dauvergne <contact@ekyna.com>
 */
class Uploader implements UploaderInterface
{
    /**
     * @var MountManager
     */
    protected $mountManager;

    /**
     * @var string
     */
    protected $targetFileSystem;

    /**
     * Constructor.
     *
     * @param MountManager $mountManager
     * @param string       $targetFs
     */
    public function __construct(MountManager $mountManager, $targetFs = 'local_upload')
    {
        $this->mountManager      = $mountManager;
        $this->targetFileSystem  = $targetFs;
    }

    /**
     * {@inheritdoc}
     */
    public function prepare(UploadableInterface $uploadable)
    {
        if ($uploadable->hasFile() || $uploadable->hasKey() || $uploadable->shouldBeRenamed()) {
            $uploadable->setOldPath($uploadable->getPath());
            $uploadable->setPath($this->generatePath($uploadable->guessFilename()));

            // Check source and set size
            // By File
            if ($uploadable->hasFile()) {
                $file = $uploadable->getFile();

                if (!file_exists($file->getRealPath())) {
                    throw new UploadException(sprintf('Source file "%s" does not exists.', $file->getRealPath()));
                }

                $uploadable->setSize(filesize($file->getRealPath()));

            // By Key
            } elseif ($uploadable->hasKey()) {
                $sourceKey = $uploadable->getKey();

                if (!$this->checkKey($sourceKey)) {
                    throw new UploadException(sprintf('Source file "%s" does not exists.', $sourceKey));
                }

                $uploadable->setSize($this->mountManager->getSize($sourceKey));
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function upload(UploadableInterface $uploadable)
    {
        if ($uploadable->hasPath()) {
            $targetKey = sprintf('%s://%s', $this->targetFileSystem, $uploadable->getPath());

            // By file
            if ($uploadable->hasFile()) {
                $file = $uploadable->getFile();

                if (false === $stream = fopen($file->getRealPath(), 'r+')) {
                    throw new UploadException(sprintf('Failed to open file "%s".', $file->getRealPath()));
                }

                if (!$this->mountManager->writeStream($targetKey, $stream)) {
                    throw new UploadException(sprintf('Failed to copy file from "%s" to "%s".', $file->getRealPath(), $targetKey));
                }

                fclose($stream);
                unlink($file->getRealPath());

                $uploadable->setFile(null);

            // By key
            } elseif ($uploadable->hasKey()) {
                $sourceKey = $uploadable->getKey();

                if (!$this->moveKey($sourceKey, $targetKey)) {
                    throw new UploadException(sprintf('Failed to copy file from "%s" to "%s".', $sourceKey, $targetKey));
                }

                $uploadable->setKey(null);

            // Rename
            } elseif ($uploadable->hasOldPath()) {
                $fs = $this->getFilesystem();

                if (!$fs->rename($uploadable->getOldPath(), $uploadable->getPath())) {
                    throw new UploadException(sprintf('Failed to rename file from "%s" to "%s".', $uploadable->getOldPath(), $uploadable->getPath()));
                }

                $this->cleanUp($uploadable->getOldPath());
                $uploadable->setOldPath(null);
            }
        }
    }

    /**
     * {@inheritdoc}
     */
    public function remove(UploadableInterface $uploadable)
    {
        if (0 < strlen($path = $uploadable->getOldPath())) {
            $fs = $this->getFilesystem();

            if (!$fs->has($path)) {
                throw new UploadException(sprintf('File "%s" not found.', $path));
            }
            if (!$fs->delete($path)) {
                throw new UploadException(sprintf('Failed to delete file "%s".', $path));
            }

            $this->cleanUp($path);
            $uploadable->setOldPath(null);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function generatePath($filename)
    {
        do {
            $hash = md5(uniqid(mt_rand()));
            $path = sprintf(
                '%s/%s/%s',
                substr($hash, 0, 3),
                substr($hash, 3, 3),
                $filename
            );
        } while ($this->mountManager->has(sprintf('%s://%s', $this->targetFileSystem, $path)));

        return $path;
    }

    /**
     * Removes the path directories if they are empty.
     *
     * @param string $path
     * @throws UploadException
     */
    private function cleanUp($path)
    {
        $fs = $this->getFilesystem();
        $parts = explode('/', $path);

        while (0 < count($parts)) {
            $key = implode('/', $parts);
            if ($fs->has($key)) {
                $dir = $fs->get($key);
                if (!$dir->isDir() || 0 < count($fs->listContents($key))) {
                    break;
                }
                if (!$fs->deleteDir($key)) {
                    throw new UploadException(sprintf('Failed to delete directory "%s".', $key));
                }
            }
            array_pop($parts);
        }
    }

    /**
     * Returns the target filesystem.
     *
     * @return \League\Flysystem\FilesystemInterface
     */
    private function getFilesystem()
    {
        return $this->mountManager->getFilesystem($this->targetFileSystem);
    }

    /**
     * Check the distant source key.
     *
     * @param string $sourceKey
     * @return bool
     */
    private function checkKey($sourceKey)
    {
        if ($this->mountManager->has($sourceKey)) {
            return true;
        }

        if ($this->reconnectDistantFs($sourceKey)) {
            if ($this->mountManager->has($sourceKey)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Copies or moves the distant source key to target key.
     *
     * @param string $sourceKey
     * @param string $targetKey
     * @return bool
     */
    private function moveKey($sourceKey, $targetKey)
    {
        if (!$this->isDistant($sourceKey)) {
            return $this->mountManager->move($sourceKey, $targetKey);
        }

        if ($this->mountManager->copy($sourceKey, $targetKey)) {
            return true;
        }
        if ($this->reconnectDistantFs($sourceKey)) {
            if ($this->mountManager->copy($sourceKey, $targetKey)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Reconnects the file system adapter if possible.
     *
     * @param string $key
     * @return bool
     */
    private function reconnectDistantFs($key)
    {
        /** @noinspection PhpUnusedLocalVariableInspection */
        list($prefix, $args) = $this->mountManager->filterPrefix([$key]);

        /** @var \League\Flysystem\FileSystem $fs */
        $fs = $this->mountManager->getFilesystem($prefix);
        $adapter = $fs->getAdapter();

        // Try reconnection
        if (!$adapter instanceof AbstractFtpAdapter) {
            return false;
        }

        $adapter->disconnect();
        sleep(1);
        $adapter->connect();

        return true;
    }

    /**
     * Returns whether the key is distant.
     *
     * @param string $key
     * @return bool
     */
    private function isDistant($key)
    {
        /** @noinspection PhpUnusedLocalVariableInspection */
        list($prefix, $args) = $this->mountManager->filterPrefix([$key]);

        /** @var \League\Flysystem\FileSystem $fs */
        $fs = $this->mountManager->getFilesystem($prefix);
        $adapter = $fs->getAdapter();

        // Try reconnection
        if ($adapter instanceof AbstractFtpAdapter) {
            return true;
        }

        return false;
    }
}


<?php
/**
 * This file is part of graze/array-filter
 *
 * Copyright (c) 2016 Nature Delivered Ltd. <https://www.graze.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @license https://github.com/graze/array-filter/blob/master/LICENSE.md
 * @link    https://github.com/graze/array-filter
 */

namespace Graze\ArrayFilter\Factory;

use Graze\ArrayFilter\AllOfFilter;
use Graze\ArrayFilter\ArrayFilterInterface;
use Graze\ArrayFilter\ClosureFilter;
use Graze\ArrayFilter\Exception\UnknownPropertyDefinitionException;

/**
 * Factory class to interpret string filter configuration into different configurations
 *
 * Currently supports:
 *
 * ```
 * [
 *     'param' => 'value',    // $data['param'] == 'value';
 *     'param =' => 'value',  // $data['param'] == 'value';
 *     'param ~' => 'value',  // preg_match($value, $data['param']);
 *     'param >' => 'value',  // $data['param'] > 'value';
 *     'param >=' => 'value', // $data['param'] >= 'value';
 *     'param <' => 'value',  // $data['param'] < 'value';
 *     'param <=' => 'value', // $data['param'] <= 'value';
 *     'param !=' => 'value', // $data['param'] != 'value';
 *     'param <>' => 'value', // $data['param'] != 'value';
 *     'param in' => ['value1','value2'], // in_array($data['param'], ['value1','value2'])
 * ]
 * ```
 */
class FilterFactory implements FilterFactoryInterface
{
    /**
     * @var array
     */
    protected $definitions;

    /**
     * @var ValueFactoryInterface
     */
    protected $valueFactory;

    /**
     * @var FilterFactory
     */
    static private $instance = null;

    /**
     * Build the definitions
     *
     * @param ValueFactoryInterface $valueFactory
     */
    public function __construct(ValueFactoryInterface $valueFactory)
    {
        $this->valueFactory = $valueFactory;
        $this->definitions = [
            '/^(\w+)(\s*=)?$/i'    => function ($property, $expected) {
                return new ClosureFilter($property, function ($actual) use ($expected) {
                    return $actual == $expected;
                });
            },
            '/^(\w+)\s*~=?$/i'     => function ($property, $expected) {
                return new ClosureFilter($property, function ($actual) use ($expected) {
                    return preg_match($expected, $actual);
                });
            },
            '/^(\w+)\s*>$/i'       => function ($property, $expected) {
                return new ClosureFilter($property, function ($actual) use ($expected) {
                    return $actual > $expected;
                });
            },
            '/^(\w+)\s*>=$/i'      => function ($property, $expected) {
                return new ClosureFilter($property, function ($actual) use ($expected) {
                    return $actual >= $expected;
                });
            },
            '/^(\w+)\s*<$/i'       => function ($property, $expected) {
                return new ClosureFilter($property, function ($actual) use ($expected) {
                    return $actual < $expected;
                });
            },
            '/^(\w+)\s*<=$/i'      => function ($property, $expected) {
                return new ClosureFilter($property, function ($actual) use ($expected) {
                    return $actual <= $expected;
                });
            },
            '/^(\w+)\s*(<>|!=)$/i' => function ($property, $expected) {
                return new ClosureFilter($property, function ($actual) use ($expected) {
                    return $actual != $expected;
                });
            },
            '/^(\w+)\s*in$/i'      => function ($property, $expected) {
                return new ClosureFilter($property, function ($actual) use ($expected) {
                    return in_array($actual, $expected);
                });
            },
        ];
    }

    /**
     * @param array $configuration
     *
     * @return ArrayFilterInterface
     */
    public function createFilters(array $configuration)
    {
        $filters = [];
        foreach ($configuration as $property => $value) {
            $expected = is_string($value) ? $this->valueFactory->parseValue($value) : $value;
            $filters[] = $this->getFilter($property, $expected);
        }

        return new AllOfFilter($filters);
    }

    /**
     * @param string $property
     * @param mixed  $value
     *
     * @return ArrayFilterInterface
     * @throws UnknownPropertyDefinitionException
     */
    public function getFilter($property, $value)
    {
        foreach ($this->definitions as $key => $definition) {
            if (preg_match($key, $property, $matches)) {
                $expected = is_string($value) ? $this->valueFactory->parseValue($value) : $value;
                return call_user_func($definition, $matches[1], $expected);
            }
        }

        throw new UnknownPropertyDefinitionException($property);
    }

    /**
     * @param array $configuration
     *
     * @return ArrayFilterInterface
     */
    public static function fromConfiguration(array $configuration)
    {
        return self::getInstance()->createFilters($configuration);
    }

    /**
     * @param string $property
     * @param mixed  $value
     *
     * @return ArrayFilterInterface
     * @throws UnknownPropertyDefinitionException
     */
    public static function filter($property, $value)
    {
        return self::getInstance()->getFilter($property, $value);
    }

    /**
     * @return FilterFactory
     */
    private static function getInstance()
    {
        if (is_null(self::$instance)) {
            self::$instance = new FilterFactory(new ValueFactory());
        }
        return self::$instance;
    }
}


<?php

/*
 * This file is part of the Valkyrja framework.
 *
 * (c) Melech Mizrachi
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Valkyrja\ORM\Repositories;

use InvalidArgumentException;
use PDO;
use PDOStatement;
use Valkyrja\ORM\Entity;
use Valkyrja\ORM\EntityManager;
use Valkyrja\ORM\Enums\OrderBy;
use Valkyrja\ORM\Enums\PropertyMap;
use Valkyrja\ORM\Enums\PropertyType;
use Valkyrja\ORM\Exceptions\ExecuteException;
use Valkyrja\ORM\Exceptions\InvalidEntityException;
use Valkyrja\ORM\QueryBuilder;
use Valkyrja\ORM\Repository;

/**
 * Class PDORepository.
 */
class PDORepository implements Repository
{
    /**
     * The entity manager.
     *
     * @var EntityManager
     */
    protected $entityManager;

    /**
     * The entity to use.
     *
     * @var string|\Valkyrja\ORM\Entity
     */
    protected $entity;

    /**
     * The table to use.
     *
     * @var string
     */
    protected $table;

    /**
     * The PDO Store.
     *
     * @var PDO
     */
    protected $store;

    /**
     * MySQLRepository constructor.
     *
     * @param EntityManager $entityManager
     * @param string        $entity
     *
     * @throws InvalidArgumentException
     */
    public function __construct(EntityManager $entityManager, string $entity)
    {
        $this->entityManager = $entityManager;
        $this->entity        = $entity;
        $this->table         = $this->entity::getTable();
        $this->store         = $this->entityManager->store();
    }

    /**
     * Get the store.
     *
     * @return PDO
     */
    public function store(): PDO
    {
        return $this->store;
    }

    /**
     * Find a single entity given its id.
     *
     * @param string|int $id
     * @param bool|null  $getRelations
     *
     * @throws InvalidArgumentException If id is not a string or int
     *
     * @return \Valkyrja\ORM\Entity|null
     */
    public function find($id, bool $getRelations = null): ? Entity
    {
        if (! \is_string($id) && ! \is_int($id)) {
            throw new InvalidArgumentException('ID should be an int or string only.');
        }

        return $this->findBy(
                [$this->entity::getIdField() => $id],
                null,
                null,
                null,
                null,
                $getRelations
            )[0]
            ?? null;
    }

    /**
     * Find entities by given criteria.
     *
     *
     *      $repository
     *          ->findBy(
     *              [
     *                  'column'  => 'value',
     *                  'column2' => 'value2',
     *              ],
     *              [
     *                  'column'
     *                  'column2' => OrderBy::ASC,
     *                  'column3' => OrderBy::DESC,
     *              ],
     *              1,
     *              1
     *          )
     *
     *
     * @param array      $criteria
     * @param array|null $orderBy
     * @param int|null   $limit
     * @param int|null   $offset
     * @param array|null $columns
     * @param bool|null  $getRelations
     *
     * @throws InvalidArgumentException
     *
     * @return \Valkyrja\ORM\Entity[]
     */
    public function findBy(
        array $criteria,
        array $orderBy = null,
        int $limit = null,
        int $offset = null,
        array $columns = null,
        bool $getRelations = null
    ): array {
        return $this->select($columns, $criteria, $orderBy, $limit, $offset, $getRelations);
    }

    /**
     * Find entities by given criteria.
     *
     *
     *      $repository
     *          ->findBy(
     *              [
     *                  'column'
     *                  'column2' => OrderBy::ASC,
     *                  'column3' => OrderBy::DESC,
     *              ]
     *          )
     *
     *
     * @param array      $orderBy
     * @param array|null $columns
     * @param bool|null  $getRelations
     *
     * @throws InvalidArgumentException
     *
     * @return \Valkyrja\ORM\Entity[]
     */
    public function findAll(array $orderBy = null, array $columns = null, bool $getRelations = null): array
    {
        return $this->findBy([], $orderBy, null, null, $columns, $getRelations);
    }

    /**
     * Count all the results of given criteria.
     *
     *
     *      $repository
     *          ->count(
     *              [
     *                  'column'  => 'value',
     *                  'column2' => 'value2',
     *              ]
     *          )
     *
     *
     * @param array $criteria
     *
     * @throws InvalidArgumentException
     *
     * @return int
     */
    public function count(array $criteria): int
    {
        return (int) $this->select(['COUNT(*)'], $criteria);
    }

    /**
     * Create a new model.
     *
     *
     *      $this->create(Entity::class)
     *
     *
     * @param \Valkyrja\ORM\Entity $entity
     *
     * @throws ExecuteException
     * @throws InvalidArgumentException
     * @throws InvalidEntityException
     *
     * @return bool
     */
    public function create(Entity $entity): bool
    {
        $this->validateEntity($entity);

        return $this->saveCreateDelete('insert', $entity);
    }

    /**
     * Save an existing model given criteria to find. If no criteria specified uses all model properties.
     *
     *
     *      $this->save(Entity::class)
     *
     *
     * @param \Valkyrja\ORM\Entity $entity
     *
     * @throws ExecuteException
     * @throws InvalidArgumentException
     * @throws InvalidEntityException
     *
     * @return bool
     */
    public function save(Entity $entity): bool
    {
        $this->validateEntity($entity);

        return $this->saveCreateDelete('update', $entity);
    }

    /**
     * Delete an existing model.
     *
     *
     *      $this->delete(Entity::class)
     *
     *
     * @param \Valkyrja\ORM\Entity $entity
     *
     * @throws ExecuteException
     * @throws InvalidArgumentException
     * @throws InvalidEntityException
     *
     * @return bool
     */
    public function delete(Entity $entity): bool
    {
        $this->validateEntity($entity);

        return $this->saveCreateDelete('delete', $entity);
    }

    /**
     * Get the last inserted id.
     *
     * @return string
     */
    public function lastInsertId(): string
    {
        return $this->store->lastInsertId();
    }

    /**
     * Validate the passed entity.
     *
     * @param Entity $entity
     *
     * @throws InvalidEntityException
     *
     * @return void
     */
    protected function validateEntity(Entity $entity): void
    {
        if (! ($entity instanceof $this->entity)) {
            throw new InvalidEntityException(
                'This repository expects entities to be instances of '
                . $this->entity
                . '. Entity instanced from '
                . \get_class($entity)
                . ' provided instead.'
            );
        }
    }

    /**
     * Get a column param from a column name.
     *
     * @param string $column
     *
     * @return string
     */
    protected function columnParam(string $column): string
    {
        return ':' . $column;
    }

    /**
     * Get a criterion param from a criterion name.
     *
     * @param string $criterion
     *
     * @return string
     */
    protected function criterionParam(string $criterion): string
    {
        return ':criterion_' . $criterion;
    }

    /**
     * Select entities by given criteria.
     *
     *
     *      $this->select(
     *              [
     *                  'column',
     *                  'column2',
     *              ],
     *              [
     *                  'column'  => 'value',
     *                  'column2' => 'value2',
     *              ],
     *              [
     *                  'column'
     *                  'column2' => OrderBy::ASC,
     *                  'column3' => OrderBy::DESC,
     *              ],
     *              1,
     *              1
     *          )
     *
     *
     * @param array|null $columns
     * @param array|null $criteria
     * @param array|null $orderBy
     * @param int|null   $limit
     * @param int|null   $offset
     * @param bool|null  $getRelations
     *
     * @throws InvalidArgumentException
     *
     * @return \Valkyrja\ORM\Entity[]|int
     */
    protected function select(
        array $columns = null,
        array $criteria = null,
        array $orderBy = null,
        int $limit = null,
        int $offset = null,
        bool $getRelations = null
    ) {
        // Build the query
        $query = $this->selectQueryBuilder($columns, $criteria, $orderBy, $limit, $offset);

        // Create a new PDO statement from the query builder
        $stmt = $this->store->prepare($query->getQuery());

        // Iterate through the criteria once more
        foreach ($criteria as $column => $criterion) {
            // If the criterion is null
            if ($criterion === null) {
                // Skip as we've already set the where to IS NULL
                continue;
            }

            // If the criterion is an array
            if (\is_array($criterion)) {
                // Iterate through the criterion and bind each value individually
                foreach ($criterion as $index => $criterionItem) {
                    $stmt->bindValue($this->columnParam($column . $index), $criterionItem);
                }

                continue;
            }

            // And bind each value to the column
            $stmt->bindValue($this->columnParam($column), $criterion);
        }

        // Execute the PDO statement
        $stmt->execute();

        // Get all the results from the PDO statement
        $rows    = $stmt->fetchAll(PDO::FETCH_ASSOC);
        $results = [];

        // If the result of the query was a count
        if (isset($rows[0]['COUNT(*)'])) {
            return (int) $rows[0]['COUNT(*)'];
        }

        // Iterate through the rows found
        foreach ($rows as $row) {
            // Create a new model
            /** @var \Valkyrja\ORM\Entity $entity */
            $entity = new $this->entity();
            // Apply the model's contents given the row
            $entity->fromArray($row);

            // If no columns were specified then we can safely get all the relations
            if (null === $columns && $getRelations === true) {
                // Add the model to the final results
                $results[] = $this->getEntityRelations($entity);
            } else {
                // Add the model to the final results
                $results[] = $entity;
            }
        }

        return $results;
    }

    /**
     * Build a select query statement by given criteria.
     *
     *
     *      $this->queryBuilder(
     *              [
     *                  'column',
     *                  'column2',
     *              ],
     *              [
     *                  'column'  => 'value',
     *                  'column2' => 'value2',
     *              ],
     *              [
     *                  'column'
     *                  'column2' => OrderBy::ASC,
     *                  'column3' => OrderBy::DESC,
     *              ],
     *              1,
     *              1
     *          )
     *
     *
     * @param array|null $columns
     * @param array|null $criteria
     * @param array|null $orderBy
     * @param int|null   $limit
     * @param int|null   $offset
     *
     * @return QueryBuilder
     */
    protected function selectQueryBuilder(
        array $columns = null,
        array $criteria = null,
        array $orderBy = null,
        int $limit = null,
        int $offset = null
    ): QueryBuilder {
        // Create a new query
        $query = $this->entityManager
            ->getQueryBuilder()
            ->select($columns)
            ->table($this->table);

        // If criteria has been passed
        if (null !== $criteria) {
            $this->setCriteriaInQuery($query, $criteria);
        }

        // If order by has been passed
        if (null !== $orderBy) {
            $this->setOrderByInQuery($query, $orderBy);
        }

        // If a limit is passed
        if (null !== $limit) {
            $this->setLimitInQuery($query, $limit);
        }

        // If an offset is passed
        if (null !== $offset) {
            $this->setOffsetInQuery($query, $offset);
        }

        return $query;
    }

    /**
     * Set the criteria in the query builder.
     *
     * @param QueryBuilder $query
     * @param array        $criteria
     *
     * @return void
     */
    protected function setCriteriaInQuery(QueryBuilder $query, array $criteria): void
    {
        // Iterate through each criteria and set the column = :column
        // so we can use bindColumn() in PDO later
        foreach ($criteria as $column => $criterion) {
            // If the criterion is null
            if ($criterion === null) {
                $this->setNullCriterionInQuery($query, $column);

                continue;
            }

            // If the criterion is an array
            if (\is_array($criterion)) {
                $this->setArrayCriterionInQuery($query, $column, $criterion);

                continue;
            }

            // If the criterion has a percent at the start or the end
            if ($criterion[0] === '%' || $criterion[\strlen($criterion) - 1] === '%') {
                $this->setLikeCriterionInQuery($query, $column);

                continue;
            }

            $this->setEqualCriterionInQuery($query, $column);
        }
    }

    /**
     * Set a null criterion in the query builder.
     *
     * @param QueryBuilder $query
     * @param string       $column
     *
     * @return void
     */
    protected function setNullCriterionInQuery(QueryBuilder $query, string $column): void
    {
        $query->where($column . ' IS NULL');
    }

    /**
     * Set an array criterion in the query builder.
     *
     * @param QueryBuilder $query
     * @param string       $column
     * @param array        $criterion
     *
     * @return void
     */
    protected function setArrayCriterionInQuery(QueryBuilder $query, string $column, array $criterion): void
    {
        $criterionConcat = '';
        $lastIndex       = \count($criterion) - 1;

        // Iterate through the criterion and set each item individually to be bound later
        foreach ($criterion as $index => $criterionItem) {
            $criterionConcat .= $this->columnParam($column . $index);

            // If this is not the last index, add a comma
            if ($index < $lastIndex) {
                $criterionConcat .= ',';
            }
        }

        // Set the where statement as an in
        $query->where($column . ' IN (' . $criterionConcat . ')');
    }

    /**
     * Set a like where statement for a criterion/column in the query builder.
     *
     * @param QueryBuilder $query
     * @param string       $column
     *
     * @return void
     */
    protected function setLikeCriterionInQuery(QueryBuilder $query, string $column): void
    {
        $query->where($column . ' LIKE ' . $this->columnParam($column));
    }

    /**
     * Set an equal where statement for a criterion/column in the query builder.
     *
     * @param QueryBuilder $query
     * @param string       $column
     *
     * @return void
     */
    protected function setEqualCriterionInQuery(QueryBuilder $query, string $column): void
    {
        $query->where($column . ' = ' . $this->columnParam($column));
    }

    /**
     * Set the order by options in a query builder.
     *
     * @param QueryBuilder $query
     * @param array        $orderBy
     *
     * @return void
     */
    protected function setOrderByInQuery(QueryBuilder $query, array $orderBy): void
    {
        // Iterate through each order by
        foreach ($orderBy as $column => $order) {
            // Switch through the order (value) set
            switch ($order) {
                // If the order is ascending
                case OrderBy::ASC:
                    // Set the column via the orderByAsc method
                    $query->orderByAsc($column);
                    break;
                // If the order is descending
                case OrderBy::DESC:
                    // Set the column via the orderByDesc method
                    $query->orderByDesc($column);
                    break;
                default:
                    // Otherwise set the order (which is the column)
                    $query->orderBy($order);
                    break;
            }
        }
    }

    /**
     * Set the limit in the query builder.
     *
     * @param QueryBuilder $query
     * @param int          $limit
     *
     * @return void
     */
    protected function setLimitInQuery(QueryBuilder $query, int $limit): void
    {
        // Set it in the query
        $query->limit($limit);
    }

    /**
     * Set the offset in the query builder.
     *
     * @param QueryBuilder $query
     * @param int          $offset
     *
     * @return void
     */
    protected function setOffsetInQuery(QueryBuilder $query, int $offset): void
    {
        // Set it in the query
        $query->offset($offset);
    }

    /**
     * Save or create or delete a row.
     *
     *
     *      $this
     *          ->saveOrCreate(
     *             'update' | 'insert' | 'delete',
     *              Entity::class
     *          )
     *
     *
     * @param string $type
     * @param Entity $entity
     *
     * @throws ExecuteException
     * @throws InvalidArgumentException
     *
     * @return int
     */
    protected function saveCreateDelete(string $type, Entity $entity): int
    {
        if (! $this->store->inTransaction()) {
            $this->store->beginTransaction();
        }

        // Create a new query
        $query      = $this->entityManager
            ->getQueryBuilder()
            ->table($this->table)
            ->{$type}();
        $idField    = $entity::getIdField();
        $properties = $entity->forDataStore();

        /* @var QueryBuilder $query */

        // If this is an insert
        if ($type === 'insert') {
            // Ensure all the required properties are set
            $this->ensureRequiredProperties($entity, $properties);
        }

        // If this type isn't an insert
        if ($type !== 'insert') {
            // Set the id for the where clause
            $query->where($idField . ' = ' . $this->criterionParam($idField));
        }

        if ($type !== 'delete') {
            // Set the properties
            $this->setPropertiesForSaveCreateDeleteQuery($query, $properties);
        }

        // Prepare a PDO statement with the query
        $stmt = $this->store->prepare($query->getQuery());

        // If this type isn't an insert
        if ($type !== 'insert') {
            // Set the id value for the where clause
            $stmt->bindValue($this->criterionParam($idField), $properties[$idField]);
        }

        if ($type !== 'delete') {
            // Set the properties.
            $this->setPropertiesForSaveCreateDeleteStatement($stmt, $properties);
        }

        // If the execute failed
        if (! $executeResult = $stmt->execute()) {
            // Throw a fail exception
            throw new ExecuteException($stmt->errorInfo()[2]);
        }

        return $executeResult;
    }

    /**
     * Set properties for save, delete, or create queries.
     *
     * @param QueryBuilder $query
     * @param array        $properties
     *
     * @return void
     */
    protected function setPropertiesForSaveCreateDeleteQuery(QueryBuilder $query, array $properties): void
    {
        // Iterate through the properties
        foreach ($properties as $column => $property) {
            if ($property === null) {
                continue;
            }

            // Set the column and param name
            $query->set($column, $this->columnParam($column));
        }
    }

    /**
     * Set properties for save, create, or delete statements.
     *
     * @param PDOStatement $statement
     * @param array        $properties
     *
     * @return void
     */
    protected function setPropertiesForSaveCreateDeleteStatement(PDOStatement $statement, array $properties): void
    {
        // Iterate through the properties
        foreach ($properties as $column => $property) {
            if ($property === null) {
                continue;
            }

            // If the property is an object, then serialize it
            if (\is_object($property)) {
                $property = \serialize($property);
            } // Otherwise json encode if its an array
            elseif (\is_array($property)) {
                $property = \json_encode($property);
            }

            $type = PDO::PARAM_STR;

            if (\is_int($property) || \is_bool($property)) {
                $type     = PDO::PARAM_INT;
                $property = (int) $property;
            }

            // Bind each column's value to the statement
            $statement->bindValue($this->columnParam($column), $property, $type);
        }
    }

    /**
     * Get an entity with all its relations.
     *
     * @param Entity $entity
     *
     * @return Entity
     */
    protected function getEntityRelations(Entity $entity): Entity
    {
        $propertyTypes  = $entity::getPropertyTypes();
        $propertyMapper = $entity->getPropertyMapper();

        // Iterate through the property types
        foreach ($propertyTypes as $property => $type) {
            $entityName  = \is_array($type) ? $type[0] : $type;
            $propertyMap = $propertyMapper[$property] ?? null;

            if (null !== $propertyMap && (\is_array($type) || ! PropertyType::isValid($type))) {
                $repository   = $this->entityManager->getRepository($entityName);
                $orderBy      = $propertyMap[PropertyMap::ORDER_BY] ?? null;
                $limit        = $propertyMap[PropertyMap::LIMIT] ?? null;
                $offset       = $propertyMap[PropertyMap::OFFSET] ?? null;
                $columns      = $propertyMap[PropertyMap::COLUMNS] ?? null;
                $getRelations = $propertyMap[PropertyMap::GET_RELATIONS] ?? true;

                unset(
                    $propertyMap[PropertyMap::ORDER_BY],
                    $propertyMap[PropertyMap::LIMIT],
                    $propertyMap[PropertyMap::OFFSET],
                    $propertyMap[PropertyMap::COLUMNS],
                    $propertyMap[PropertyMap::GET_RELATIONS]
                );

                $entities = $repository->findBy($propertyMap, $orderBy, $limit, $offset, $columns, $getRelations);

                if (\is_array($type)) {
                    $entity->{$property} = $entities;

                    continue;
                }

                if (empty($entities)) {
                    continue;
                }

                $entity->{$property} = $entities[0];
            }
        }

        return $entity;
    }

    /**
     * Ensure all required properties have been passed.
     *
     * @param Entity $entity
     * @param array  $properties
     *
     * @throws InvalidArgumentException
     *
     * @return void
     */
    protected function ensureRequiredProperties(Entity $entity, array $properties): void
    {
        // Iterate through the required properties
        foreach ($entity::getRequiredProperties() as $requiredProperty) {
            // If the required property is not set
            if (! isset($properties[$requiredProperty])) {
                // Throw an exception
                throw new InvalidArgumentException('Missing required property: ' . $requiredProperty);
            }
        }
    }
}


<?php declare(strict_types=1); namespace BapCat\Tailor;

use BapCat\Hashing\Algorithms\Sha1WeakHasher;
use BapCat\Nom\Pipeline;
use BapCat\Nom\TemplateNotFoundException;
use BapCat\Persist\Directory;
use BapCat\Persist\File;
use BapCat\Persist\NotAFileException;

class Generator {
  /** @var Directory $templates */
  private $templates;

  /** @var Directory $cache */
  private $cache;

  /** @var Pipeline $pipeline */
  private $pipeline;

  /** @var DefaultHashGenerator $hasher */
  private $hasher;

  public function __construct(Directory $templates, Directory $cache, Pipeline $pipeline, ?HashGenerationStrategy $hasher = null) {
    $this->templates = $templates;
    $this->cache     = $cache;
    $this->pipeline  = $pipeline;
    $this->hasher    = $hasher ?: new DefaultHashGenerator(new Sha1WeakHasher());
  }

  /**
   * @param  string   $template
   * @param  mixed[]  $params
   *
   * @return  File  The cached compiled template
   *
   * @throws  TemplateNotFoundException
   * @throws  NotAFileException
   */
  public function generate(string $template, array $params = []): File {
    $template = $this->templates->child["$template.php"];

    if(!($template instanceof File)) {
      throw new NotAFileException($template->path);
    }

    $hash = $this->makeTemplateHash($template, $params);

    $compiled_file = $this->cache->child["$hash.php"];

    if(!$compiled_file->exists) {
      $compiled_code = $this->pipeline->compile($template, $params);
      $compiled_file = $this->cache->child["$hash.php"];
      $compiled_file->writeAll($compiled_code);
    }

    return $compiled_file;
  }

  public function includeFile(File $compiled): void {
    if((include $compiled->makeLocal()->full_path) === false) {
      //@TODO
      throw new \Exception("Could not include [{$compiled->path}]");
    }
  }

  private function makeTemplateHash(File $template, array $params): string {
    return $template->name . '.' . $this->hasher->generateHash($template, $params);
  }
}


<?php

namespace Steem;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;

class Steemit
{

    /**
     * Domain
     *
     * @var string
     */
    private $domain = 'https://v2.steemconnect.com/api';

    /**
     * Access Token
     *
     * @var string
     */
    private $token;

    /**
     * Object of operations
     *
     * @var Operations
     */
    private $operations;

    /**
     * Steemit constructor
     */
    public function __construct()
    {
        $this->operations = new Operations();
    }

    /**
     * Return headers
     *
     * @param  array $options Header array
     * @return array
     */
    private function getHeaders(array $options = [])
    {
        $headears = array_merge([
            'Content-Type'  => 'application/json',
            'Accept'        => 'application/json',
            'Authorization' => "Bearer $this->token"
        ], $options);

        return $headears;
    }

    /**
     * Create client via Guzzle!
     *
     * @return Client
     */
    private function getClient()
    {
        return new Client();
    }

    /**
     * Set access token
     *
     * @param string $token
     * @return $this
     */
    public function setToken($token)
    {
        $this->token = $token;

        return $this;
    }

    /**
     * Return access token
     *
     * @return string
     */
    public function getToken()
    {
        return $this->token;
    }

    /**
     * Set domain
     *
     * @param  string $domain
     *
     * @return $this
     */
    public function setDomain($domain)
    {
        $this->domain = $domain;

        return $this;
    }

    /**
     * Execute broadcast operations
     *
     * @param array  $body
     * @return array
     */
    private function broadcast($body)
    {
        $method   = 'POST';
        $url      = "{$this->domain}/broadcast";
        $headears = $this->getHeaders();
        $body     = json_encode($body);

        try {
            $request = $this->getClient()->request($method, $url, [
                'headers' => $headears,
                'body'    => $body
            ]);

            $response = $request->getBody()->getContents();

            return json_decode($response, true);

        } catch (RequestException $e) {
            $response = $e->getResponse()->getBody()->getContents();
            $response = json_decode($response, true);
            return $response;
        } catch (\Exception $e) {
            return ['error' => $e->getCode(), 'error_description' => $e->getMessage()];
        }
    }

    /**
     * Execute an operation to broadcast!
     *
     * @param  string $do     Name of the operation
     * @param  array  $params Required parameters
     * @return array
     */
    public function exec($do, array $params)
    {
        $body = call_user_func_array(array($this->operations, $do), $params);

        return $this->broadcast($body);
    }
}


<?php namespace Ordercloud\Requests\Handlers;

trait IdentifyByIdTrait
{
    /**
     * @param array|int[] $ids
     *
     * @return array
     */
    public function identifyAllById(array $ids = null)
    {
        if (is_null($ids)) return null;

        $mappedIds = array_map([$this, 'identifyById'], $ids);

        return array_filter($mappedIds);
    }

    /**
     * @param int $id
     *
     * @return array|null
     */
    public function identifyById($id)
    {
        if (is_null($id)) return null;

        return ['id' => $id];
    }
}


<?php namespace Arcanedev\NoCaptcha;

use Arcanedev\NoCaptcha\Utilities\Attributes;
use Arcanedev\NoCaptcha\Utilities\Request;
use Psr\Http\Message\ServerRequestInterface;

/**
 * Class     NoCaptcha
 *
 * @package  Arcanedev\NoCaptcha
 * @author   ARCANEDEV <arcanedev.maroc@gmail.com>
 */
class NoCaptcha implements Contracts\NoCaptcha
{
    /* ------------------------------------------------------------------------------------------------
     |  Constants
     | ------------------------------------------------------------------------------------------------
     */
    const CLIENT_URL   = 'https://www.google.com/recaptcha/api.js';
    const VERIFY_URL   = 'https://www.google.com/recaptcha/api/siteverify';
    const CAPTCHA_NAME = 'g-recaptcha-response';

    /* ------------------------------------------------------------------------------------------------
     |  Properties
     | ------------------------------------------------------------------------------------------------
     */
    /**
     * The shared key between your site and ReCAPTCHA
     *
     * @var string
     */
    private $secret;

    /**
     * Your site key
     *
     * @var string
     */
    private $siteKey;

    /**
     * Forces the widget to render in a specific language.
     * Auto-detects the user's language if unspecified.
     *
     * @var string
     */
    protected $lang;

    /**
     * Decides if we've already loaded the script file or not.
     *
     * @param bool
     */
    protected $scriptLoaded = false;

    /**
     * HTTP Request Client
     *
     * @var \Arcanedev\NoCaptcha\Contracts\Utilities\RequestInterface
     */
    protected $request;

    /**
     * noCaptcha Attributes
     *
     * @var \Arcanedev\NoCaptcha\Contracts\Utilities\AttributesInterface
     */
    protected $attributes;

    /* ------------------------------------------------------------------------------------------------
     |  Constructor
     | ------------------------------------------------------------------------------------------------
     */
    /**
     * NoCaptcha constructor.
     *
     * @param  string       $secret
     * @param  string       $siteKey
     * @param  string|null  $lang
     * @param  array        $attributes
     */
    public function __construct($secret, $siteKey, $lang = null, array $attributes = [])
    {
        $this->setSecret($secret);
        $this->setSiteKey($siteKey);
        $this->setLang($lang);

        $this->setRequestClient(new Request);
        $this->setAttributes(new Attributes($attributes));
    }

    /* ------------------------------------------------------------------------------------------------
     |  Getters & Setters
     | ------------------------------------------------------------------------------------------------
     */
    /**
     * Set the secret key.
     *
     * @param  string  $secret
     *
     * @return self
     */
    protected function setSecret($secret)
    {
        $this->checkKey('secret key', $secret);

        $this->secret = $secret;

        return $this;
    }

    /**
     * Set Site key.
     *
     * @param  string  $siteKey
     *
     * @return self
     */
    protected function setSiteKey($siteKey)
    {
        $this->checkKey('site key', $siteKey);

        $this->siteKey = $siteKey;

        return $this;
    }

    /**
     * Set language code.
     *
     * @param  string  $lang
     *
     * @return self
     */
    public function setLang($lang)
    {
        $this->lang = $lang;

        return $this;
    }

    /**
     * Get script source link.
     *
     * @param  string|null  $callbackName
     *
     * @return string
     */
    private function getScriptSrc($callbackName = null)
    {
        $queries = [];

        if ($this->hasLang()) {
            array_set($queries, 'hl', $this->lang);
        }

        if ($this->hasCallbackName($callbackName)) {
            array_set($queries, 'onload', $callbackName);
            array_set($queries, 'render', 'explicit');
        }

        return static::CLIENT_URL . (count($queries) ? '?' . http_build_query($queries) : '');
    }

    /**
     * Set HTTP Request Client.
     *
     * @param  \Arcanedev\NoCaptcha\Contracts\Utilities\RequestInterface  $request
     *
     * @return self
     */
    public function setRequestClient(
        Contracts\Utilities\RequestInterface $request
    ) {
        $this->request = $request;

        return $this;
    }

    /**
     * Set noCaptcha Attributes.
     *
     * @param  \Arcanedev\NoCaptcha\Contracts\Utilities\AttributesInterface  $attributes
     *
     * @return self
     */
    public function setAttributes(
        Contracts\Utilities\AttributesInterface $attributes
    ) {
        $this->attributes = $attributes;

        return $this;
    }

    /* ------------------------------------------------------------------------------------------------
     |  Main Functions
     | ------------------------------------------------------------------------------------------------
     */
    /**
     * Display Captcha.
     *
     * @param  string|null  $name
     * @param  array        $attributes
     *
     * @return string
     */
    public function display($name = null, array $attributes = [])
    {
        $output = $this->attributes->build($this->siteKey, array_merge(
            $this->attributes->prepareNameAttribute($name),
            $attributes
        ));

        return '<div ' . $output . '></div>';
    }

    /**
     * Display image Captcha.
     *
     * @param  string|null  $name
     * @param  array        $attributes
     *
     * @return string
     */
    public function image($name = null, array $attributes = [])
    {
        return $this->display(
            $name, array_merge($attributes, $this->attributes->getImageAttribute())
        );
    }

    /**
     * Display audio Captcha.
     *
     * @param  string|null  $name
     * @param  array        $attributes
     *
     * @return string
     */
    public function audio($name = null, array $attributes = [])
    {
        return $this->display(
            $name, array_merge($attributes, $this->attributes->getAudioAttribute())
        );
    }

    /**
     * Verify Response.
     *
     * @param  string  $response
     * @param  string  $clientIp
     *
     * @return bool
     */
    public function verify($response, $clientIp = null)
    {
        if (empty($response)) return false;

        $response = $this->sendVerifyRequest([
            'secret'   => $this->secret,
            'response' => $response,
            'remoteip' => $clientIp
        ]);

        return isset($response['success']) && $response['success'] === true;
    }

    /**
     * Calls the reCAPTCHA siteverify API to verify whether the user passes CAPTCHA
     * test using a PSR-7 ServerRequest object.
     *
     * @param  \Psr\Http\Message\ServerRequestInterface  $request
     *
     * @return bool
     */
    public function verifyRequest(ServerRequestInterface $request)
    {
        $body   = $request->getParsedBody();
        $server = $request->getServerParams();

        $response = isset($body[self::CAPTCHA_NAME])
            ? $body[self::CAPTCHA_NAME]
            : '';

        $remoteIp = isset($server['REMOTE_ADDR'])
            ? $server['REMOTE_ADDR']
            : null;

        return $this->verify($response, $remoteIp);
    }

    /**
     * Get script tag.
     *
     * @param  string|null  $callbackName
     *
     * @return string
     */
    public function script($callbackName = null)
    {
        $script = '';

        if ( ! $this->scriptLoaded) {
            $script = '<script src="' . $this->getScriptSrc($callbackName) . '" async defer></script>';
            $this->scriptLoaded = true;
        }

        return $script;
    }

    /**
     * Get script tag with a callback function.
     *
     * @param  array   $captchas
     * @param  string  $callbackName
     *
     * @return string
     */
    public function scriptWithCallback(array $captchas, $callbackName = 'captchaRenderCallback')
    {
        $script = $this->script($callbackName);

        if (empty($script) || empty($captchas)) {
            return $script;
        }

        return implode(PHP_EOL, [implode(PHP_EOL, [
            '<script>',
                "
                var ".implode(',', $captchas).";
                var $callbackName = function() {",
                    $this->renderCaptchas($captchas),
                '};',
            '</script>'
        ]), $script]);
    }

    /**
     * Rendering captchas with callback function.
     *
     * @param  array  $captchas
     *
     * @return string
     */
    private function renderCaptchas(array $captchas)
    {
        return implode(PHP_EOL, array_map(function($captcha) {
            return "if (document.getElementById('$captcha')) { $captcha = grecaptcha.render('$captcha', {'sitekey' : '{$this->siteKey}'}); }";
        }, $captchas));
    }

    /* ------------------------------------------------------------------------------------------------
     |  Check Functions
     | ------------------------------------------------------------------------------------------------
     */
    /**
     * Check if has lang.
     *
     * @return bool
     */
    private function hasLang()
    {
        return ! empty($this->lang);
    }

    /**
     * Check if callback is not empty.
     *
     * @param  string|null  $callbackName
     *
     * @return bool
     */
    private function hasCallbackName($callbackName)
    {
        return ! (is_null($callbackName) || trim($callbackName) === '');
    }

    /**
     * Check key.
     *
     * @param  string  $name
     * @param  string  $value
     *
     * @throws \Arcanedev\NoCaptcha\Exceptions\ApiException
     */
    private function checkKey($name, &$value)
    {
        $this->checkIsString($name, $value);

        $value = trim($value);

        $this->checkIsNotEmpty($name, $value);
    }

    /**
     * Check if the value is a string value.
     *
     * @param  string  $name
     * @param  string  $value
     *
     * @throws \Arcanedev\NoCaptcha\Exceptions\ApiException
     */
    private function checkIsString($name, $value)
    {
        if ( ! is_string($value)) {
            throw new Exceptions\ApiException(
                'The ' . $name . ' must be a string value, ' . gettype($value) . ' given'
            );
        }
    }

    /**
     * Check if the value is not empty.
     *
     * @param string  $name
     * @param string  $value
     *
     * @throws \Arcanedev\NoCaptcha\Exceptions\ApiException
     */
    private function checkIsNotEmpty($name, $value)
    {
        if (empty($value)) {
            throw new Exceptions\ApiException('The ' . $name . ' must not be empty');
        }
    }

    /* ------------------------------------------------------------------------------------------------
     |  Other functions
     | ------------------------------------------------------------------------------------------------
     */
    /**
     * Send verify request to API and get response.
     *
     * @param  array  $query
     *
     * @return array
     */
    private function sendVerifyRequest(array $query = [])
    {
        $query    = array_filter($query);
        $url      = static::VERIFY_URL . '?' . http_build_query($query);
        $response = $this->request->send($url);

        return $response;
    }
}


<?php

namespace Reaction\Base;

use Reaction\Exceptions\InvalidCallException;
use Reaction\Exceptions\ViewNotFoundException;
use Reaction;
use Reaction\Helpers\FileHelper;
use Reaction\Widgets\Block;
use Reaction\Widgets\ContentDecorator;
use Reaction\Widgets\FragmentCache;

/**
 * View represents a view object in the MVC pattern.
 *
 * View provides a set of methods (e.g. [[render()]]) for rendering purpose.
 *
 * For more details and usage information on View, see the [guide article on views](guide:structure-views).
 *
 * @property string|bool $viewFile The view file currently being rendered. False if no view file is being
 * rendered. This property is read-only.
 */
class View extends Component implements ViewInterface, DynamicContentAwareInterface
{
    /**
     * @event Event an event that is triggered by [[beginPage()]].
     */
    const EVENT_BEGIN_PAGE = 'beginPage';
    /**
     * @event Event an event that is triggered by [[endPage()]].
     */
    const EVENT_END_PAGE = 'endPage';
    /**
     * @event ViewEvent an event that is triggered by [[renderFile()]] right before it renders a view file.
     */
    const EVENT_BEFORE_RENDER = 'beforeRender';
    /**
     * @event ViewEvent an event that is triggered by [[renderFile()]] right after it renders a view file.
     */
    const EVENT_AFTER_RENDER = 'afterRender';

    /**
     * @var ViewContextInterface the context under which the [[renderFile()]] method is being invoked.
     */
    public $context;
    /**
     * @var mixed custom parameters that are shared among view templates.
     */
    public $params = [];
    /**
     * @var array a list of available renderers indexed by their corresponding supported file extensions.
     * Each renderer may be a view renderer object or the configuration for creating the renderer object.
     * For example, the following configuration enables both Smarty and Twig view renderers:
     *
     * ```php
     * [
     *     'tpl' => ['class' => 'yii\smarty\ViewRenderer'],
     *     'twig' => ['class' => 'yii\twig\ViewRenderer'],
     * ]
     * ```
     *
     * If no renderer is available for the given view file, the view file will be treated as a normal PHP
     * and rendered via [[renderPhpFile()]].
     */
    public $renderers;
    /**
     * @var string the default view file extension. This will be appended to view file names if they don't have file extensions.
     */
    public $defaultExtension = 'php';
    /**
     * @var Theme|array|string the theme object or the configuration for creating the theme object.
     * If not set, it means theming is not enabled.
     */
    public $theme;
    /**
     * @var array a list of named output blocks. The keys are the block names and the values
     * are the corresponding block content. You can call [[beginBlock()]] and [[endBlock()]]
     * to capture small fragments of a view. They can be later accessed somewhere else
     * through this property.
     */
    public $blocks;
    /**
     * @var array|DynamicContentAwareInterface[] a list of currently active dynamic content class instances.
     * This property is used internally to implement the dynamic content caching feature. Do not modify it directly.
     * @internal
     */
    protected $cacheStack = [];
    /**
     * @var array a list of placeholders for embedding dynamic contents. This property
     * is used internally to implement the content caching feature. Do not modify it directly.
     * @internal
     */
    protected $dynamicPlaceholders = [];
    /**
     * @var string View layout in which it will be rendered
     */
    public $layout = '@reaction/Views/layout/main';

    /**
     * @var array the view files currently being rendered. There may be multiple view files being
     * rendered at a moment because one view may be rendered within another.
     */
    private $_viewFiles = [];

    /**
     * Initializes the view component.
     */
    public function init()
    {
        parent::init();
        if (is_array($this->theme)) {
            if (!isset($this->theme['class'])) {
                $this->theme['class'] = 'Reaction\Base\Theme';
            }
            $this->theme = \Reaction::create($this->theme);
        } elseif (is_string($this->theme)) {
            $this->theme = \Reaction::create($this->theme);
        }
    }

    /**
     * Renders a view.
     *
     * The view to be rendered can be specified in one of the following formats:
     *
     * - [path alias](guide:concept-aliases) (e.g. "@app/views/site/index");
     * - absolute path within application (e.g. "//site/index"): the view name starts with double slashes.
     *   The actual view file will be looked for under the [[Application::viewPath|view path]] of the application.
     * - absolute path within current module (e.g. "/site/index"): the view name starts with a single slash.
     *   The actual view file will be looked for under the [[Module::viewPath|view path]] of the [[Controller::module|current module]].
     * - relative view (e.g. "index"): the view name does not start with `@` or `/`. The corresponding view file will be
     *   looked for under the [[ViewContextInterface::getViewPath()|view path]] of the view `$context`.
     *   If `$context` is not given, it will be looked for under the directory containing the view currently
     *   being rendered (i.e., this happens when rendering a view within another view).
     *
     * @param string $view the view name.
     * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.
     * @param object $context the context to be assigned to the view and can later be accessed via [[context]]
     * in the view. If the context implements [[ViewContextInterface]], it may also be used to locate
     * the view file corresponding to a relative view name.
     * @return string the rendering result
     * @throws ViewNotFoundException if the view file does not exist.
     * @throws InvalidCallException if the view cannot be resolved.
     * @see renderFile()
     */
    public function render($view, $params = [], $context = null)
    {
        $viewFile = $this->findViewFile($view, $context);
        return $this->renderFile($viewFile, $params, $context);
    }

    /**
     * Finds the view file based on the given view name.
     * @param string $view the view name or the [path alias](guide:concept-aliases) of the view file. Please refer to [[render()]]
     * on how to specify this parameter.
     * @param object $context the context to be assigned to the view and can later be accessed via [[context]]
     * in the view. If the context implements [[ViewContextInterface]], it may also be used to locate
     * the view file corresponding to a relative view name.
     * @return string the view file path. Note that the file may not exist.
     * @throws InvalidCallException if a relative view name is given while there is no active context to
     * determine the corresponding view file.
     */
    public function findViewFile($view, $context = null)
    {
        if (strncmp($view, '@', 1) === 0) {
            // e.g. "@app/views/main"
            $file = \Reaction::$app->getAlias($view);
        } elseif (strncmp($view, '//', 2) === 0) {
            // e.g. "//layouts/main"
            $file = Reaction::$app->getViewPath() . DIRECTORY_SEPARATOR . ltrim($view, '/');
        } elseif ($context instanceof ViewContextInterface) {
            $view = ltrim($view, '/');
            $file = $context->getViewPath() . DIRECTORY_SEPARATOR . $view;
        } elseif (($currentViewFile = $this->getViewFile()) !== false) {
            $file = dirname($currentViewFile) . DIRECTORY_SEPARATOR . $view;
        } else {
            throw new InvalidCallException("Unable to resolve view file for view '$view': no active view context.");
        }

        $pathInfo = pathinfo($file, PATHINFO_EXTENSION);
        if ($pathInfo !== '' && !empty($pathInfo)) {
            return $file;
        }
        $path = $file . '.' . $this->defaultExtension;
        if ($this->defaultExtension !== 'php' && !is_file($path)) {
            $path = $file . '.php';
        }

        return $path;
    }

    /**
     * Renders a view file.
     *
     * If [[theme]] is enabled (not null), it will try to render the themed version of the view file as long
     * as it is available.
     *
     * The method will call [[FileHelper::localize()]] to localize the view file.
     *
     * If [[renderers|renderer]] is enabled (not null), the method will use it to render the view file.
     * Otherwise, it will simply include the view file as a normal PHP file, capture its output and
     * return it as a string.
     *
     * @param string $viewFile the view file. This can be either an absolute file path or an alias of it.
     * @param array $params the parameters (name-value pairs) that will be extracted and made available in the view file.
     * @param object $context the context that the view should use for rendering the view. If null,
     * existing [[context]] will be used.
     * @return string the rendering result
     * @throws ViewNotFoundException if the view file does not exist
     */
    public function renderFile($viewFile, $params = [], $context = null)
    {
        $viewFile = \Reaction::$app->getAlias($viewFile);

        if ($this->theme !== null) {
            $viewFile = $this->theme->applyTo($viewFile);
        }
        if (is_file($viewFile)) {
            $viewFile = FileHelper::localize($viewFile);
        } else {
            throw new ViewNotFoundException("The view file does not exist: $viewFile");
        }

        $oldContext = $this->context;
        if ($context !== null) {
            $this->context = $context;
        }
        $output = '';
        $this->_viewFiles[] = $viewFile;

        $this->beforeRender($viewFile, $params);

        if (Reaction::isDebug()) {
            //\Reaction::debug("Rendering view file: $viewFile");
        }
        $ext = pathinfo($viewFile, PATHINFO_EXTENSION);
        if (isset($this->renderers[$ext])) {
            if (is_array($this->renderers[$ext]) || is_string($this->renderers[$ext])) {
                $this->renderers[$ext] = \Reaction::create($this->renderers[$ext]);
            }
            /* @var $renderer ViewRenderer */
            $renderer = $this->renderers[$ext];
            $output = $renderer->render($this, $viewFile, $params);
        } else {
            $output = $this->renderPhpFile($viewFile, $params);
        }
        $this->afterRender($viewFile, $params, $output);

        array_pop($this->_viewFiles);
        $this->context = $oldContext;

        return $output;
    }

    /**
     * @return string|bool the view file currently being rendered. False if no view file is being rendered.
     */
    public function getViewFile()
    {
        return end($this->_viewFiles);
    }

    /**
     * This method is invoked right before [[renderFile()]] renders a view file.
     * The default implementation will trigger the [[EVENT_BEFORE_RENDER]] event.
     * If you override this method, make sure you call the parent implementation first.
     * @param string $viewFile the view file to be rendered.
     * @param array $params the parameter array passed to the [[render()]] method.
     */
    public function beforeRender($viewFile, $params)
    {
        $this->emit(self::EVENT_BEFORE_RENDER, [$this, $viewFile, $params]);
    }

    /**
     * This method is invoked right after [[renderFile()]] renders a view file.
     * The default implementation will trigger the [[EVENT_AFTER_RENDER]] event.
     * If you override this method, make sure you call the parent implementation first.
     * @param string $viewFile the view file being rendered.
     * @param array $params the parameter array passed to the [[render()]] method.
     * @param string $output the rendering result of the view file. Updates to this parameter
     * will be passed back and returned by [[renderFile()]].
     */
    public function afterRender($viewFile, $params, &$output)
    {
        $this->emit(self::EVENT_AFTER_RENDER, [$this, $viewFile, $params, &$output]);
    }

    /**
     * Renders a view file as a PHP script.
     *
     * This method treats the view file as a PHP script and includes the file.
     * It extracts the given parameters and makes them available in the view file.
     * The method captures the output of the included view file and returns it as a string.
     *
     * This method should mainly be called by view renderer or [[renderFile()]].
     *
     * @param string $_file_ the view file.
     * @param array $_params_ the parameters (name-value pairs) that will be extracted and made available in the view file.
     * @return string the rendering result
     * @throws \Exception
     * @throws \Throwable
     */
    public function renderPhpFile($_file_, $_params_ = [])
    {
        $_obInitialLevel_ = ob_get_level();
        ob_start();
        ob_implicit_flush(false);
        extract($_params_, EXTR_OVERWRITE);
        try {
            require $_file_;
            return ob_get_clean();
        } catch (\Exception $e) {
            while (ob_get_level() > $_obInitialLevel_) {
                if (!@ob_end_clean()) {
                    ob_clean();
                }
            }
            throw $e;
        } catch (\Throwable $e) {
            while (ob_get_level() > $_obInitialLevel_) {
                if (!@ob_end_clean()) {
                    ob_clean();
                }
            }
            throw $e;
        }
    }

    /**
     * Renders dynamic content returned by the given PHP statements.
     * This method is mainly used together with content caching (fragment caching and page caching)
     * when some portions of the content (called *dynamic content*) should not be cached.
     * The dynamic content must be returned by some PHP statements.
     * @param string $statements the PHP statements for generating the dynamic content.
     * @return string the placeholder of the dynamic content, or the dynamic content if there is no
     * active content cache currently.
     */
    public function renderDynamic($statements)
    {
        if (!empty($this->cacheStack)) {
            $n = count($this->dynamicPlaceholders);
            $placeholder = "<![CDATA[REACTION-DYNAMIC-$n]]>";
            $this->addDynamicPlaceholder($placeholder, $statements);

            return $placeholder;
        }

        return $this->evaluateDynamicContent($statements);
    }

    /**
     * {@inheritdoc}
     */
    public function getDynamicPlaceholders()
    {
        return $this->dynamicPlaceholders;
    }

    /**
     * {@inheritdoc}
     */
    public function setDynamicPlaceholders($placeholders)
    {
        $this->dynamicPlaceholders = $placeholders;
    }

    /**
     * {@inheritdoc}
     */
    public function addDynamicPlaceholder($placeholder, $statements)
    {
        foreach ($this->cacheStack as $cache) {
            if ($cache instanceof DynamicContentAwareInterface) {
                $cache->addDynamicPlaceholder($placeholder, $statements);
            } else {
                // TODO: Remove in 2.1
                $cache->dynamicPlaceholders[$placeholder] = $statements;
            }
        }
        $this->dynamicPlaceholders[$placeholder] = $statements;
}

    /**
     * Evaluates the given PHP statements.
     * This method is mainly used internally to implement dynamic content feature.
     * @param string $statements the PHP statements to be evaluated.
     * @return mixed the return value of the PHP statements.
     */
    public function evaluateDynamicContent($statements)
    {
        return eval($statements);
    }

    /**
     * Returns a list of currently active dynamic content class instances.
     * @return DynamicContentAwareInterface[] class instances supporting dynamic contents.
     */
    public function getDynamicContents()
    {
        return $this->cacheStack;
    }

    /**
     * Adds a class instance supporting dynamic contents to the end of a list of currently active
     * dynamic content class instances.
     * @param DynamicContentAwareInterface $instance class instance supporting dynamic contents.
     */
    public function pushDynamicContent(DynamicContentAwareInterface $instance)
    {
        $this->cacheStack[] = $instance;
    }

    /**
     * Removes a last class instance supporting dynamic contents from a list of currently active
     * dynamic content class instances.
     */
    public function popDynamicContent()
    {
        array_pop($this->cacheStack);
    }

    /**
     * Begins recording a block.
     *
     * This method is a shortcut to beginning [[Block]].
     * @param string $id the block ID.
     * @param bool $renderInPlace whether to render the block content in place.
     * Defaults to false, meaning the captured block will not be displayed.
     * @return Block the Block widget instance
     */
    public function beginBlock($id, $renderInPlace = false)
    {
        return Block::begin([
            'id' => $id,
            'renderInPlace' => $renderInPlace,
            'view' => $this,
        ]);
    }

    /**
     * Ends recording a block.
     */
    public function endBlock()
    {
        Block::end();
    }

    /**
     * Begins the rendering of content that is to be decorated by the specified view.
     *
     * This method can be used to implement nested layout. For example, a layout can be embedded
     * in another layout file specified as '@app/views/layouts/base.php' like the following:
     *
     * ```php
     * <?php $this->beginContent('@app/views/layouts/base.php'); ?>
     * //...layout content here...
     * <?php $this->endContent(); ?>
     * ```
     *
     * @param string $viewFile the view file that will be used to decorate the content enclosed by this widget.
     * This can be specified as either the view file path or [path alias](guide:concept-aliases).
     * @param array $params the variables (name => value) to be extracted and made available in the decorative view.
     * @return ContentDecorator the ContentDecorator widget instance
     * @see ContentDecorator
     */
    public function beginContent($viewFile, $params = [])
    {
        return ContentDecorator::begin([
            'viewFile' => $viewFile,
            'params' => $params,
            'view' => $this,
        ]);
    }

    /**
     * Ends the rendering of content.
     */
    public function endContent()
    {
        ContentDecorator::end();
    }

    /**
     * Begins fragment caching.
     *
     * This method will display cached content if it is available.
     * If not, it will start caching and would expect an [[endCache()]]
     * call to end the cache and save the content into cache.
     * A typical usage of fragment caching is as follows,
     *
     * ```php
     * if ($this->beginCache($id)) {
     *     // ...generate content here
     *     $this->endCache();
     * }
     * ```
     *
     * @param string $id a unique ID identifying the fragment to be cached.
     * @param array $properties initial property values for [[FragmentCache]]
     * @return bool whether you should generate the content for caching.
     * False if the cached version is available.
     */
    public function beginCache($id, $properties = [])
    {
        $properties['id'] = $id;
        $properties['view'] = $this;
        /* @var $cache FragmentCache */
        $cache = FragmentCache::begin($properties);
        if ($cache->getCachedContent() !== false) {
            $this->endCache();

            return false;
        }

        return true;
    }

    /**
     * Ends fragment caching.
     */
    public function endCache()
    {
        FragmentCache::end();
    }

    /**
     * Marks the beginning of a page.
     */
    public function beginPage()
    {
        ob_start();
        ob_implicit_flush(false);

        $this->emit(self::EVENT_BEGIN_PAGE, [$this]);
    }

    /**
     * Marks the ending of a page.
     */
    public function endPage()
    {
        $this->emit(self::EVENT_END_PAGE, [$this]);
        ob_end_flush();
    }

    /**
     * Static helper for rendering any php file
     * @param string $_file_
     * @param array $_params_
     * @return string
     * @throws \Throwable
     */
    public static function renderPhpStateless($_file_, $_params_ = [])
    {
        $_file_ = Reaction::$app->getAlias($_file_);
        $_obInitialLevel_ = ob_get_level();
        ob_start();
        ob_implicit_flush(false);
        extract($_params_, EXTR_OVERWRITE);
        try {
            require $_file_;
            return ob_get_clean();
        } catch (\Exception $e) {
            while (ob_get_level() > $_obInitialLevel_) {
                if (!@ob_end_clean()) {
                    ob_clean();
                }
            }
            throw $e;
        } catch (\Throwable $e) {
            while (ob_get_level() > $_obInitialLevel_) {
                if (!@ob_end_clean()) {
                    ob_clean();
                }
            }
            throw $e;
        }
    }
}


<?php

namespace FlexPress\Components\PostType;

class Helper
{

    /**
     * @var \SplObjectStorage
     * @author Tim Perry
     */
    protected $postTypes;

    public function __construct(\SplObjectStorage $postTypes, array $postTypeArray)
    {
        $this->postTypes = $postTypes;

        if (!empty($postTypeArray)) {

            foreach ($postTypeArray as $postType) {

                if (!$postType instanceof AbstractPostType) {

                    $message = "One or more of the post types you have passed to ";
                    $message .= get_class($this);
                    $message .= " does not extend the AbstractPostType class.";

                    throw new \RuntimeException($message);

                }

                $this->postTypes->attach($postType);

            }

        }

    }

    /**
     * Registers all the post types added
     * @author Tim Perry
     */
    public function registerPostTypes()
    {

        if (!function_exists('register_post_type')) {
            return;
        }

        $this->postTypes->rewind();
        while ($this->postTypes->valid()) {

            $postType = $this->postTypes->current();
            register_post_type($postType->getName(), $postType->getArgs());
            $this->postTypes->next();

        }

    }
}


<?php
namespace UPhp\helpers;

use \ZipArchive;

class MakeZip
{
    /**
     * Add files and sub-directories in a folder to zip file.
     * @param string $folder
     * @param ZipArchive $zipFile
     * @param int $exclusiveLength Number of text to be exclusived from the file path.
     */
    private static function folderToZip($folder, &$zipFile, $exclusiveLength) {
        $handle = opendir($folder);
        while (false !== $f = readdir($handle)) {
            if ($f != '.' && $f != '..') {
                $filePath = "$folder/$f";
                // Remove prefix from file path before add to zip.
                $localPath = substr($filePath, $exclusiveLength);
                if (is_file($filePath)) {
                    $zipFile->addFile($filePath, $localPath);
                } elseif (is_dir($filePath)) {
                    // Add sub-directory.
                    $zipFile->addEmptyDir($localPath);
                    self::folderToZip($filePath, $zipFile, $exclusiveLength);
                }
            }
        }
        closedir($handle);
    }

    /**
     * Zip a folder (include itself).
     * Usage:
     *   HZip::zipDir('/path/to/sourceDir', '/path/to/out.zip');
     *
     * @param string $sourcePath Path of directory to be zip.
     * @param string $outZipPath Path of output zip file.
     * @param bool $deleteOriginal Deleta a pasta original, default false
     * @return mixed
     */
    public static function zipDir($sourcePath, $outZipPath, $deleteOriginal = false)
    {
        $pathInfo = pathInfo($sourcePath);
        $parentPath = $pathInfo['dirname'];
        $dirName = $pathInfo['basename'];

        $z = new ZipArchive();

        $ret = $z->open($outZipPath, ZipArchive::OVERWRITE);
        if ($ret === ZipArchive::ER_NOENT)
            $z->open($outZipPath, ZipArchive::CREATE);
        elseif ($ret !== ZipArchive::ER_MULTIDISK) return self::zipStatusString($ret);

        $z->addEmptyDir($dirName);
        self::folderToZip($sourcePath, $z, strlen("$parentPath/"));
        $z->close();

        self::delTree($sourcePath, $deleteOriginal);
    }

    /**
     * Compacta arquivos no formato .zip
     * Exemplo:
     *      $filePath = 'download/temp/';
     *      $outZipPath = 'download/nameFile.zip';
     *      $prefix = 'img_';
     *
     *      # O parametro $files pode receber um array com sub arrays conforme segue o exemplo abaixo, ou pode receber um array contendo diretamente as extensões, ex.: ['png', 'jpg', ...]
     *      $files = ['files' => ['file1.jpg', 'file2.jpg], 'extensions' => ['png', 'gif']];
     *
     *      # $deleteOriginals por default é TRUE
     *      $deleteOriginals = false;
     *
     *      # $removeAllPath por default é TRUE
     *      $removeAllPath = false;
     *
     *      HZip::zipFiles($filesPath, $outZipPath, $prefix, $files, $deleteOriginals, $removeAllPath);
     *
     * @param string $filesPath Caminho dos arquivos que serão compactados.
     * @param string $outZipPath Caminho onde será armazenado o arquivo Zip.
     * @param string $prefix String que precede o nome do arquivo.
     * @param mixed $files Array para especificar extensões ou arquivos, ou um valor empty todos.
     * @param bool $deleteOriginals Chave para informar se serão deletados os arquivos originais.
     * @param bool $removeAllPath Chave para informar se será deletado o caminho até os arquivos originais.
     * @return mixed Retorna TRUE caso tenha sucesso ou uma string de erro.
     */
    public static function zipFiles($filesPath, $outZipPath, string $prefix = '.', $files = null, $deleteOriginals = true, $removeAllPath = true)
    {
        # Força o carregamente de todos os arquivos caso seja empty()
        if (empty($files)) $files = ['[a-z]+'];
        elseif (! is_array($files) && ! empty($files)) return self::zipStatusString(ZipArchive::ER_NOENT);

        $pattern = '';
        $z = new ZipArchive();

        $ret = $z->open($outZipPath, ZipArchive::OVERWRITE);
        if ($ret === ZipArchive::ER_NOENT) {
            $ret = $z->open($outZipPath, ZipArchive::CREATE);
            if ($ret !== true) return self::zipStatusString($ret);
        } elseif ($ret !== true) return self::zipStatusString($ret);

        $options = ['add_path' => $prefix, 'remove_all_path' => boolval($removeAllPath)];

        if (isset($files['files']))
            $pattern = implode("|", $files['files']);

        if (isset($files['extensions'])) {
            $sfiles = implode("|", $files['extensions']);
            if (empty($pattern)) $pattern = '\.(?:' . $sfiles . ')';
            else $pattern .= '|\.(?:' . $sfiles . ')';
        } elseif (! isset($files['files']) && ! isset($files['extensions'])) {
            $sfiles = implode("|", $files);
            $pattern = '\.(?:' . $sfiles . ')';
        }

        $ret = $z->addPattern('/' . $pattern . '$/', $filesPath, $options);
        if (! $ret) {
            return self::zipStatusString(ZipArchive::ER_WRITE);
        }

        $ret = $z->close();
        if ($ret !== true) return self::zipStatusString($ret);

        return self::deleteFiles($filesPath, $files, $deleteOriginals);
    }

    public static function zipStatusString($status)
    {
        switch( (int) $status ) {
            case ZipArchive::ER_OK           : return 'No error';
            case ZipArchive::ER_MULTIDISK    : return 'Multi-disk zip archives not supported';
            case ZipArchive::ER_RENAME       : return 'Renaming temporary file failed';
            case ZipArchive::ER_CLOSE        : return 'Closing zip archive failed';
            case ZipArchive::ER_SEEK         : return 'Seek error';
            case ZipArchive::ER_READ         : return 'Read error';
            case ZipArchive::ER_WRITE        : return 'Write error';
            case ZipArchive::ER_CRC          : return 'CRC error';
            case ZipArchive::ER_ZIPCLOSED    : return 'Containing zip archive was closed';
            case ZipArchive::ER_NOENT        : return 'No such file';
            case ZipArchive::ER_EXISTS       : return 'File already exists';
            case ZipArchive::ER_OPEN         : return 'Can\'t open file';
            case ZipArchive::ER_TMPOPEN      : return 'Failure to create temporary file';
            case ZipArchive::ER_ZLIB         : return 'Zlib error';
            case ZipArchive::ER_MEMORY       : return 'Malloc failure';
            case ZipArchive::ER_CHANGED      : return 'Entry has been changed';
            case ZipArchive::ER_COMPNOTSUPP  : return 'Compression method not supported';
            case ZipArchive::ER_EOF          : return 'Premature EOF';
            case ZipArchive::ER_INVAL        : return 'Invalid argument';
            case ZipArchive::ER_NOZIP        : return 'Not a zip archive';
            case ZipArchive::ER_INTERNAL     : return 'Internal error';
            case ZipArchive::ER_INCONS       : return 'Zip archive inconsistent';
            case ZipArchive::ER_REMOVE       : return 'Can\'t remove file';
            case ZipArchive::ER_DELETED      : return 'Entry has been deleted';

            default: return sprintf('Unknown status %s', $status );
        }
    }

    private static function deleteFiles($filesPath, $files, $delete = false)
    {
        if (empty($delete)) return true;

        $ret = true;
        if (empty($files)) {
            $files = array_diff(scandir($filesPath), array('.','..'));
            foreach ($files as $file) {
                if (is_dir($filesPath . $file)) $ret &= self::delTree($filesPath . $file);
                else $ret &= unlink($filesPath . $file);

                if (! $ret) return self::zipStatusString(ZipArchive::ER_REMOVE);
            }
        } else {
            $extensions = null;
            $fileName = null;
            if (isset($files['files'])) $fileName = $files['files'];

            if (isset($files['extensions'])) $extensions = $files['extensions'];
            elseif (! isset($files['files']) && ! isset($files['extensions'])) $extensions = $files;

            if (is_dir($filesPath)) {
                if ($dh = opendir($filesPath)) {
                    while (($file = readdir($dh)) !== false) {
                        if (! empty($extensions) && in_array(pathinfo($file)['extension'], $extensions)) {
                            $ret &= unlink($filesPath . $file);
                            if (! $ret) return self::zipStatusString(ZipArchive::ER_REMOVE);
                        }
                        if (! empty($fileName) && in_array(pathinfo($file)['basename'], $fileName)) {
                            $ret &= unlink($filesPath . $file);
                            if (! $ret) return self::zipStatusString(ZipArchive::ER_REMOVE);
                        }
                    }
                    closedir($dh);
                }
            }
        }
        return $ret;
    }

    private static function delTree($dir, $delete = true)
    {
        if (empty($delete)) return true;

        $files = array_diff(scandir($dir), array('.','..'));
        foreach ($files as $file) {
            (is_dir($dir . $file)) ? delTree($dir . $file) : unlink($dir . $file);
        }
        return rmdir($dir);
    }

    private static function downloadZip($zipName)
    {
        ///Then download the zipped file.
        header('Content-Type: application/zip');
        header('Content-disposition: attachment; filename='.$zipName);
        header('Content-Length: ' . filesize($zipName));
        readfile($zipName);
    }
}


<?php
/**
 * @section LICENSE
 * This file is part of MediaWiki simple i18n.
 *
 * MediaWiki simple i18n is free software: you can redistribute it
 * and/or modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * MediaWiki simple i18n is distributed in the hope that it will
 * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
 * Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with MediaWiki simple i18n.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @file
 */

namespace Wikimedia\SimpleI18n;

/**
 * Integrate SimpleI18n with the Twig template engine.
 *
 * Provides a `message` Twig filter that treats the modified variable as
 * a Message key and returns the plain formatted value of that Message.
 * Message parameters can be provided as either individual arguments to the
 * filter or optionally as a single array of argument containing a list of
 * parameters.
 *
 *
 * {{ 'my-message-key'|message }}
 * {{ 'my-message-key'|message( 'foo', 'bar' ) }}
 * {{ 'my-message-key'|message( [ 'foo', 'bar' ] ) }}
 * {{ 'my-message-key'|message( 'foo', 'bar' )|raw }}
 *
 *
 * @copyright © 2014 Bryan Davis and Wikimedia Foundation
 */
class TwigExtension extends \Twig_Extension {

	/**
	 * @var I18nContext $ctx
	 */
	protected $ctx;

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

	public function getName() {
		return 'Wikimedia-SimpleI18n';
	}

	public function getFilters() {
		return array(
			new \Twig_SimpleFilter(
				'message', array( $this, 'messageFilterCallback' )
			),
		);
	}

	/**
	 * Callback for 'message' filter.
	 *
	 *
	 * {{ 'my-message-key'|message }}
	 * {{ 'my-message-key'|message( 'foo', 'bar' ) }}
	 * {{ 'my-message-key'|message( [ 'foo', 'bar' ] ) }}
	 * {{ 'my-message-key'|message( 'foo', 'bar' )|raw }}
	 *
	 *
	 * @param string $key Message key
	 * @param string ... Message params
	 * @return string Unescaped message content
	 * @see I18nContext::message
	 */
	public function messageFilterCallback( $key /*...*/ ) {
		$params = func_get_args();
		array_shift( $params );
		if ( count( $params ) == 1 && is_array( $params[0] ) ) {
			// Unwrap args array
			$params = $params[0];
		}
		$msg = $this->ctx->message( $key, $params );
		return $msg->plain();
	}
}