<?php

namespace Vinelab\Auth\Social;

use Vinelab\Http\Client as HttpClient;
use Illuminate\Routing\Redirector;
use Illuminate\Config\Repository as Config;
use Vinelab\Auth\Contracts\StoreInterface;
use Vinelab\Auth\Contracts\ProfileInterface;
use Vinelab\Auth\Contracts\ProvidersManagerInterface;
use Vinelab\Auth\Exceptions\ProviderNotSupportedException;
use Vinelab\Auth\Social\Providers\Twitter\Contracts\OAuthInterface;
use Vinelab\Auth\Social\Providers\Twitter\Contracts\OAuthTokenInterface;
use Vinelab\Auth\Social\Providers\Facebook\Contracts\AccessTokenInterface;
use Vinelab\Auth\Social\Providers\Twitter\Contracts\OAuthConsumerInterface;
use Vinelab\Auth\Social\Providers\Twitter\Contracts\OAuthSignatureInterface;

class ProvidersManager implements ProvidersManagerInterface
{
    /**
     * Lists the supported provider
     * networks.
     *
     * @var array
     */
    protected $supported = ['facebook', 'twitter'];

    /**
     * The configuration instance.
     *
     * @var Illuminate\Config\Repository
     */
    protected $config;

    /**
     * Create a new ProvidersManager instance.
     *
     * @param Illuminate\Config\Repository                                          $config
     * @param Illuminate\Routing\Redirector                                         $redirector
     * @param Vinelab\Http\Client                                                   $http
     * @param Vinelab\Auth\Contracts\StoreInterface                                 $store
     * @param Vinelab\Auth\Contracts\ProfileInterface                               $profile
     * @param Vinelab\Auth\Social\Providers\Facebook\Contracts\AccessTokenInterface $access_token
     */
    public function __construct(Config $config,
                                Redirector $redirector,
                                HttpClient $http,
                                StoreInterface $store,
                                ProfileInterface $profile,
                                AccessTokenInterface $access_token,
                                OAuthInterface $oauth,
                                OAuthTokenInterface $token,
                                OAuthSignatureInterface $signature,
                                OAuthConsumerInterface $consumer)
    {
        $this->config = $config;
        $this->redirector = $redirector;
        $this->http = $http;
        $this->store = $store;
        $this->profile = $profile;
        $this->access_token = $access_token;
        $this->signature = $signature;
        $this->consumer = $consumer;
        $this->oauth = $oauth;
        $this->token = $token;
    }

    /**
     * Instantiates and returns a
     * social provider instance.
     *
     * @param string $provider
     *
     * @return Vinelab\Auth\Contracts\ProviderInterface
     */
    public function instantiate($provider)
    {
        if (!$this->supported($provider)) {
            throw new ProviderNotSupportedException($provider);
        }

        $class = $this->providerClass($provider);

        switch ($provider) {
            case 'facebook':
                return new $class($this->config,
                        $this->redirector,
                        $this->http,
                        $this->store,
                        $this->profile,
                        $this->access_token);
            break;

            case 'twitter':
                return new $class($this->config,
                                $this->http,
                                $this->redirector,
                                $this->store,
                                $this->profile,
                                $this->signature,
                                $this->consumer,
                                $this->token,
                                $this->oauth);
            break;
        }
    }

    /**
     * Determines whether an authentication
     * provider is supported.
     *
     * @param string $provider
     *
     * @return bool
     */
    public function supported($provider)
    {
        return in_array($provider, $this->supported);
    }

    /**
     * Returns the class name of
     * a provider network.
     *
     * @param string $provider
     *
     * @return string
     */
    private function providerClass($provider)
    {
        return 'Vinelab\Auth\Social\Providers\\'.ucwords(strtolower($provider));
    }
}


<?php
/**
 * Custom Rating Grifus · Extensions For Grifus
 *
 * @author    Josantonius <hello@josantonius.com>
 * @package   Josantonius/Custom-Rating-Grifus
 * @copyright 2017 - 2018 (c) Josantonius - Custom Rating Grifus
 * @license   GPL-2.0+
 * @link      https://github.com/eliasis-framework/custom-rating-grifus.git
 * @since     1.0.0
 */

namespace EFG\Modules\CustomRatingGrifus\Controller;

use Josantonius\WP_Register\WP_Register;
use Eliasis\Complement\Type\Module;
use Eliasis\Framework\Controller;
use Eliasis\Framework\App;

/**
 * Module main controller.
 */
class Launcher extends Controller {

	/**
	 * Instance of Rating class.
	 *
	 * @var object
	 */
	public $rating;

	/**
	 * Class initializer method.
	 *
	 * @return boolean
	 */
	public function init() {

		$state = Module::CustomRatingGrifus()->getOption( 'state' );

		if ( 'active' === $state || 'outdated' === $state ) {
			App::setCurrentID( 'EFG' );
			$this->rating = Module::CustomRatingGrifus()->getControllerInstance( 'Rating' );

			add_action( 'init', [ $this, 'set_language' ] );
			$this->run_ajax();
			if ( is_admin() ) {
				return $this->admin();
			}

			$this->front();
		}
	}

	/**
	 * Module activation hook. Executed when module is activated.
	 */
	public function activation() {

		$this->model->add_options();
		$this->model->create_tables();
	}

	/**
	 * Module uninstallation hook. Executed when module is uninstalled.
	 */
	public function uninstallation() {

		$this->model->delete_post_meta();
		$this->model->delete_options();
		$this->model->remove_tables();
	}

	/**
	 * Set plugin textdomain.
	 */
	public function set_language() {

		$plugin_slug = App::EFG()->getOption( 'slug' );
		$module_slug = Module::CustomRatingGrifus()->getOption( 'slug' );

		$path = $plugin_slug . '/modules/' . $module_slug . '/languages/';

		load_plugin_textdomain( $plugin_slug . '-rating', false, $path );
	}

	/**
	 * Run ajax when change the rating.
	 */
	public function run_ajax() {

		$methods = [ 'add_movie_rating' ];

		foreach ( $methods as $method ) {
			add_action( 'wp_ajax_' . $method, [ $this->rating, $method ] );
			add_action( 'wp_ajax_nopriv_' . $method, [ $this->rating, $method ] );
		}
	}

	/**
	 * Admin initializer method.
	 *
	 * @uses add_action() → hooks a function on to a specific action
	 */
	public function admin() {

		$this->set_options();

		$namespace    = Module::CustomRatingGrifus()->getOption( 'namespaces' );
		$module_pages = Module::CustomRatingGrifus()->getOption( 'pages' );

		App::main()->set_menus( $module_pages, $namespace['admin-page'] );

		add_action( 'add_meta_boxes', [ $this->rating, 'add_meta_boxes' ], 10, 2 );
		add_action( 'save_post', [ $this->rating, 'restart_rating' ], 1, 3 );
		add_action( 'save_post', [ $this->rating, 'update_rating' ], 10, 3 );
	}

	/**
	 * Set database module options.
	 *
	 * @since 1.0.1
	 */
	public function set_options() {

		$slug    = Module::CustomRatingGrifus()->getOption( 'slug' );
		$options = $this->model->get_options();

		foreach ( $options as $option => $value ) {
			Module::CustomRatingGrifus()->setOption( $option, $value );
		}
	}

	/**
	 * Front initializer method.
	 */
	public function front() {

		add_action(
			'wp', function() {
				App::setCurrentID( 'EFG' );
				if ( App::main()->is_single() && ! is_preview() ) {
					$this->add_scripts( 'customRatingGrifus' );
					$this->add_styles();
				} elseif ( is_home() || is_category() || is_archive() || is_search() ) {
					$this->add_scripts( 'customRatingGrifusHome' );
				}
			}
		);
	}

	/**
	 * Add scripts.
	 *
	 * @param string $name → script name.
	 */
	protected function add_scripts( $name ) {

		$params   = $this->rating->set_movie_params();
		$settings = Module::CustomRatingGrifus()->getOption( 'assets', 'js', $name );

		$settings['params'] = array_merge( $settings['params'], $params );

		WP_Register::add( 'script', $settings );
	}

	/**
	 * Add styles.
	 */
	protected function add_styles() {

		$css = Module::CustomRatingGrifus()->getOption( 'assets', 'css' );

		WP_Register::add(
			'style',
			$css['customRatingGrifus']
		);
	}
}


<?php

/**
 * This file is part of MetaModels/attribute_translatedlongtext.
 *
 * (c) 2012-2019 The MetaModels team.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * This project is provided in good faith and hope to be usable by anyone.
 *
 * @package    MetaModels/attribute_translatedlongtext
 * @author     David Molineus <david.molineus@netzmacht.de>
 * @copyright  2012-2019 The MetaModels team.
 * @license    https://github.com/MetaModels/attribute_translatedlongtext/blob/master/LICENSE LGPL-3.0-or-later
 * @filesource
 */

namespace MetaModels\AttributeTranslatedLongtextBundle\ContaoManager;

use Contao\CoreBundle\ContaoCoreBundle;
use Contao\ManagerPlugin\Bundle\BundlePluginInterface;
use Contao\ManagerPlugin\Bundle\Config\BundleConfig;
use Contao\ManagerPlugin\Bundle\Parser\ParserInterface;
use MetaModels\AttributeTranslatedLongtextBundle\MetaModelsAttributeTranslatedLongtextBundle;
use MetaModels\CoreBundle\MetaModelsCoreBundle;

/**
 * Plugin for the Contao Manager.
 */
class Plugin implements BundlePluginInterface
{
    /**
     * {@inheritdoc}
     */
    public function getBundles(ParserInterface $parser)
    {
        return [
            BundleConfig::create(MetaModelsAttributeTranslatedLongtextBundle::class)
                ->setLoadAfter(
                    [
                        ContaoCoreBundle::class,
                        MetaModelsCoreBundle::class
                    ]
                )
                ->setReplace(['metamodelsattribute_translatedlongtext'])
        ];
    }
}


<?php
/**
 * Factory
 */

namespace Graviton\PhpProxy;

use GatewayBundle\Service\Proxy\Middleware\LoggingMiddleware;
use Graviton\PhpProxy\Guzzle\Middleware\Logging;
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use Monolog\Logger;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ServerRequestFactoryInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UploadedFileFactoryInterface;
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
use Zend\Diactoros\ResponseFactory;
use Zend\Diactoros\ServerRequestFactory;
use Zend\Diactoros\StreamFactory;
use Zend\Diactoros\UploadedFileFactory;

/**
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
 * @license  https://opensource.org/licenses/MIT MIT License
 * @link     http://swisscom.ch
 */
class Factory {

    /**
     * creates a new PsrHttpFactory based on diactoros
     *
     * @return PsrHttpFactory psr http factory
     */
    public static function createPsrHttpFactory() {
        return new PsrHttpFactory(
            new ServerRequestFactory(),
            new StreamFactory(),
            new UploadedFileFactory(),
            new ResponseFactory()
        );
    }

    /**
     * creates a http client (ie guzzle)
     *
     * @return Client guzzle client
     */
    public static function createHttpClient(
        $baseParams = [],
        $debugLogging = false,
        Logger $logger = null,
        $maxMessageLogLength = 5000
    ) {

        // attach our debug logger?
        if ($debugLogging && $logger instanceof Logger) {
            if (!isset($baseParams['handler']) || !($baseParams['handler'] instanceof HandlerStack)) {
                $baseParams['handler'] = HandlerStack::create();
            }

            $baseParams['handler']->push(
                Middleware::mapRequest(
                    Logging::getCallable($logger, 'REQUEST', $maxMessageLogLength)
                )
            );
            $baseParams['handler']->push(
                Middleware::mapResponse(
                    Logging::getCallable($logger, 'RESPONSE', $maxMessageLogLength)
                )
            );
        }

        return new Client($baseParams);
    }
}


<?php
/**
 * Proxy
 */
namespace Graviton\PhpProxy;

use GuzzleHttp\Client;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\Uri;

/**
 * proxies request from one place to another.. thanks jenssegers for the first approach ;-)
 *
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
 * @license  https://opensource.org/licenses/MIT MIT License
 * @link     http://swisscom.ch
 */
class Proxy
{
    /**
     * @var Client
     */
    private $client;

    /**
     * @var ServerRequestInterface
     */
    private $request;

    /**
     * Proxy constructor.
     *
     * @param Client $client client
     */
    public function __construct(Client $client)
    {
        $this->client = $client;
    }

    /**
     * Prepare the proxy to forward a request instance.
     *
     * @param  ServerRequestInterface $request request
     *
     * @return $this
     */
    public function forward(ServerRequestInterface $request)
    {
        $this->request = $request;
        return $this;
    }

    /**
     * Forward the request to the target url and return the response.
     *
     * @param string $target target
     *
     * @throws LogicException
     * @return ResponseInterface
     */
    public function to($target)
    {
        if (is_null($this->request)) {
            throw new \LogicException('Missing request instance.');
        }

        $target = new Uri($target);

        // Overwrite target scheme and host.
        $uri = $this->request->getUri()
            ->withScheme($target->getScheme())
            ->withHost($target->getHost());

        // Check for custom port.
        if ($port = $target->getPort()) {
            $uri = $uri->withPort($port);
        }

        // Check for subdirectory.
        if ($path = $target->getPath()) {
            $uri = $uri->withPath(rtrim($path, '/') . '/' . ltrim($uri->getPath(), '/'));
        }

        if (!empty($this->request->getQueryParams())) {
            // special case for rql
            $queryParams = $this->request->getQueryParams();

            if (count($queryParams) == 1 && empty(array_shift($queryParams))) {
                $queryKeys = array_keys($this->request->getQueryParams());
                $uri = $uri->withQuery($queryKeys[0]);
            } else {
                $uri = $uri->withQuery(http_build_query($this->request->getQueryParams()));
            }
        }

        $request = $this->request->withUri($uri);

        // make sure we don't send empty headers
        foreach ($request->getHeaders() as $headerName => $headerValue) {
            if (empty($headerValue[0])) {
                $request = $request->withoutHeader($headerName);
            }
        }

        return $this->client->send($request);
    }
}


<?php
/*
 * This file is part of the Sidus/BaseBundle package.
 *
 * Copyright (c) 2015-2019 Vincent Chalnot
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sidus\BaseBundle\Request\ParamConverter;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * Common pattern for param converters
 *
 * @author Vincent Chalnot <vincent@sidus.fr>
 */
abstract class AbstractParamConverter implements ParamConverterInterface
{
    /**
     * Stores the object in the request.
     *
     * @param Request        $request
     * @param ParamConverter $configuration Contains the name, class and options of the object
     *
     * @throws NotFoundHttpException
     * @throws \InvalidArgumentException
     *
     * @return bool True if the object has been successfully set, else false
     */
    public function apply(Request $request, ParamConverter $configuration)
    {
        $param = $this->getRequestAttributeName($request, $configuration);

        if (!$request->attributes->has($param)) {
            return false;
        }

        $value = $request->attributes->get($param);

        if (!$value && $configuration->isOptional()) {
            return false;
        }

        $convertedValue = $this->convertValue($value, $configuration);

        if (null === $convertedValue && false === $configuration->isOptional()) {
            throw new NotFoundHttpException(
                "Unable to find '{$configuration->getClass()}' with identifier '{$value}' not found"
            );
        }

        $request->attributes->set($configuration->getName(), $convertedValue);

        return true;
    }

    /**
     * Checks if the object is supported.
     *
     * @param ParamConverter $configuration Should be an instance of ParamConverter
     *
     * @return bool True if the object is supported, else false
     */
    public function supports(ParamConverter $configuration)
    {
        return $configuration->getClass() && is_a($configuration->getClass(), $this->getClass(), true);
    }

    /**
     * @param Request        $request
     * @param ParamConverter $configuration
     *
     * @return string
     */
    protected function getRequestAttributeName(Request $request, ParamConverter $configuration)
    {
        $param = $configuration->getName();
        if (array_key_exists('id', $configuration->getOptions())) {
            $param = $configuration->getOptions()['id'];
        }

        return $param;
    }

    /**
     * @param mixed          $value
     * @param ParamConverter $configuration
     *
     * @return mixed
     */
    abstract protected function convertValue($value, ParamConverter $configuration);

    /**
     * @return mixed
     */
    abstract protected function getClass();
}


<?php

/*
 * This file is part of the OneBundleApp package.
 *
 * Copyright (c) >=2017 Marc Morera
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * Feel free to edit as you please, and have fun.
 *
 * @author Marc Morera <yuhu@mmoreram.com>
 */

declare(strict_types=1);

namespace OneBundleApp\App;

use Mmoreram\BaseBundle\Kernel\BaseKernel;
use Psr\Http\Message\ServerRequestInterface;
use React\Http\Response as ReactResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

/**
 * Class RequestHandler.
 */
class RequestHandler
{
    /**
     * @var BaseKernel
     *
     * Kernel
     */
    private $kernel;

    /**
     * RequestHandler constructor.
     *
     * @param BaseKernel $kernel
     */
    public function __construct(BaseKernel $kernel)
    {
        $this->kernel = $kernel;
    }

    /**
     * Handle server request and return response.
     *
     * Return an array of an instance of ResponseInterface and an array of
     * Printable instances
     *
     * @param ServerRequestInterface $request
     *
     * @return array
     */
    public function handleServerRequest(ServerRequestInterface $request): array
    {
        $messages = [];

        try {
            $body = $request->getBody()->getContents();
            $uriPath = $request->getUri()->getPath();

            if ('/favicon.ico' === $uriPath) {
                return [$this->createFaviconResponse(), []];
            }

            $from = microtime(true);
            $method = $request->getMethod();
            $headers = $request->getHeaders();

            $symfonyRequest = new Request(
                $request->getQueryParams(),
                $request->getParsedBody() ?? [],
                $request->getAttributes(),
                $request->getCookieParams(),
                $request->getUploadedFiles(),
                [], // Server is partially filled a few lines below
                $body
            );

            $symfonyRequest->setMethod($method);
            $symfonyRequest->headers->replace($headers);
            $symfonyRequest->server->set('REQUEST_URI', $uriPath);
            if (isset($headers['Host'])) {
                $symfonyRequest->server->set('SERVER_NAME', explode(':', $headers['Host'][0]));
            }

            $symfonyResponse = $this->kernel->handle($symfonyRequest);
            $this->kernel->terminate($symfonyRequest, $symfonyResponse);
            $to = microtime(true);
            $messages[] = new ConsoleMessage(
                $request->getUri()->getPath(),
                $method,
                $symfonyResponse->getStatusCode(),
                $symfonyResponse->getContent(),
                \intval(($to - $from) * 1000)
            );

            $this->applyResponseEncoding(
                $symfonyRequest,
                $symfonyResponse
            );

            $httpResponse = new \React\Http\Response(
                $symfonyResponse->getStatusCode(),
                $symfonyResponse->headers->all(),
                $symfonyResponse->getContent()
            );

            $symfonyRequest = null;
            $symfonyResponse = null;

            /*
             * Catching errors and sending to syslog.
             */
        } catch (\Throwable $exception) {
            $messages[] = new ConsoleException($exception);
            $httpResponse = new \React\Http\Response(
                400,
                ['Content-Type' => 'text/plain'],
                $exception->getMessage()
            );
        }

        return [$httpResponse, $messages];
    }

    /**
     * Echo favicon.
     *
     * @return \React\Http\Response
     */
    private function createFaviconResponse()
    {
        return new ReactResponse(
            200,
            [
                'cache-control' => 'max-age=31556926, public, s-maxage=31556926',
                'access-control-allow-origin' => '*',
                'Content-Type' => 'image/ico',
                'Content-Length' => 0,
            ],
            ''
        );
    }

    /**
     * Apply response encoding
     *
     * @param Request $request
     * @param Response $response
     */
    private function applyResponseEncoding(
        Request $request,
        Response $response
    )
    {
        $allowedCompressionAsString = $request
            ->headers
            ->get('Accept-Encoding');

        if (!$allowedCompressionAsString) {
            return;
        }

        $allowedCompression = explode(',', $allowedCompressionAsString);
        $allowedCompression = array_map('trim', $allowedCompression);

        if (in_array('gzip', $allowedCompression)) {
            $response->setContent(gzencode($response->getContent()));
            $response
                ->headers
                ->set('Content-Encoding', 'gzip');

            return;
        }

        if (in_array('deflate', $allowedCompression)) {
            $response->setContent(gzdeflate($response->getContent()));
            $response
                ->headers
                ->set('Content-Encoding', 'deflate');

            return;
        }
    }
}


<?php

namespace Sidus\BaseBundle\Translator;

use Symfony\Component\Translation\Exception\InvalidArgumentException;
use Symfony\Component\Translation\MessageCatalogueInterface;
use Symfony\Component\Translation\TranslatorBagInterface;
use Symfony\Component\Translation\TranslatorInterface;

/**
 * Overrides base translator to ignore translations when domain is false
 */
class TranslatorDecorator implements TranslatorInterface, TranslatorBagInterface
{
    /** @var TranslatorInterface */
    protected $translator;

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

    /**
     * {@inheritdoc}
     */
    public function trans($id, array $parameters = [], $domain = null, $locale = null)
    {
        if (false === $domain) {
            return strtr($id, $parameters);
        }

        return $this->translator->trans($id, $parameters, $domain, $locale);
    }

    /**
     * {@inheritdoc}
     */
    public function transChoice($id, $number, array $parameters = [], $domain = null, $locale = null)
    {
        if (false === $domain) {
            return strtr($id, $parameters);
        }

        return $this->translator->transChoice($id, $number, $parameters, $domain, $locale);
    }

    /**
     * {@inheritdoc}
     */
    public function setLocale($locale)
    {
        $this->translator->setLocale($locale);
    }

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

    /**
     * Gets the catalogue by locale.
     *
     * @param string|null $locale The locale or null to use the default
     *
     * @return MessageCatalogueInterface
     *
     * @throws InvalidArgumentException If the locale contains invalid characters
     */
    public function getCatalogue($locale = null)
    {
        if ($this->translator instanceof TranslatorBagInterface) {
            return $this->translator->getCatalogue($locale);
        }

        return null;
    }
}


<?php
/*
 * This file is part of the Sidus/BaseBundle package.
 *
 * Copyright (c) 2015-2019 Vincent Chalnot
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sidus\BaseBundle\DependencyInjection;

use Sidus\BaseBundle\DependencyInjection\Loader\ServiceLoader;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;

/**
 * You can directly inherit from this class to automatically load every files from your Resources/config/services dir.
 *
 * @author Vincent Chalnot <vincent@sidus.fr>
 */
class SidusBaseExtension extends Extension
{
    /**
     * {@inheritdoc}
     *
     * @throws \Exception
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $refl = new \ReflectionClass($this); // Supports for class extending this one
        $loader = new ServiceLoader($container);
        $serviceFolderPath = \dirname($refl->getFileName(), 2).'/Resources/config/services';
        $loader->loadFiles($serviceFolderPath);
    }
}


<?php
/*
 * This file is part of the {Package name}.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * Feel free to edit as you please, and have fun.
 *
 * @author Marc Morera <yuhu@mmoreram.com>
 */

namespace OneBundleApp\PPM;

use OneBundleApp\App\RequestHandler;
use PHPPM\Bridges\BridgeInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

/**
 * Class Bridge.
 */
class Bridge implements BridgeInterface
{
    /**
     * @var RequestHandler
     *
     * Request handler
     */
    private $requestHandler;

    /**
     * Bootstrap an application.
     *
     * @param string|null $appBootstrap
     * @param string      $appenv
     * @param bool        $debug
     */
    public function bootstrap($appBootstrap, $appenv, $debug)
    {
        $bootstrap = (new $appBootstrap());
        $bootstrap->initialize($appenv, $debug);
        $kernel = $bootstrap->getApplication();
        $this->requestHandler = new RequestHandler($kernel);
    }

    /**
     * Handle the request and return a response.
     *
     * @param ServerRequestInterface $request
     *
     * @return ResponseInterface
     */
    public function handle(ServerRequestInterface $request)
    {
        list($httpResponse, $_) = $this
            ->requestHandler
            ->handleServerRequest($request);

        return $httpResponse;
    }
}


<?php namespace Mosaiqo\OptimusPrime;

use Exception;
use Mosaiqo\OptimusPrime\Contracts\Autobots;

/**
 * Class Transformer
 * @package Mosaiqo\OptimusPrime
 */
abstract class Transformer implements Autobots
{

	/**
	 * Iterates whether a collection or an
	 * array to the transform method.
	 *
	 * @param array|collection $collection
	 * @return array
	 */
	public function transformCollection($collection)
	{
		if(is_object($collection))
			$collection = $collection->toArray()["data"];

		return array_map([$this , "transform"], $collection);
	}

	/**
	 * Abstract method to transform each item in the collection,
	 * you need to implement this in your inherited class.
	 *
	 * @param $item
	 * @throws Exception
	 * @return mixed
	 */
	public function transform($item)
	{
		throw new Exception("You have to implement your own transform method");
	}
}


<?php namespace Mosaiqo\OptimusPrime;

use ReflectionClass;
use Symfony\Component\Process\Exception\InvalidArgumentException;

/**
 * Class Transformable
 *
 * @package Mosaiqo\OptimusPrime
 */
trait Transformable
{

	protected $_defaultTransformer;

	/**
	 * Gets the transformer class name
	 *
	 * @return mixed
	 */
	public function getTransformer()
	{

		if (!property_exists($this , 'transformer') ||  !$this->transformer )
		{
			if(!$this->_defaultTransformer )
			{
				$this->createQualifiedTransformerClass();
			}
			return $this->_defaultTransformer;
		}

		return $this->transformer;
	}

	/**
	 * Sets the transformer class name
	 *
	 * @param $transformerClass
	 *
	 * @return $this
	 */
	public function setTransformer( $transformerClass )
	{
		$this->_defaultTransformer = $transformerClass;

		return $this;
	}

	/**
	 * @return mixed
	 */
	public function transform()
	{
		$transformer = $this->getTransformerClass();

		return $transformer->transform( $this );
	}

	/**
	 * Creates the transformer class name in base
	 * of the current class name.
	 */
	private function createQualifiedTransformerClass()
	{
		$reflection                = new ReflectionClass( __CLASS__ );
		$name                      = $reflection->getName();
		$qualifiedTransformerClass = $name . "Transformer";

		$this->setTransformer( $qualifiedTransformerClass );
	}

	/**
	 * @return mixed
	 */
	public function getTransformerClass()
	{

		$className = $this->getTransformer();

		if ( ! class_exists( $className ) )
		{
			throw new InvalidArgumentException( "The class {$className} does not exist." );
		}

		app()->bind( 'transformer', $className );
		$transformer = app()->make( 'transformer' );

		if ( ! method_exists( $transformer, 'transform' ) )
		{
			throw new InvalidArgumentException( "The method 'transform' must exist in class {$className}." );
		}

		return $transformer;
	}
}


<?php
/**
 * logging middleware used as closure/callable
 */
namespace Graviton\PhpProxy\Guzzle\Middleware;

use Monolog\Logger;
use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

/**
 * @author  List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
 * @license https://opensource.org/licenses/MIT MIT License
 * @link    http://swisscom.ch
 */
class Logging
{
    /**
     * returns the middleware closure
     *
     * @param Logger $logger
     * @param string $type
     * @param int    $maxMessageLength
     *
     * @return \Closure
     */
	public static function getCallable(Logger $logger, $type, $maxMessageLength)
	{
		return function (MessageInterface $message) use ($logger, $type, $maxMessageLength) {
			$startMessage = null;
			if ($message instanceof RequestInterface) {
				$startMessage = sprintf(
					'Proxy %s start: HTTP/%s %s %s',
					$type,
					$message->getProtocolVersion(),
					$message->getMethod(),
					$message->getRequestTarget()
				);
			} elseif ($message instanceof ResponseInterface) {
				$startMessage = sprintf(
					'Proxy %s start: HTTP/%s %s %s',
					$type,
					$message->getProtocolVersion(),
					$message->getStatusCode(),
					$message->getReasonPhrase()
				);
			}

			if (!is_null($startMessage)) {
				$logger->log(Logger::INFO, $startMessage);
			}

			// output headers
			foreach ($message->getHeaders() as $name => $value) {
				$logger->log(
					Logger::INFO,
					sprintf(
						"Proxy %s header: %s => %s",
						$type,
						$name,
						implode(', ', $value)
					)
				);
			}

			$body = $message->getBody();
			if (strlen($body) > $maxMessageLength) {
				$body = substr($body, 0, $maxMessageLength).'[TRUNCATED]';
			}

			$logger->log(
				Logger::INFO,
				sprintf(
					"Proxy %s body: %s",
					$type,
					$body
				)
			);

			if (!is_null($message) && $message->getBody()->isSeekable()) {
				$message->getBody()->rewind();
			}
			return $message;
		};
	}
}


<?php

/*
 * This file is part of the OneBundleApp package.
 *
 * Copyright (c) >=2017 Marc Morera
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * Feel free to edit as you please, and have fun.
 *
 * @author Marc Morera <yuhu@mmoreram.com>
 */

declare(strict_types=1);

namespace OneBundleApp\App;

/**
 * Class ComposerHook.
 */
class ComposerHook
{
    /**
     * Install one bundle app environment.
     */
    public static function installEnvironment()
    {
        self::installConsole();
        self::installReactServer();
        self::installWebServer();
    }

    /**
     * Install react server.
     */
    public static function installReactServer()
    {
        $appPath = __DIR__.'/../../../..';
        self::createFolderIfNotExists("$appPath/bin");
        self::createCopy(
            __DIR__,
            'server.php',
            "$appPath/bin",
            'server'
        );
        chmod("$appPath/bin/server", 0755);
    }

    /**
     * Install one bundle console.
     */
    public static function installConsole()
    {
        $appPath = __DIR__.'/../../../..';
        self::createFolderIfNotExists("$appPath/bin");
        self::createCopy(
            __DIR__,
            'console.php',
            "$appPath/bin",
            'console'
        );
        chmod("$appPath/bin/console", 0755);
    }

    /**
     * Install web server.
     */
    public static function installWebServer()
    {
        $appPath = __DIR__.'/../../../..';
        self::createFolderIfNotExists("$appPath/web");
        self::createCopy(
            __DIR__,
            'app.php',
            "$appPath/web",
            'app.php'
        );
        self::createCopy(
            __DIR__,
            'app_dev.php',
            "$appPath/web",
            'app_dev.php'
        );
    }

    /**
     * Create folder if not exists.
     *
     * @param string $path
     */
    private static function createFolderIfNotExists(string $path)
    {
        if (false === @mkdir($path, 0777, true) && !is_dir($path)) {
            throw new \RuntimeException(sprintf("Unable to create the %s directory\n", $path));
        }
    }

    /**
     * Make a copy of a file, from a folder, into a folder.
     *
     * @param string $from
     * @param string $fromFilename
     * @param string $to
     * @param string $toFilename
     */
    private static function createCopy(
        string $from,
        string $fromFilename,
        string $to,
        string $toFilename
    ) {
        if (file_exists("$to/$toFilename")) {
            unlink("$to/$toFilename");
        }

        copy(
            realpath($from)."/$fromFilename",
            realpath($to)."/$toFilename"
        );

        echo '> * Copy origin - '.realpath($from)."/$toFilename".PHP_EOL;
        echo '> * Copy destination - '.realpath($to)."/$toFilename".PHP_EOL;
    }
}


<?php
/*
 * This file is part of the Sidus/BaseBundle package.
 *
 * Copyright (c) 2015-2019 Vincent Chalnot
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sidus\BaseBundle\Doctrine;

use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;

/**
 * Simplified access to entities repository with proper exception management
 *
 * This class is not defined as a service so you can use this bundle without Doctrine
 *
 * @author Vincent Chalnot <vincent@sidus.fr>
 */
class RepositoryFinder
{
    /** @var ManagerRegistry */
    protected $doctrine;

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

    /**
     * @param string $className
     *
     * @return EntityRepository
     */
    public function getRepository($className)
    {
        $entityManager = $this->doctrine->getManagerForClass($className);
        if (!$entityManager instanceof EntityManagerInterface) {
            throw new \UnexpectedValueException("No manager found for class {$className}");
        }
        $repository = $entityManager->getRepository($className);
        if (!$repository instanceof EntityRepository) {
            throw new \UnexpectedValueException("No repository found for class {$className}");
        }

        return $repository;
    }
}


<?php

/*
 * This file is part of the OneBundleApp package.
 *
 * Copyright (c) >=2017 Marc Morera
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * Feel free to edit as you please, and have fun.
 *
 * @author Marc Morera <yuhu@mmoreram.com>
 */

declare(strict_types=1);

namespace OneBundleApp\App;

use Mmoreram\BaseBundle\Kernel\BaseKernel;
use Symfony\Component\Dotenv\Dotenv;

/**
 * Class AppFactory.
 */
class AppFactory
{
    /**
     * Create app.
     *
     * @param string $appPath
     * @param string $environment
     * @param bool   $debug
     *
     * @return BaseKernel
     */
    public static function createApp(
        string $appPath,
        string $environment,
        bool $debug
    ): BaseKernel {
        $envPath = $appPath.'/.env';
        if (file_exists($envPath)) {
            $dotenv = new Dotenv();
            $dotenv->load($envPath);
        }

        $oneBundleAppConfig = new \OneBundleApp\App\OneBundleAppConfig($appPath, $environment);
        \Symfony\Component\Debug\ErrorHandler::register();
        \Symfony\Component\Debug\ExceptionHandler::register();

        return new \Mmoreram\BaseBundle\Kernel\BaseKernel(
            $oneBundleAppConfig->getBundles(),
            $oneBundleAppConfig->getConfig(),
            $oneBundleAppConfig->getRoutes(),
            $environment,
            $debug,
            $appPath.'/var'
        );
    }
}


<?php
/*
 * This file is part of the Sidus/BaseBundle package.
 *
 * Copyright (c) 2015-2019 Vincent Chalnot
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sidus\BaseBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

/**
 * Allows to input the same kind of data in a choice validator than in a ChoiceType (unwrap choice groups)
 *
 * @author Vincent Chalnot <vincent@sidus.fr>
 *
 * @Annotation
 */
class ChoiceUnwrapperValidator extends ChoiceValidator
{
    /**
     * {@inheritdoc}
     */
    public function validate($value, Constraint $constraint)
    {
        if ($constraint instanceof ChoiceUnwrapper) {
            if ((null === $value || '' === $value) && $constraint->allowBlank) {
                return;
            }
            $resolvedChoices = [];
            if (\is_array($constraint->choices)) {
                /** @noinspection ForeachSourceInspection */
                foreach ($constraint->choices as $choice) {
                    if (\is_array($choice)) {
                        /** @var array $choice */
                        foreach ($choice as $subChoice) {
                            $resolvedChoices[] = $subChoice;
                        }
                    } else {
                        $resolvedChoices[] = $choice;
                    }
                }

                $constraint->choices = $resolvedChoices;
            }
        }

        parent::validate($value, $constraint);
    }
}


<?php
/*
 * This file is part of the Sidus/BaseBundle package.
 *
 * Copyright (c) 2015-2019 Vincent Chalnot
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sidus\BaseBundle\Serializer\Normalizer;

use Sidus\BaseBundle\Serializer\Annotation\NestedClass;
use Sidus\BaseBundle\Serializer\Annotation\NestedPropertyDenormalizer as NestedPropertyDenormalizerAnnotation;
use Doctrine\Common\Annotations\AnnotationReader;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;

/**
 * During denormalization: reads the NestedClass annotation to hydrate properties properly
 *
 * Utilise la réflection pour hydrater les valeurs de la même manière que le PropertyNormalizer
 *
 * @see PropertyNormalizer For global denormalization logic
 * @see NestedClass        The annotation used for type hinting
 *
 * @property NormalizerInterface|DenormalizerInterface $serializer
 */
class NestedPropertyDenormalizer extends PropertyNormalizer
{
    /** @var AnnotationReader */
    protected $annotationReader;

    /**
     * @param AnnotationReader $annotationReader
     */
    public function setAnnotationReader(AnnotationReader $annotationReader)
    {
        $this->annotationReader = $annotationReader;
    }

    /**
     * Only supports class with the NestedPropertyDenormalizer annotation
     *
     * {@inheritDoc}
     */
    public function supportsDenormalization($data, $type, $format = null)
    {
        if (!\class_exists($type)) {
            return false;
        }
        $classAnnotation = $this->annotationReader->getClassAnnotation(
            new \ReflectionClass($type),
            NestedPropertyDenormalizerAnnotation::class
        );

        return $classAnnotation instanceof NestedPropertyDenormalizerAnnotation;
    }

    /**
     * Never supports normalization, only denormalization
     *
     * {@inheritDoc}
     */
    public function supportsNormalization($data, $format = null)
    {
        return false;
    }

    /**
     * {@inheritdoc}
     *
     * @throws \UnexpectedValueException
     */
    protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = [])
    {
        try {
            $reflectionProperty = new \ReflectionProperty(get_class($object), $attribute);
        } catch (\ReflectionException $reflectionException) {
            return;
        }

        if ($reflectionProperty->isStatic()) {
            return;
        }

        // Override visibility
        if (!$reflectionProperty->isPublic()) {
            $reflectionProperty->setAccessible(true);
        }

        $nestedClassAnnotation = $this->annotationReader->getPropertyAnnotation(
            $reflectionProperty,
            NestedClass::class
        );
        if ($nestedClassAnnotation instanceof NestedClass) {
            $value = $this->denormalizeNested($nestedClassAnnotation, $value);
        }

        $reflectionProperty->setValue($object, $value);
    }

    /**
     * @param NestedClass $nestedClassAnnotation
     * @param mixed       $value
     *
     * @throws \UnexpectedValueException
     *
     * @return mixed
     */
    protected function denormalizeNested(NestedClass $nestedClassAnnotation, $value)
    {
        if (!$nestedClassAnnotation->multiple) {
            return $this->serializer->denormalize($value, $nestedClassAnnotation->targetClass);
        }

        if (!\is_iterable($value)) {
            throw new \UnexpectedValueException('Value should be an array');
        }
        $values = [];
        /** @var array $value */
        foreach ($value as $item) {
            $values[] = $this->serializer->denormalize($item, $nestedClassAnnotation->targetClass);
        }

        return $values;
    }
}


<?php namespace Mosaiqo\OptimusPrime\Console;

use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;

class TransformerGenerate extends Command {

	/**
	 * The console command name.
	 *
	 * @var string
	 */
	protected $name = 'transformer:generate';

	/**
	 * The console command description.
	 *
	 * @var string
	 */
	protected $description = 'Generates a transformer with the given arguments.';
	/**
	 * @var TransformerGenerator
	 */
	private $generator;

	/**
	 * Create a new command instance.
	 *
	 * @param TransformerGenerator $generator
	 * @return \Mosaiqo\OptimusPrime\Console\TransformerGenerate
	 */
	public function __construct(TransformerGenerator $generator)
	{
		parent::__construct();
		$this->generator = $generator;
	}

	/**
	 * Execute the console command.
	 *
	 * @return mixed
	 */
	public function fire()
	{
		$path = $this->argument('path');
		$properties = $this->option('properties');
		$base = $this->option('base');

		$input = $this->parse($path, $properties);

		$this->generator->make($input, $this->getTemplate(), $this->getDestination($base, $path));

		$this->info($this->getThemeSong());
	}

	/**
	 * @param $base
	 * @param $path
	 * @return string
	 */
	public function getDestination($base, $path)
	{
		return "{$base}/{$path}.php";
	}

	/**
	 * @return string
	 */
	public function getTemplate()
	{
		return __DIR__ . '/stubs/transformer.stub';
	}

	/**
	 * @param $path
	 * @param $properties
	 * @return mixed
	 */
	public function parse($path, $properties)
	{
		$segments = explode('\\', str_replace('/', '\\',$path));
		$name = array_pop($segments);
		$namespace = implode('\\', $segments);

		$properties = preg_split('/ ?, ?/', $properties, null, PREG_SPLIT_NO_EMPTY);

		return compact('name','namespace','properties');
	}

	/**
	 * @return string
	 */
	public function getThemeSong()
	{
		return "The Transformers! More than meets the eye!\r\nAutobots wage their battle to destroy the evil forces of the Decepticons!\r\nThe Transformers! Robots in disguise!\r\nThe Transformers! More than meets the eye!\r\nThe Transformers!";
	}

	/**
	 * Get the console command arguments.
	 *
	 * @return array
	 */
	protected function getArguments()
	{
		return array(
			array('path', InputArgument::REQUIRED, 'The class path for the command to generate.'),
		);
	}

	/**
	 * Get the console command options.
	 *
	 * @return array
	 */
	protected function getOptions()
	{
		return array(
			array('properties', null, InputOption::VALUE_OPTIONAL, 'A comma-separated list of properties for the command.', "id"),
			array('base', null, InputOption::VALUE_OPTIONAL, 'The path to where your domain root is located.', "app"),
		);
	}

}


<?php
/*
 * This file is part of the Sidus/BaseBundle package.
 *
 * Copyright (c) 2015-2019 Vincent Chalnot
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sidus\BaseBundle\Utilities;

use Symfony\Component\VarDumper\Caster\Caster;
use Symfony\Component\VarDumper\Caster\CutStub;

/**
 * Easy debug info builder with exclusion support
 */
class DebugInfoUtility
{
    /**
     * @param object $a
     * @param array  $excludedProperties
     *
     * @return array
     */
    public static function debugInfo($a, array $excludedProperties = [])
    {
        $a = Caster::castObject($a, \get_class($a), false);
        foreach ($a as $k => $v) {
            if (\in_array($k, $excludedProperties, true)) {
                $a[$k] = new CutStub($a[$k]);
            }
        }

        return $a;
    }
}


<?php
/*
 * This file is part of the Sidus/BaseBundle package.
 *
 * Copyright (c) 2015-2019 Vincent Chalnot
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sidus\BaseBundle\Utilities;

use DateTime;
use UnexpectedValueException;

/**
 * @see    DateTimeUtility::parse
 *
 * @author Vincent Chalnot <vincent@sidus.fr>
 */
class DateTimeUtility
{
    /**
     * Parse a datetime, allowing either DateTime objects (passthrough), Unix timestamps as integers or valid ATOM or
     * ISO8601 string
     *
     * @param DateTime|int|string $data
     * @param bool                $allowNull
     *
     * @throws UnexpectedValueException
     *
     * @return DateTime
     */
    public static function parse($data, $allowNull = true)
    {
        if (null === $data) {
            if ($allowNull) {
                return null;
            }

            throw new UnexpectedValueException('Expecting DateTime or timestamp, null given');
        }

        if ($data instanceof DateTime) {
            return $data;
        }

        if (\is_int($data)) {
            if (0 === $data) {
                throw new UnexpectedValueException('Expecting timestamp, numeric value "0" given');
            }
            $date = new DateTime();
            $date->setTimestamp($data);

            return $date;
        }
        $date = DateTime::createFromFormat(DateTime::ATOM, $data);
        if (!$date) { // Trying deprecated pattern
            /** @noinspection DateTimeConstantsUsageInspection */
            $date = DateTime::createFromFormat(DateTime::ISO8601, $data);
        }
        if (!$date && '' !== $data) {
            try {
                $date = new DateTime($data);
            } catch (\Exception $e) {
                $date = null;
            }
        }
        if (!$date) {
            throw new UnexpectedValueException(
                "Unable to parse DateTime value: '{$data}' expecting DateTime or timestamp"
            );
        }

        return $date;
    }
}


<?php
/*
 * This file is part of the Sidus/BaseBundle package.
 *
 * Copyright (c) 2015-2019 Vincent Chalnot
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sidus\BaseBundle\Utilities;

/**
 * Use this class to output the proper values for the magic __sleep method when you want to exclude certain properties
 *
 * @author Vincent Chalnot <vincent@sidus.fr>
 */
class SleepUtility
{
    /**
     * @param string $class
     * @param array  $excludedProperties
     *
     * @return array
     */
    public static function sleep($class, array $excludedProperties = [])
    {
        $propertyNames = [];
        $refl = new \ReflectionClass($class);
        foreach ($refl->getProperties() as $property) {
            if (\in_array($property->getName(), $excludedProperties, true)) {
                continue;
            }
            $propertyNames[] = $property->getName();
        }

        return $propertyNames;
    }
}


<?php
/*
 * This file is part of the Sidus/BaseBundle package.
 *
 * Copyright (c) 2015-2019 Vincent Chalnot
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sidus\BaseBundle\Utilities;

use Symfony\Component\Translation\TranslatorBagInterface;
use Symfony\Component\Translation\TranslatorInterface;

/**
 * @see    TranslatorUtility::tryTranslate
 *
 * @author Vincent Chalnot <vincent@sidus.fr>
 */
class TranslatorUtility
{
    /**
     * Will check the translator for the provided keys and humanize the code if no translation is found
     *
     * @param TranslatorInterface $translator
     * @param string|array        $tIds
     * @param array               $parameters
     * @param string              $fallback
     * @param bool                $humanizeFallback
     *
     * @return string
     */
    public static function tryTranslate(
        TranslatorInterface $translator,
        $tIds,
        array $parameters = [],
        $fallback =
        null,
        $humanizeFallback =
        true
    ) {
        foreach ((array) $tIds as $tId) {
            try {
                if ($translator instanceof TranslatorBagInterface) {
                    if ($translator->getCatalogue()->has($tId)) {
                        /** @noinspection PhpUndefinedMethodInspection */

                        return $translator->trans($tId, $parameters);
                    }
                } elseif ($translator instanceof TranslatorInterface) {
                    $label = $translator->trans($tId, $parameters);
                    if ($label !== $tId) {
                        return $label;
                    }
                }
            } catch (\InvalidArgumentException $e) {
                // Do nothing
            }
        }

        if (null === $fallback) {
            return null;
        }
        if (!$humanizeFallback) {
            return $fallback;
        }
        $pattern = '/(?!^)[A-Z]{2,}(?=[A-Z][a-z])|[A-Z][a-z]|\d{1,}/';

        return str_replace('_', ' ', preg_replace($pattern, ' $0', $fallback));
    }
}


<?php
/*
 * This file is part of the Sidus/BaseBundle package.
 *
 * Copyright (c) 2015-2019 Vincent Chalnot
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sidus\BaseBundle\DependencyInjection\Loader;

use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\Finder\Finder;

/**
 * Loads all configuration files from a folder
 *
 * @author Vincent Chalnot <vincent@sidus.fr>
 */
class ServiceLoader
{
    /** @var ContainerBuilder */
    protected $container;

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

    /**
     * @param string $path
     * @param string $extension
     *
     * @throws \Exception
     */
    public function loadFiles(string $path, $extension = 'yml')
    {
        $finder = new Finder();
        $finder->in($path)->name('*.'.$extension)->files();
        $loader = new YamlFileLoader($this->container, new FileLocator($path));
        foreach ($finder as $file) {
            $loader->load($file->getFilename());
        }
    }
}


<?php
/*
 * This file is part of the Sidus/BaseBundle package.
 *
 * Copyright (c) 2015-2019 Vincent Chalnot
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sidus\BaseBundle\Action;

use Psr\Container\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;

/**
 * Tries to resolve the current controller from the route name
 *
 * @author Vincent Chalnot <vincent@sidus.fr>
 */
class ControllerResolver implements ControllerResolverInterface
{
    /** @var ContainerInterface */
    protected $container;

    /** @var ControllerResolverInterface */
    protected $baseControllerResolver;

    /**
     * @param ContainerInterface          $container
     * @param ControllerResolverInterface $baseControllerResolver
     */
    public function __construct(ContainerInterface $container, ControllerResolverInterface $baseControllerResolver)
    {
        $this->container = $container;
        $this->baseControllerResolver = $baseControllerResolver;
    }

    /**
     * {@inheritdoc}
     */
    public function getController(Request $request)
    {
        if (!$request->attributes->get('_controller')) {
            $route = $request->attributes->get('_route');
            if (!$route || !$this->container->has($route)) {
                return false;
            }

            $request->attributes->set('_controller', $route);
        }

        return $this->baseControllerResolver->getController($request);
    }

    /**
     * {@inheritdoc}
     */
    public function getArguments(Request $request, $controller)
    {
        return $this->baseControllerResolver->getArguments($request, $controller);
    }
}


<?php

/*
 * This file is part of the OneBundleApp package.
 *
 * Copyright (c) >=2017 Marc Morera
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * Feel free to edit as you please, and have fun.
 *
 * @author Marc Morera <yuhu@mmoreram.com>
 */

declare(strict_types=1);

namespace OneBundleApp\App;

/**
 * Class ConsoleMessage.
 */
class ConsoleMessage implements Printable
{
    protected $url;
    protected $method;
    protected $code;
    protected $message;
    protected $elapsedTime;

    /**
     * Message constructor.
     *
     * @param string $url
     * @param string $method
     * @param int    $code
     * @param string $message
     * @param int    $elapsedTime
     */
    public function __construct(
        string $url,
        string $method,
        int $code,
        string $message,
        int $elapsedTime
    ) {
        $this->url = $url;
        $this->method = $method;
        $this->code = $code;
        $this->message = $message;
        $this->elapsedTime = $elapsedTime;
    }

    /**
     * Print.
     */
    public function print()
    {
        $method = str_pad($this->method, 6, ' ');
        $color = '32';
        if ($this->code >= 300 && $this->code < 400) {
            $color = '33';
        } elseif ($this->code >= 400) {
            $color = '31';
        }

        echo "\033[01;{$color}m".$this->code."\033[0m";
        echo " $method $this->url ";
        echo "(\e[00;37m".$this->elapsedTime.' ms | '.((int) (memory_get_usage() / 1000000))." MB\e[0m)";
        if ($this->code >= 300) {
            echo " - \e[00;37m".$this->messageInMessage($this->message)."\e[0m";
        }
        echo PHP_EOL;
    }

    /**
     * Find message.
     *
     * @param string $message
     *
     * @return string
     */
    private function messageInMessage(string $message): string
    {
        $decodedMessage = json_decode($message, true);
        if (
            is_array($decodedMessage) &&
            isset($decodedMessage['message']) &&
            is_string($decodedMessage['message'])
        ) {
            return $decodedMessage['message'];
        }

        return $message;
    }
}


<?php
/*
 * This file is part of the Sidus/BaseBundle package.
 *
 * Copyright (c) 2015-2019 Vincent Chalnot
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sidus\BaseBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
use Symfony\Component\DependencyInjection\Reference;

/**
 * Generic compiler pass to add tagged services to another service
 *
 * @author Vincent Chalnot <vincent@sidus.fr>
 */
class GenericCompilerPass implements CompilerPassInterface
{
    /** @var string */
    protected $registry;

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

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

    /** @var bool */
    protected $withPriority;

    /**
     * @param string $registry
     * @param string $tag
     * @param string $method
     * @param bool   $withPriority
     */
    public function __construct($registry, $tag, $method, $withPriority = false)
    {
        $this->registry = $registry;
        $this->tag = $tag;
        $this->method = $method;
        $this->withPriority = $withPriority;
    }

    /**
     * Inject tagged services into defined registry
     *
     * @api
     *
     * @param ContainerBuilder $container
     *
     * @throws InvalidArgumentException
     */
    public function process(ContainerBuilder $container)
    {
        if (!$container->has($this->registry)) {
            return;
        }

        $definition = $container->findDefinition($this->registry);
        $taggedServices = $container->findTaggedServiceIds($this->tag);

        foreach ($taggedServices as $id => $tags) {
            $arguments = [new Reference($id)];
            if ($this->withPriority) {
                $arguments[] = $this->resolvePriority($tags);
            }
            $definition->addMethodCall($this->method, $arguments);
        }
    }

    /**
     * @param array $tags
     *
     * @return int
     */
    protected function resolvePriority(array $tags)
    {
        foreach ($tags as $tag) {
            if (array_key_exists('priority', $tag)) {
                return (int) $tag['priority'];
            }
        }

        return 0;
    }
}


<?php
/*
 * This file is part of the Sidus/BaseBundle package.
 *
 * Copyright (c) 2015-2019 Vincent Chalnot
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sidus\BaseBundle\Translator;

use Sidus\BaseBundle\Utilities\TranslatorUtility;
use Symfony\Component\Translation\TranslatorInterface;

/**
 * Used to try multiple translations with fallback
 *
 * @author Vincent Chalnot <vincent@sidus.fr>
 */
trait TranslatableTrait
{
    /**
     * @var TranslatorInterface
     */
    protected $translator;

    /**
     * @param TranslatorInterface $translator
     */
    public function setTranslator(TranslatorInterface $translator)
    {
        $this->translator = $translator;
    }

    /**
     * Will check the translator for the provided keys and humanize the code if no translation is found
     *
     * @param string|array $tIds
     * @param array        $parameters
     * @param string       $fallback
     * @param bool         $humanizeFallback
     *
     * @return string
     */
    protected function tryTranslate($tIds, array $parameters = [], $fallback = null, $humanizeFallback = true)
    {
        return TranslatorUtility::tryTranslate($this->translator, $tIds, $parameters, $fallback, $humanizeFallback);
    }
}


<?php
/*
 * This file is part of the Sidus/BaseBundle package.
 *
 * Copyright (c) 2015-2019 Vincent Chalnot
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sidus\BaseBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

/**
 * Allows to input the iterable values instead of only arrays for choice validation
 *
 * @author Vincent Chalnot <vincent@sidus.fr>
 *
 * @Annotation
 */
class ChoiceValidator extends \Symfony\Component\Validator\Constraints\ChoiceValidator
{
    /**
     * {@inheritdoc}
     */
    public function validate($value, Constraint $constraint)
    {
        if ($value instanceof \Traversable) {
            $value = iterator_to_array($value);
        }

        parent::validate($value, $constraint);
    }
}


<?php
/*
 * This file is part of the Sidus/BaseBundle package.
 *
 * Copyright (c) 2015-2019 Vincent Chalnot
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Sidus\BaseBundle\Form\Extension;

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;

/**
 * Allow multiple choices to handle iterators
 *
 * @author Vincent Chalnot <vincent@sidus.fr>
 */
class ChoiceTypeExtension extends AbstractTypeExtension
{
    /**
     * @param FormBuilderInterface $builder
     * @param array                $options
     *
     * @throws \InvalidArgumentException
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->addModelTransformer(
            new CallbackTransformer(
                function ($toView) {
                    if ($toView instanceof \Traversable) {
                        return iterator_to_array($toView);
                    }

                    return $toView;
                },
                function ($toModel) {
                    return $toModel;
                }
            )
        );
    }

    /**
     * Returns the name of the type being extended.
     *
     * @return string The name of the type being extended
     */
    public function getExtendedType()
    {
        return ChoiceType::class;
    }
}


<?php

namespace PeskyCMF\Db\Traits;

use PeskyCMF\Db\CmfDbTable;
use PeskyORM\Core\DbExpr;
use PeskyORM\ORM\RecordsArray;
use PeskyORM\ORM\RecordsSet;
use Swayok\Utils\Set;

trait CacheForDbSelects {

    /**
     * @var bool|null
     */
    static protected $_cachingIsPossible;

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

    /**
     * Override to change default value
     * (NOT FOR AUTO CACHEING)
     * Default cache timeout for "select one" queries
     * @return int
     */
    abstract public function getDefaultCacheDurationForSelectOneInMinutes();

    /**
     * Override to change default value
     * (NOT FOR AUTO CACHEING)
     * Default cache timeout for "select many" queries
     * @return int
     */
    abstract public function getDefaultCacheDurationForSelectManyInMinutes();

    /**
     * Override to change default value
     * @return boolean
     */
    abstract public function canCleanRelationsCache();

    /**
     * Override to change default value
     * @return boolean
     */
    abstract public function canAutoCacheSelectOneQueries();

    /**
     * Allow/disallow cache for DbModel->expression().
     * Cache timeout provided by $this->getDefaultCacheDurationForSelectOneInMinutes()
     * Override to change default value
     * @return boolean
     */
    public function canAutoCacheExpressionQueries() {
        return $this->canAutoCacheSelectOneQueries();
    }

    /**
     * Override to change default value
     * @return boolean
     */
    abstract public function canAutoCacheSelectManyQueries();

    /**
     * Override to change default value
     * This is used only when $this->isAutoCacheForSelectOneAllowed() === true
     * @return int
     */
    abstract public function getAutoCacheTimeoutForSelectOneInMinutes();

    /**
     * Override to change default value
     * This is used only when $this->isAutoCacheForSelectAllAllowed() === true
     * @return int
     */
    abstract public function getAutoCacheTimeoutForSelectManyInMinutes();

    /**
     * Detect if caching is possible
     * @return bool
     */
    abstract protected function cachingIsPossible();

    /**
     * Cache key builder
     * Override if you wish to change algorythm
     * @param string|array $columns
     * @param null|array|string $conditionsAndOptions
     * @return string
     * @throws \InvalidArgumentException
     */
    static public function buildCacheKey($columns = '*', array $conditionsAndOptions = []) {
        foreach ($conditionsAndOptions as &$value) {
            if ($value instanceof DbExpr) {
                $value = $value->get();
            } else if (is_object($value)) {
                throw new \InvalidArgumentException(
                    '$conditionsAndOptions argument may contain only strings and objects of class \PeskyORM\Core\DbExpr.'
                    . ' Object of class ' . get_class($value) . ' detected'
                );
            }
        }
        unset($value);
        if (is_array($columns)) {
            foreach ($columns as &$value) {
                if ($value instanceof DbExpr) {
                    $value = $value->get();
                } else if (is_object($value)) {
                    throw new \InvalidArgumentException(
                        '$columns argument may contain only strings and objects of class \PeskyORM\Core\DbExpr.'
                        . ' Object of class ' . get_class($value) . ' detected'
                    );
                }
            }
            unset($value);
        } else if ($columns instanceof DbExpr) {
            $columns = $columns->get();
        }
        return hash('sha256', json_encode(array($columns, $conditionsAndOptions)));
    }

/** ============ Next methods does not contain any setting or normally overridable content ============ */

    /**
     * Set custom cache timeout for next query in this model
     * @param int|\DateTime|bool $minutes
     * @return $this
     */
    public function withCacheTimeout($minutes) {
        /** @var CmfDbTable|CacheForDbSelects $this */
        $this->_cacheTimeoutForNextSelect = $minutes;
        return $this;
    }

    /**
     * Removes custom cache timeout for next query
     * @return $this
     */
    public function cleanCacheTimeout() {
        $this->_cacheTimeoutForNextSelect = null;
        return $this;
    }

    /**
     * @return int
     */
    private function _getCacheDurationForSelectOneInMinutes() {
        /** @var CmfDbTable|CacheForDbSelects $this */
        return $this->_cacheTimeoutForNextSelect === null
            ? $this->getDefaultCacheDurationForSelectOneInMinutes()
            : $this->_cacheTimeoutForNextSelect;
    }

    /**
     * Override this to change default value
     * @return int
     */
    private function _getCacheDurationForSelectManyInMinutes() {
        /** @var CmfDbTable|CacheForDbSelects $this */
        return $this->_cacheTimeoutForNextSelect === null
            ? $this->getDefaultCacheDurationForSelectManyInMinutes()
            : $this->_cacheTimeoutForNextSelect;
    }

    /**
     * @param bool|true $cleanRelatedModelsCache - true: clean cache for all related models
     */
    abstract public function cleanModelCache($cleanRelatedModelsCache = null);

    /**
     * Clean cache for all related models
     * @param bool|null $cleanRelatedModelsCache
     */
    abstract public function cleanRelatedModelsCache($cleanRelatedModelsCache = true);

    /**
     * @param bool $cleanRelatedModelsCache
     */
    abstract public function cleanSelectManyCache($cleanRelatedModelsCache = null);

    /**
     * @param bool $cleanRelatedModelsCache
     */
    abstract public function cleanSelectOneCache($cleanRelatedModelsCache = null);

    /**
     * @param int|string|array|Object $record
     * @param bool $cleanRelatedModelsCache
     */
    abstract public function cleanRecordCache($record, $cleanRelatedModelsCache = null);

    /**
     * @return string
     * @throws \UnexpectedValueException
     * @throws \PeskyORM\Exception\OrmException
     * @throws \InvalidArgumentException
     * @throws \BadMethodCallException
     */
    public function getModelCachePrefix() {
        /** @var CmfDbTable|CacheForDbSelects $this */
        $structure = $this::getStructure();
        $parts = [
            $structure::getConnectionName(false),
            $structure::getSchema(),
            $this::getConnection(false)->getConnectionConfig()->getDbName(),
            $this::getAlias()
        ];
        return implode('.', $parts) . '.';
    }

    /**
     * Get data from cache or put data from $callback to cache (for external use)
     * @param bool $affectsSingleRecord
     * @param array|string $cacheSettings - array: settings; string: cache key
     *      array: [
     *          'key' => 'string, cache key',
     *          'timeout' => 'int (minutes or unix timestamp), \DateTime, null (infinite)',
     *          'tags' => ['custom', 'cache', 'tags'],
     *          'recache' => 'bool, ignore cached data and replace it with fresh data'
     *      ]
     * @param \Closure $callback
     * @return array
     * @throws \InvalidArgumentException
     */
    public function getCachedData($affectsSingleRecord, $cacheSettings, \Closure $callback) {
        $defaultTimeout = $affectsSingleRecord
                ? $this->_getCacheDurationForSelectOneInMinutes()
                : $this->_getCacheDurationForSelectManyInMinutes();
        $resolvedCacheSettings = $this->resolveCacheSettings(
            $cacheSettings,
            $defaultTimeout,
            function () {
                throw new \InvalidArgumentException('$cacheSettings must contain a "key" key (if array) or be the cache key (if string)');
            }
        );
        if (is_array($cacheSettings)) {
            return $this->_getCachedData($affectsSingleRecord, $resolvedCacheSettings, $callback);
        } else {
            return $callback();
        }
    }

    /**
     * Get data from cache or put data from $callback to cache (for internal use)
     * @param bool $affectsSingleRecord
     * @param array $cacheSettings - prepared cache settings. Always contains 'key' key
     * @param \Closure $callback
     * @return array
     */
    abstract protected function _getCachedData($affectsSingleRecord, array $cacheSettings, \Closure $callback);

    /**
     * @param bool $affectsSingleRecord
     * @param null|string|array $columns
     * @param null|array $conditionsAndOptions
     * @return string
     * @throws \UnexpectedValueException
     * @throws \PeskyORM\Exception\OrmException
     * @throws \BadMethodCallException
     * @throws \InvalidArgumentException
     */
    public function buildDefaultCacheKey($affectsSingleRecord, $columns, $conditionsAndOptions) {
        $mid = $affectsSingleRecord ? 'one.' : 'many.';
        return $this->getModelCachePrefix() . $mid . static::buildCacheKey($columns, $conditionsAndOptions);
    }

    /**
     * @param mixed $cacheSettings
     * @param int|bool|\DateTime $defaultTimeout
     * @param \Closure $defaultCacheKey
     * @return array|bool - bool: false - when caching is not possible
     */
    protected function resolveCacheSettings($cacheSettings, $defaultTimeout, \Closure $defaultCacheKey) {
        if (!is_array($cacheSettings)) {
            $tmp = [
                'timeout' => $defaultTimeout,
            ];
            if (is_numeric($cacheSettings) || $cacheSettings instanceof \DateTime) {
                $tmp['timeout'] = $cacheSettings;
            } else if (is_string($cacheSettings)) {
                $tmp['key'] = $cacheSettings;
            }
            $cacheSettings = $tmp;
        }
        if (empty($cacheSettings['key'])) {
            $cacheSettings['key'] = $defaultCacheKey();
        }
        if (empty($cacheSettings['tags'])) {
            $cacheSettings['tags'] = [];
        } else if (!is_array($cacheSettings['tags'])) {
            $cacheSettings['tags'] = [$cacheSettings['tags']];
        }
        // prevent possible cache-related problems in post/put/delete requests
        if (in_array(request()->method(), ['POST', 'PUT', 'DELETE'], true)) {
            $cacheSettings['recache'] = true;
        }
        $this->cleanCacheTimeout();
        return $cacheSettings;
    }

    /**
     * @param string|array $columns
     * @param null|array|string $conditionsAndOptions
     *  special key: 'CACHE'. Accepted values:
     *  - array: cache settings, accepted keys:
     *      'key' => optional
     *              string - cache key
     *              Default: $this->buildDefaultCacheKey(false, $columns, $conditionsAndOptions)
     *      'timeout' => optional
     *              int|DateTime - cache timeout
     *              false|null - cache for forever
     *              Default: $this->getCacheDurationForSelectAllInMinutes()
     *      'tags' => optional
     *              string|array - cache tags.
     *              Default: $this->alias
     *  - true: cache with default settings
     *  - numeric or \DateTime: cache timeout, key and tags are set to default values
     *  - string: cache tag, timeout and tags are set to default
     * @param \Closure $configurator
     * @return RecordsSet|RecordsArray
     * @throws \InvalidArgumentException
     */
    public function select($columns = '*', array $conditionsAndOptions = [], \Closure $configurator = null) {
        if ($this->cachingIsPossible()) {
            /** @var CmfDbTable|CacheForDbSelects $this */
            $hasCacheOption = is_array($conditionsAndOptions) && array_key_exists('CACHE', $conditionsAndOptions);
            if (
                $hasCacheOption
                || (
                    $this->canAutoCacheSelectManyQueries()
                    && $this->getAutoCacheTimeoutForSelectManyInMinutes() > 0
                )
            ) {
                $cacheSettings = $hasCacheOption
                    ? $conditionsAndOptions['CACHE']
                    : ['timeout' => $this->getAutoCacheTimeoutForSelectManyInMinutes()];
                unset($conditionsAndOptions['CACHE']);
                if ($cacheSettings !== false) {
                    $cacheSettings = $this->resolveCacheSettings(
                        $cacheSettings,
                        $this->_getCacheDurationForSelectManyInMinutes(),
                        function () use ($columns, $conditionsAndOptions) {
                            return $this->buildDefaultCacheKey(false, $columns, $conditionsAndOptions);
                        }
                    );
                    $records = $this->_getCachedData(false, $cacheSettings, function () use ($columns, $conditionsAndOptions, $configurator) {
                        return parent::select($columns, $conditionsAndOptions, $configurator);
                    });
                    return RecordsSet::createFromArray($this, $records, null);
                }
            }
        }
        unset($conditionsAndOptions['CACHE']);
        return parent::select($columns, $conditionsAndOptions, $configurator);
    }

    /**
     * @param string|array $columns
     * @param array $conditions
     * @param \Closure|null $configurator
     * @return RecordsSet|RecordsArray
     */
    public function selectFromCache($columns = '*', array $conditions = [], \Closure $configurator = null) {
        if ($this->cachingIsPossible()) {
            /** @var CmfDbTable|CacheForDbSelects $this */
            static::addCacheOptionToConditionsAndOptions($conditions);
        }
        return $this->select($columns, $conditions, $configurator);
    }

    /**
     * @param string $column
     * @param array $conditions
     * @param \Closure|null $configurator
     * @return array
     * @throws \UnexpectedValueException
     * @throws \PeskyORM\Exception\OrmException
     * @throws \PDOException
     * @throws \InvalidArgumentException
     * @throws \BadMethodCallException
     */
    public function selectColumnFromCache($column, array $conditions = [], \Closure $configurator = null) {
        if ($this->cachingIsPossible()) {
            /** @var CmfDbTable|CacheForDbSelects $this */
            static::addCacheOptionToConditionsAndOptions($conditions);
        }
        return $this->selectColumn($column, $conditions, $configurator);
    }

    /**
     * @param string $keysColumn
     * @param string $valuesColumn
     * @param array $conditions
     * @param \Closure $configurator
     * @return array
     * @throws \UnexpectedValueException
     * @throws \PeskyORM\Exception\OrmException
     * @throws \PDOException
     * @throws \InvalidArgumentException
     * @throws \BadMethodCallException
     */
    public function selectAssocFromCache($keysColumn, $valuesColumn, array $conditions = [], \Closure $configurator = null) {
        if ($this->cachingIsPossible()) {
            /** @var CmfDbTable|CacheForDbSelects $this */
            static::addCacheOptionToConditionsAndOptions($conditions);
        }
        return $this->selectAssoc($keysColumn, $valuesColumn, $conditions, $configurator);
    }

    /**
     * @param null|array|string $conditionsAndOptions
     * @param bool $cacheSettings
     */
    static private function addCacheOptionToConditionsAndOptions(array &$conditionsAndOptions, $cacheSettings = true) {
        $conditionsAndOptions['CACHE'] = $cacheSettings;
    }

    /**
     * @param array $conditions
     * @param \Closure $configurator
     * @param bool $removeNotInnerJoins
     * @return int
     */
    public function count(array $conditions = [], \Closure $configurator = null, $removeNotInnerJoins = false) {
        if ($this->cachingIsPossible()) {
            /** @var CmfDbTable|CacheForDbSelects $this */
            $hasCacheOption = is_array($conditions) && array_key_exists('CACHE', $conditions);
            if (
                $hasCacheOption
                || (
                    $this->canAutoCacheSelectManyQueries()
                    && $this->getAutoCacheTimeoutForSelectManyInMinutes() > 0
                )
            ) {
                $cacheSettings = $hasCacheOption
                    ? $conditions['CACHE']
                    : ['timeout' => $this->getAutoCacheTimeoutForSelectManyInMinutes()];
                unset($conditions['CACHE']);
                if ($cacheSettings !== false) {
                    /** @var array $cacheSettings */
                    $cacheSettings = $this->resolveCacheSettings(
                        $cacheSettings,
                        $this->_getCacheDurationForSelectManyInMinutes(),
                        function () use ($conditions) {
                            return $this->buildDefaultCacheKey(false, '__COUNT__', $conditions);
                        }
                    );
                    $cacheSettings['key'] .= '_count'; //< for DbModel->selectWithCount() method when cache key provided by user
                    $count = $this->_getCachedData(
                        false,
                        $cacheSettings,
                        function () use ($conditions, $configurator, $removeNotInnerJoins) {
                            return parent::count($conditions, $configurator, $removeNotInnerJoins);
                        }
                    );
                    return $count;
                }
            }
        }
        unset($conditions['CACHE']);
        return parent::count($conditions, $configurator, $removeNotInnerJoins);
    }

    /**
     * @param array $conditions
     * @param \Closure $configurator
     * @param bool $removeNotInnerJoins
     * @return int
     */
    public function countFromCache(array $conditions = [], \Closure $configurator = null, $removeNotInnerJoins = false) {
        if ($this->cachingIsPossible()) {
            /** @var CmfDbTable|CacheForDbSelects $this */
            static::addCacheOptionToConditionsAndOptions($conditions);
        }
        return $this->count($conditions, $configurator, $removeNotInnerJoins);
    }

    /**
     * @param DbExpr $expression
     * @param array $conditions
     * @param \Closure|null $configurator
     * @return array
     */
    public function selectValue(DbExpr $expression, array $conditions = [], \Closure $configurator = null) {
        if ($this->cachingIsPossible()) {
            /** @var CmfDbTable|CacheForDbSelects $this */
            $hasCacheOption = is_array($conditions) && array_key_exists('CACHE', $conditions);
            if (
                $hasCacheOption
                || (
                    $this->canAutoCacheExpressionQueries()
                    && $this->getAutoCacheTimeoutForSelectOneInMinutes() > 0
                )
            ) {
                $cacheSettings = $hasCacheOption
                    ? $conditions['CACHE']
                    : ['timeout' => $this->getAutoCacheTimeoutForSelectOneInMinutes()];
                unset($conditions['CACHE']);
                if ($cacheSettings !== false) {
                    /** @var array $cacheSettings */
                    $cacheSettings = $this->resolveCacheSettings(
                        $cacheSettings,
                        $this->getAutoCacheTimeoutForSelectOneInMinutes(),
                        function () use ($expression, $conditions) {
                            return $this->buildDefaultCacheKey(false, $expression, $conditions);
                        }
                    );
                    $result = $this->_getCachedData(
                        false,
                        $cacheSettings,
                        function () use ($expression, $conditions, $configurator) {
                            return parent::expression($expression, $conditions, $configurator);
                        }
                    );
                    return $result;
                }
            }
        }
        unset($conditions['CACHE']);
        return parent::expression($expression, $conditions, $configurator);
    }

    /**
     * @param DbExpr $expression
     * @param array $conditions
     * @param \Closure|null $configurator
     * @return mixed
     */
    public function selectValueFromCache(DbExpr $expression, array $conditions = [], \Closure $configurator = null) {
        if ($this->cachingIsPossible()) {
            /** @var CmfDbTable|CacheForDbSelects $this */
            static::addCacheOptionToConditionsAndOptions($conditions);
        }
        return $this->selectValue($expression, $conditions, $configurator);
    }

    /**
     * Get 1 record from DB
     * @param string|array $columns
     * @param null|array|string|int $conditions -
     *      array|string: conditions,
     *      numeric|int: record's pk value, automatically converted to array($this->primaryKey => $where)
     * @return array
     * @throws \UnexpectedValueException
     * @throws \PeskyORM\Exception\OrmException
     * @throws \InvalidArgumentException
     * @throws \BadMethodCallException
     */
    public function selectOneFromCache($columns, array $conditions, \Closure $configurator = null) {
        if ($this->cachingIsPossible()) {
            /** @var CmfDbTable|CacheForDbSelects $this */
            if (is_numeric($conditions) || is_int($conditions)) {
                $conditions = array(static::getPkColumnName() => $conditions);
            }
            static::addCacheOptionToConditionsAndOptions($conditions, true);
        }
        return $this->selectOne($columns, $conditions, $configurator);
    }

    /**
     * Get 1 record from DB
     * @param string|array $columns
     * @param null|array|string|int $conditions -
     *      array|string: conditions,
     *      numeric|int: record's pk value, automatically converted to array($this->primaryKey => $where)
     * @return array
     * @throws \UnexpectedValueException
     * @throws \PeskyORM\Exception\OrmException
     * @throws \PeskyORM\Exception\InvalidDataException
     * @throws \PDOException
     * @throws \InvalidArgumentException
     * @throws \BadMethodCallException
     */
    public function selectOneAsDbRecordFromCache($columns, array $conditions, \Closure $configurator = null) {
        if ($this->cachingIsPossible()) {
            /** @var CmfDbTable|CacheForDbSelects $this */
            if (is_numeric($conditions) || is_int($conditions)) {
                $conditions = array(static::getPkColumnName() => $conditions);
            }
            static::addCacheOptionToConditionsAndOptions($conditions, true);
        }
        return $this->selectOneAsDbRecord($columns, $conditions, $configurator);
    }

    /**
     * @inheritdoc
     * Also you can use 'CACHE' option. See description of select() method
     * @throws \PeskyORM\Exception\DbModelException
     * @throws \BadMethodCallException
     * @throws \PeskyORM\Exception\DbConnectionConfigException
     * @throws \PeskyORM\Exception\DbObjectException
     * @throws \InvalidArgumentException
     * @throws \PeskyORM\Exception\OrmException
     * @throws \UnexpectedValueException
     */
    public function selectOne($columns, array $conditions, \Closure $configurator = null) {
        if ($this->cachingIsPossible()) {
            /** @var CmfDbTable|CacheForDbSelects $this */
            if (empty($conditionsAndOptions)) {
                throw new DbModelException($this, 'Selecting one record without conditions is not allowed');
            }
            $hasCacheOption = is_array($conditionsAndOptions) && array_key_exists('CACHE', $conditionsAndOptions);
            if (
                $hasCacheOption
                || (
                    $this->canAutoCacheSelectOneQueries()
                    && $this->getAutoCacheTimeoutForSelectOneInMinutes() > 0
                )
            ) {
                $cacheSettings = $hasCacheOption
                    ? $conditionsAndOptions['CACHE']
                    : ['timeout' => $this->getAutoCacheTimeoutForSelectOneInMinutes()];
                unset($conditionsAndOptions['CACHE']);
                if ($cacheSettings !== false) {
                    $cacheSettings = $this->resolveCacheSettings(
                        $cacheSettings,
                        $this->_getCacheDurationForSelectOneInMinutes(),
                        function () use ($columns, $conditionsAndOptions) {
                            return $this->buildDefaultCacheKey(true, $columns, $conditionsAndOptions);
                        }
                    );
                    $record = $this->_getCachedData(true, $cacheSettings, function () use ($columns, $conditionsAndOptions) {
                        return parent::selectOne($columns, $conditionsAndOptions, false, false);
                    });
                    if ($asObject) {
                        $record = static::getOwnDbObject($record, false, true);
                    } else if ($withRootAlias) {
                        $record = array($this->getAlias() => $record);
                    }
                    return $record;
                }
            }
        }
        unset($conditionsAndOptions['CACHE']);
        return parent::selectOne($columns, $conditionsAndOptions, $asObject, $withRootAlias);
    }

    /**
     * @inheritdoc
     * @throws \BadMethodCallException
     * @throws \PeskyORM\Exception\DbConnectionConfigException
     * @throws \PeskyORM\Exception\DbModelException
     */
    public function insert($data, $returning = null) {
        $ret = parent::insert($data, $returning);
        if ($this->cachingIsPossible()) {
            $this->cleanSelectManyCache();
        }
        return $ret;
    }

    /**
     * @inheritdoc
     * @throws \BadMethodCallException
     * @throws \PeskyORM\Exception\DbConnectionConfigException
     * @throws \PeskyORM\Exception\DbModelException
     */
    public function insertMany($columnsNames, $rows, $returning = false) {
        $ret = parent::insertMany($columnsNames, $rows, $returning);
        if ($this->cachingIsPossible()) {
            $this->cleanSelectManyCache();
        }
        return $ret;
    }

    /**
     * @inheritdoc
     * @throws \BadMethodCallException
     * @throws \PeskyORM\Exception\DbConnectionConfigException
     * @throws \PeskyORM\Exception\DbModelException
     * @throws \PeskyORM\Exception\DbObjectException
     * @throws \InvalidArgumentException
     * @throws \PeskyORM\Exception\OrmException
     * @throws \UnexpectedValueException
     */
    public function update($data, $conditionsAndOptions = null, $returning = false) {
        /** @var CmfDbTable|CacheForDbSelects $this */
        $ret = parent::update($data, $conditionsAndOptions, $returning);
        if ($this->cachingIsPossible()) {
            if (!empty($ret[$this->getPkColumnName()])) {
                $ids = $ret[$this->getPkColumnName()];
            } else if (!empty($ret[0]) && !empty($ret[0][$this->getPkColumnName()])) {
                $ids = Set::extract('/' . $this->getPkColumnName());
            } else if (!empty($data[$this->getPkColumnName()])) {
                $ids = $data[$this->getPkColumnName()];
            } else if (!empty($conditionsAndOptions[$this->getPkColumnName()])) {
                $ids = $conditionsAndOptions[$this->getPkColumnName()];
            }
            if (!empty($ids)) {
                if (!is_array($ids)) {
                    $ids = array($ids);
                }
                foreach ($ids as $id) {
                    $this->cleanRecordCache($id);
                }
                $this->cleanSelectManyCache();
            } else {
                $this->cleanModelCache();
            }
        }
        return $ret;
    }

    /**
     * @inheritdoc
     * @throws \BadMethodCallException
     * @throws \PeskyORM\Exception\DbConnectionConfigException
     * @throws \PeskyORM\Exception\DbModelException
     * @throws \PeskyORM\Exception\DbObjectException
     * @throws \InvalidArgumentException
     * @throws \PeskyORM\Exception\OrmException
     * @throws \UnexpectedValueException
     */
    public function delete($conditionsAndOptions = null, $returning = false) {
        /** @var CmfDbTable|CacheForDbSelects $this */
        $ret = parent::delete($conditionsAndOptions, $returning);
        if ($this->cachingIsPossible()) {
            if (!empty($ret[$this->getPkColumnName()])) {
                $ids = $ret[$this->getPkColumnName()];
            } else if (!empty($ret[0]) && !empty($ret[0][$this->getPkColumnName()])) {
                $ids = Set::extract('/' . $ret[$this->getPkColumnName()]);
            } else if (!empty($conditionsAndOptions[$this->getPkColumnName()])) {
                $ids = $conditionsAndOptions[$this->getPkColumnName()];
            }
            if (!empty($ids)) {
                if (!is_array($ids)) {
                    $ids = array($ids);
                }
                foreach ($ids as $id) {
                    $this->cleanRecordCache($id);
                }
                $this->cleanSelectManyCache();
            } else {
                $this->cleanModelCache();
            }
        }
        return $ret;
    }
}


<?php

namespace SnooPHP\Model;

/**
 * Wrapper for array of models
 *
 * Mixed models are allowed, but some methods won't work properly
 *
 * @author sneppy
 */
class Collection
{
	/**
	 * @var Model[] $models underlying array of models
	 */
	protected $models;

	/**
	 * Create a new collection from a set of models
	 *
	 * @param Model[] $models set of models
	 */
	public function __construct(array $models)
	{
		$this->models = $models;
	}

	/**
	 * Return array of models
	 *
	 * @return Model[]
	 */
	public function array()
	{
		return $this->models;
	}

	/**
	 * Return number of models
	 *
	 * @return int
	 */
	public function size()
	{
		return count($this->models);
	}

	/**
	 * Return number of models
	 * @see $this::size()
	 *
	 * @return int
	 */
	public function num()
	{
		return $this->size();
	}

	/**
	 * Return true if empty
	 *
	 * @return bool
	 */
	public function empty()
	{
		return count($this->models) === 0;
	}

	/**
	 * Return i-th element (if 0 <= i < size)
	 *
	 * @return Model|null
	 */
	public function get($i = 0)
	{
		return count($this->models) > $i && $i >= 0 ? $this->models[$i] : null;
	}

	/**
	 * Return first element (if exists)
	 *
	 * @return Model|null
	 */
	public function first()
	{
		return $this->get();
	}

	/**
	 * Return last element (if exists)
	 *
	 * @return Model|null
	 */
	public function last()
	{
		return $this->get($this->size() - 1);
	}

	/**
	 * Find an element
	 *
	 * @param Callable|Model $criteria model to match or callable called on every element (return true if element match the criteria)
	 *
	 * @return Model|int|null	if model is given, return index or null if not found.
	 * 							If callable given, return matching element or null if none found.
	 * 							In any case returns only a single result
	 */
	public function find($criteria)
	{
		if (!$criteria)
		{
			error_log("collection error: no criteria specified");
			return null;
		}

		else if (is_callable($criteria))
		{
			foreach ($this->models as $model)
				if ($criteria($model)) return $model;
		}
		else if (is_a($criteria, "SnooPHP\Model\Model"))
		{
			foreach ($this->models as $i => $model)
				if ($model == $criteria) return $i;
		}

		return null;
	}

	// MODIFIERS

	/**
	 * Run function on each model instance (can only modify instance property)
	 *
	 * @param Callable	$iterator	function called on every element of the collection.
	 * 								Return true to repeat on current instance, return false to break.
	 * 								Note that you should define the parameter as a reference in order to affect it.
	 *
	 * @return static return this collection
	 */
	public function each(Callable $iterator)
	{
		// No callable specified
		if (!$iterator)
		{
			error_log("no callable specified");
			return $this;
		}

		foreach ($this->models as $i => $model)
		{
			do $ctrl = call_user_func_array($iterator, array(&$this->models[$i], $i)); while ($ctrl === true);
			if ($ctrl === false) break;
		}

		return $this;
	}

	/**
	 * Expands nodes within this collection
	 *
	 * @param string|array	$edges	edges to expand
	 * @param bool			$force	if true assumes all models are nodes
	 *
	 * @return static return this collection
	 */
	public function expand($edges = [], $force = false)
	{
		if ($force)	foreach ($this->models as $i => $model) $this->models[$i]->expand($edges);
		else		foreach ($this->models as $i => $model) if (is_a($model, "SnooPHP\Model\Node")) $this->models[$i]->expand($edges);
		return $this;
	}

	/**
	 * Append another collection
	 *
	 * @param Collection $collection collection to append
	 *
	 * @return static this collection
	 */
	public function append(Collection $collection)
	{
		if (!$collection) return $this;

		$this->models = array_merge($this->array(), $collection->array());
		return $this;
	}
}