<?php

namespace GeoJson;

use GeoJson\CoordinateReferenceSystem\CoordinateReferenceSystem;
use GeoJson\Exception\UnserializationException;

/**
 * Base GeoJson object.
 *
 * @see http://www.geojson.org/geojson-spec.html#geojson-objects
 * @since 1.0
 */
abstract class GeoJson implements \JsonSerializable, JsonUnserializable
{
    /**
     * @var BoundingBox
     */
    protected $boundingBox;

    /**
     * @var CoordinateReferenceSystem
     */
    protected $crs;

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

    /**
     * Return the BoundingBox for this GeoJson object.
     *
     * @return BoundingBox
     */
    public function getBoundingBox()
    {
        return $this->boundingBox;
    }

    /**
     * Return the CoordinateReferenceSystem for this GeoJson object.
     *
     * @return CoordinateReferenceSystem
     */
    public function getCrs()
    {
        return $this->crs;
    }

    /**
     * Return the type for this GeoJson object.
     *
     * @return string
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * @see http://php.net/manual/en/jsonserializable.jsonserialize.php
     */
    public function jsonSerialize()
    {
        $json = array('type' => $this->type);

        if (isset($this->crs)) {
            $json['crs'] = $this->crs->jsonSerialize();
        }

        if (isset($this->boundingBox)) {
            $json['bbox'] = $this->boundingBox->jsonSerialize();
        }

        return $json;
    }

    /**
     * @see JsonUnserializable::jsonUnserialize()
     */
    final public static function jsonUnserialize($json)
    {
        if ( ! is_array($json) && ! is_object($json)) {
            throw UnserializationException::invalidValue('GeoJson', $json, 'array or object');
        }

        $json = new \ArrayObject($json);

        if ( ! $json->offsetExists('type')) {
            throw UnserializationException::missingProperty('GeoJson', 'type', 'string');
        }

        $type = (string) $json['type'];
        $args = array();

        switch ($type) {
            case 'LineString':
            case 'MultiLineString':
            case 'MultiPoint':
            case 'MultiPolygon':
            case 'Point':
            case 'Polygon':
                if ( ! $json->offsetExists('coordinates')) {
                    throw UnserializationException::missingProperty($type, 'coordinates', 'array');
                }

                if ( ! is_array($json['coordinates'])) {
                    throw UnserializationException::invalidProperty($type, 'coordinates', $json['coordinates'], 'array');
                }

                $args[] = $json['coordinates'];
                break;

            case 'Feature':
                $geometry = isset($json['geometry']) ? $json['geometry'] : null;
                $properties = isset($json['properties']) ? $json['properties'] : null;

                if (isset($geometry) && ! is_array($geometry) && ! is_object($geometry)) {
                    throw UnserializationException::invalidProperty($type, 'geometry', $geometry, 'array or object');
                }

                if (isset($properties) && ! is_array($properties) && ! is_object($properties)) {
                    throw UnserializationException::invalidProperty($type, 'properties', $properties, 'array or object');
                }

                $args[] = isset($geometry) ? self::jsonUnserialize($geometry) : null;
                $args[] = isset($properties) ? (array) $properties : null;
                $args[] = isset($json['id']) ? $json['id'] : null;
                break;

            case 'FeatureCollection':
                if ( ! $json->offsetExists('features')) {
                    throw UnserializationException::missingProperty($type, 'features', 'array');
                }

                if ( ! is_array($json['features'])) {
                    throw UnserializationException::invalidProperty($type, 'features', $json['features'], 'array');
                }

                $args[] = array_map(array('self', 'jsonUnserialize'), $json['features']);
                break;

            case 'GeometryCollection':
                if ( ! $json->offsetExists('geometries')) {
                    throw UnserializationException::missingProperty($type, 'geometries', 'array');
                }

                if ( ! is_array($json['geometries'])) {
                    throw UnserializationException::invalidProperty($type, 'geometries', $json['geometries'], 'array');
                }

                $args[] = array_map(array('self', 'jsonUnserialize'), $json['geometries']);
                break;

            default:
                throw UnserializationException::unsupportedType('GeoJson', $type);
        }

        if (isset($json['bbox'])) {
            $args[] = BoundingBox::jsonUnserialize($json['bbox']);
        }

        if (isset($json['crs'])) {
            $args[] = CoordinateReferenceSystem::jsonUnserialize($json['crs']);
        }

        $class = sprintf('GeoJson\%s\%s', (strncmp('Feature', $type, 7) === 0 ? 'Feature' : 'Geometry'), $type);
        $class = new \ReflectionClass($class);

        return $class->newInstanceArgs($args);
    }

    /**
     * Set optional CRS and BoundingBox arguments passed to a constructor.
     *
     * @todo Decide if multiple CRS or BoundingBox instances should override a
     *       previous value or be ignored
     */
    protected function setOptionalConstructorArgs(array $args)
    {
        foreach ($args as $arg) {
            if ($arg instanceof CoordinateReferenceSystem) {
                $this->crs = $arg;
            }

            if ($arg instanceof BoundingBox) {
                $this->boundingBox = $arg;
            }
        }
    }
}


<?php

namespace GeoJson\Exception;

class UnserializationException extends \RuntimeException implements Exception
{
    /**
     * Creates an UnserializationException for a value with an invalid type.
     *
     * @param string $context
     * @param mixed $value
     * @param string $expectedType
     * @return UnserializationException
     */
    public static function invalidValue($context, $value, $expectedType)
    {
        return new self(sprintf(
            '%s expected value of type %s, %s given',
            $context,
            $expectedType,
            is_object($value) ? get_class($value) : gettype($value)
        ));
    }

    /**
     * Creates an UnserializationException for a property with an invalid type.
     *
     * @param string $context
     * @param string $property
     * @param mixed $value
     * @param string $expectedType
     * @return UnserializationException
     */
    public static function invalidProperty($context, $property, $value, $expectedType)
    {
        return new self(sprintf(
            '%s expected "%s" property of type %s, %s given',
            $context,
            $property,
            $expectedType,
            is_object($value) ? get_class($value) : gettype($value)
        ));
    }

    /**
     * Creates an UnserializationException for a missing property.
     *
     * @param string $context
     * @param string $property
     * @param string $expectedType
     * @return UnserializationException
     */
    public static function missingProperty($context, $property, $expectedType)
    {
        return new self(sprintf(
            '%s expected "%s" property of type %s, none given',
            $context,
            $property,
            $expectedType
        ));
    }

    /**
     * Creates an UnserializationException for an unsupported "type" property.
     *
     * @param string $context
     * @param string $value
     * @return UnserializationException
     */
    public static function unsupportedType($context, $value)
    {
        return new self(sprintf('Invalid %s type "%s"', $context, $value));
    }
}


<?php

namespace GeoJson\CoordinateReferenceSystem;

use GeoJson\Exception\UnserializationException;

/**
 * Named coordinate reference system object.
 *
 * @see http://www.geojson.org/geojson-spec.html#named-crs
 * @since 1.0
 */
class Named extends CoordinateReferenceSystem
{
    protected $type = 'name';

    /**
     * Constructor.
     *
     * @param string $name
     */
    public function __construct($name)
    {
        $this->properties = array('name' => (string) $name);
    }

    /**
     * Factory method for creating a Named CRS object from properties.
     *
     * @see CoordinateReferenceSystem::jsonUnserialize()
     * @param array|object $properties
     * @return Named
     * @throws UnserializationException
     */
    protected static function jsonUnserializeFromProperties($properties)
    {
        if ( ! is_array($properties) && ! is_object($properties)) {
            throw UnserializationException::invalidProperty('Named CRS', 'properties', $properties, 'array or object');
        }

        $properties = new \ArrayObject($properties);

        if ( ! $properties->offsetExists('name')) {
            throw UnserializationException::missingProperty('Named CRS', 'properties.name', 'string');
        }

        $name = (string) $properties['name'];

        return new self($name);
    }
}


<?php

namespace Joshbrw\LaravelModuleInstaller;

use Composer\Composer;
use Composer\IO\IOInterface;
use Composer\Package\PackageInterface;
use Composer\Installer\LibraryInstaller;

class LaravelModuleInstaller extends LibraryInstaller
{
    const DEFAULT_ROOT = "Modules";

    /**
     * Get the fully-qualified install path
     * {@inheritDoc}
     */
    public function getInstallPath(PackageInterface $package)
    {
        return $this->getBaseInstallationPath() . '/' . $this->getModuleName($package);
    }

    /**
     * Get the base path that the module should be installed into.
     * Defaults to Modules/ and can be overridden in the module's composer.json.
     * @return string
     */
    protected function getBaseInstallationPath()
    {
        if (!$this->composer || !$this->composer->getPackage()) {
            return self::DEFAULT_ROOT;
        }

        $extra = $this->composer->getPackage()->getExtra();

        if (!$extra || empty($extra['module-dir'])) {
            return self::DEFAULT_ROOT;
        }

        return $extra['module-dir'];
    }

    /**
     * Get the module name, i.e. "joshbrw/something-module" will be transformed into "Something"
     * @param PackageInterface $package
     * @return string
     * @throws \Exception
     */
    protected function getModuleName(PackageInterface $package)
    {
        $name = $package->getPrettyName();
        $split = explode("/", $name);

        if (count($split) !== 2) {
            throw new \Exception($this->usage());
        }

        $splitNameToUse = explode("-", $split[1]);

        if (count($splitNameToUse) < 2) {
            throw new \Exception($this->usage());
        }

        if (array_pop($splitNameToUse) !== 'module') {
            throw new \Exception($this->usage());
        }

        return implode('',array_map('ucfirst', $splitNameToUse));
    }

    /**
     * Get the usage instructions
     * @return string
     */
    protected function usage()
    {
        return "Ensure your package's name is in the format <vendor>/<name>-<module>";
    }

    /**
     * {@inheritDoc}
     */
    public function supports($packageType)
    {
        return 'laravel-module' === $packageType;
    }
}


<?php

namespace Nyholm\BundleTest;

use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Routing\RouteCollectionBuilder;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
class AppKernel extends Kernel
{
    /**
     * @var string[]
     */
    private $bundlesToRegister = [];

    /**
     * @var array
     */
    private $configFiles = [];

    /**
     * @var string
     */
    private $cachePrefix = '';

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

    /**
     * @var CompilerPassInterface[]
     */
    private $compilerPasses = [];

    /**
     * @param string $cachePrefix
     */
    public function __construct($cachePrefix)
    {
        parent::__construct($cachePrefix, true);
        $this->cachePrefix = $cachePrefix;
        $this->addBundle(FrameworkBundle::class);
        $this->addConfigFile(__DIR__.'/config/framework.yml');
    }

    /**
     * @param string $bundle
     */
    public function addBundle($bundleClassName)
    {
        $this->bundlesToRegister[] = $bundleClassName;
    }

    /**
     * @param string $configFile path to config file
     */
    public function addConfigFile($configFile)
    {
        $this->configFiles[] = $configFile;
    }

    public function getCacheDir()
    {
        return sys_get_temp_dir().'/NyholmBundleTest/'.$this->cachePrefix;
    }

    public function getLogDir()
    {
        return sys_get_temp_dir().'/NyholmBundleTest/log';
    }

    public function getProjectDir()
    {
        if (null === $this->fakedProjectDir) {
            return realpath(__DIR__.'/../../../../');
        }

        return $this->fakedProjectDir;
    }

    /**
     * @param string|null $rootDir
     */
    public function setRootDir($rootDir)
    {
        $this->rootDir = $rootDir;
    }

    /**
     * @param string|null $projectDir
     */
    public function setProjectDir($projectDir)
    {
        $this->fakedProjectDir = $projectDir;
    }

    public function registerBundles()
    {
        $this->bundlesToRegister = array_unique($this->bundlesToRegister);
        $bundles = [];
        foreach ($this->bundlesToRegister as $bundle) {
            $bundles[] = new $bundle();
        }

        return $bundles;
    }

    /**
     * (From MicroKernelTrait)
     * {@inheritdoc}
     */
    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader->load(function (ContainerBuilder $container) use ($loader) {
            $container->loadFromExtension('framework', [
                'router' => [
                    'resource' => 'kernel:loadRoutes',
                    'type' => 'service',
                ],
            ]);

            $this->configFiles = array_unique($this->configFiles);
            foreach ($this->configFiles as $path) {
                $loader->load($path);
            }

            $container->addObjectResource($this);
        });
    }

    /**
     * (From MicroKernelTrait).
     *
     * @internal
     */
    public function loadRoutes(LoaderInterface $loader)
    {
        $routes = new RouteCollectionBuilder($loader);
        $routes->import(__DIR__.'/config/routing.yml');

        return $routes->build();
    }

    /**
     * {@inheritdoc}
     */
    protected function buildContainer()
    {
        $container = parent::buildContainer();

        foreach ($this->compilerPasses as $pass) {
            $container->addCompilerPass($pass);
        }

        return $container;
    }

    /**
     * @param CompilerPassInterface[] $compilerPasses
     */
    public function addCompilerPasses(array $compilerPasses)
    {
        $this->compilerPasses = $compilerPasses;
    }
}


<?php

declare(strict_types=1);

/*
 * This file is part of the VariadicExtension package.
 *
 * (c) Kamil Kokot <kamil@kokot.me>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace FriendsOfBehat\VariadicExtension\Argument;

use Behat\Testwork\Argument\ArgumentOrganiser;
use ReflectionFunctionAbstract;

/**
 * Decorates a default argument organiser to support a variadic arguments also.
 * It will add all unused arguments to the end of argument list.
 */
final class VariadicArgumentOrganiser implements ArgumentOrganiser
{
    /**
     * @var ArgumentOrganiser
     */
    private $decoratedArgumentOrganiser;

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

    /**
     * {@inheritdoc}
     */
    public function organiseArguments(ReflectionFunctionAbstract $function, array $arguments): array
    {
        $organisedArguments = $this->decoratedArgumentOrganiser->organiseArguments($function, $arguments);

        if ($function->isVariadic()) {
            $organisedArguments += array_diff($arguments, $organisedArguments);
        }

        return $organisedArguments;
    }
}


<?php

declare(strict_types=1);

/*
 * This file is part of the VariadicExtension package.
 *
 * (c) Kamil Kokot <kamil@kokot.me>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace FriendsOfBehat\VariadicExtension\ServiceContainer;

use Behat\Testwork\Argument\ServiceContainer\ArgumentExtension;
use Behat\Testwork\ServiceContainer\Extension;
use Behat\Testwork\ServiceContainer\ExtensionManager;
use FriendsOfBehat\VariadicExtension\Argument\VariadicArgumentOrganiser;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

final class VariadicExtension implements Extension
{
    /**
     * {@inheritdoc}
     */
    public function getConfigKey(): string
    {
        return 'fob_variadic';
    }

    /**
     * {@inheritdoc}
     */
    public function initialize(ExtensionManager $extensionManager): void
    {
    }

    /**
     * {@inheritdoc}
     */
    public function configure(ArrayNodeDefinition $builder): void
    {
    }

    /**
     * {@inheritdoc}
     */
    public function load(ContainerBuilder $container, array $config): void
    {
        $definition = new Definition(VariadicArgumentOrganiser::class, [
            new Reference('fob_variadic.argument.mixed_organiser.inner'),
        ]);
        $definition->setDecoratedService(ArgumentExtension::MIXED_ARGUMENT_ORGANISER_ID);

        $container->setDefinition('fob_variadic.argument.mixed_organiser', $definition);
    }

    /**
     * {@inheritdoc}
     */
    public function process(ContainerBuilder $container): void
    {
    }
}


<?php

namespace Matthewbdaly\ArtisanStandalone\Base;

use Illuminate\Foundation\Application as BaseApplication;
use Illuminate\Contracts\Foundation\Application as ApplicationContract;
use RuntimeException;

class Application extends BaseApplication implements ApplicationContract
{

    /**
     * Get the path to the application "src" directory.
     *
     * @param  string $path Optionally, a path to append to the app path
     * @return string
     */
    public function path($path = '')
    {
        return $this->basePath.DIRECTORY_SEPARATOR.'src'.($path ? DIRECTORY_SEPARATOR.$path : $path);
    }

    /**
     * Get the path to the application configuration files.
     *
     * @param  string $path Optionally, a path to append to the config path
     * @return string
     */
    public function configPath($path = '')
    {
        return $this->basePath.DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.'matthewbdaly'.DIRECTORY_SEPARATOR.'artisan-standalone'.DIRECTORY_SEPARATOR.'config'.($path ? DIRECTORY_SEPARATOR.$path : $path);
    }

    /**
     * Get the path to the bootstrap directory.
     *
     * @param  string $path Optionally, a path to append to the bootstrap path
     * @return string
     */
    public function bootstrapPath($path = '')
    {
        return $this->basePath.DIRECTORY_SEPARATOR.'bootstrap'.($path ? DIRECTORY_SEPARATOR.$path : $path);
    }

    /**
     * Get the path to the storage directory.
     *
     * @return string
     */
    public function storagePath()
    {
        return $this->basePath.DIRECTORY_SEPARATOR.'storage';
    }

    /**
     * Get the path to the resources directory.
     *
     * @param  string  $path
     * @return string
     */
    public function resourcePath($path = '')
    {
        return $this->path().($path ? DIRECTORY_SEPARATOR.$path : $path);
    }

    /**
     * Get the path to the database directory.
     *
     * @param  string $path Optionally, a path to append to the database path
     * @return string
     */
    public function databasePath($path = '')
    {
        return $this->path().DIRECTORY_SEPARATOR.'database';
    }

    /**
     * Get the path to the routes cache file.
     *
     * @return string
     */
    public function getCachedRoutesPath()
    {
        return $this->basePath.DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.'matthewbdaly'.DIRECTORY_SEPARATOR.'artisan-standalone'.DIRECTORY_SEPARATOR.'routes/console.php';
    }

    /**
     * Get the application namespace.
     *
     * @return string
     *
     * @throws \RuntimeException
     */
    public function getNamespace()
    {
        if (! is_null($this->namespace)) {
            return $this->namespace;
        }
        $path = $this->basePath;
        $composer = json_decode(file_get_contents($path.DIRECTORY_SEPARATOR.'composer.json'), true);

        foreach ((array) data_get($composer, 'autoload.psr-4') as $namespace => $path) {
            return $this->namespace = $namespace;
        }

        throw new RuntimeException('Unable to detect application namespace.');
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\Factory;

use Cache\Adapter\Redis\RedisCachePool;
use Cache\AdapterBundle\Exception\ConnectException;
use Cache\Namespaced\NamespacedCachePool;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 * @author Aaron Scherer <aequasi@gmail.com>
 */
final class RedisFactory extends AbstractDsnAdapterFactory
{
    protected static $dependencies = [
        ['requiredClass' => 'Cache\Adapter\Redis\RedisCachePool', 'packageName' => 'cache/redis-adapter'],
    ];

    /**
     * {@inheritdoc}
     */
    public function getAdapter(array $config)
    {
        $client = new \Redis();

        $dsn = $this->getDsn();
        if (empty($dsn)) {
            if (false === $client->connect($config['host'], $config['port'])) {
                throw new ConnectException(sprintf('Could not connect to Redis database on "%s:%s".', $config['host'], $config['port']));
            }
        } else {
            if (false === $client->connect($dsn->getFirstHost(), $dsn->getFirstPort())) {
                throw new ConnectException(sprintf('Could not connect to Redis database on "%s:%s".', $dsn->getFirstHost(), $dsn->getFirstPort()));
            }

            if (!empty($dsn->getPassword())) {
                if (false === $client->auth($dsn->getPassword())) {
                    throw new ConnectException('Could not connect authenticate connection to Redis database.');
                }
            }
            $config['database'] = $dsn->getDatabase();
        }

        if (null !== $config['database'] && false === $client->select($config['database'])) {
            throw new ConnectException(sprintf('Could not select Redis database with index "%s".', $config['database']));
        }

        $pool = new RedisCachePool($client);

        if (null !== $config['pool_namespace']) {
            $pool = new NamespacedCachePool($pool, $config['pool_namespace']);
        }

        return $pool;
    }

    /**
     * {@inheritdoc}
     */
    protected static function configureOptionResolver(OptionsResolver $resolver)
    {
        parent::configureOptionResolver($resolver);

        $resolver->setDefaults(
            [
                'host' => '127.0.0.1',
                'port' => '6379',
                'pool_namespace' => null,
                'database' => null,
            ]
        );

        $resolver->setAllowedTypes('host', ['string']);
        $resolver->setAllowedTypes('port', ['string', 'int']);
        $resolver->setAllowedTypes('pool_namespace', ['string', 'null']);
        $resolver->setAllowedTypes('database', ['int', 'null']);
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\Factory;

use Cache\Adapter\Doctrine\DoctrineCachePool;
use Doctrine\Common\Cache\RedisCache;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
final class DoctrineRedisFactory extends AbstractDoctrineAdapterFactory
{
    /**
     * {@inheritdoc}
     */
    public function getAdapter(array $config)
    {
        $redis = new \Redis();
        $redis->connect($config['host'], $config['port']);

        $client = new RedisCache();
        $client->setRedis($redis);

        return new DoctrineCachePool($client);
    }

    /**
     * {@inheritdoc}
     */
    protected static function configureOptionResolver(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'host' => '127.0.0.1',
            'port' => '6379',
        ]);

        $resolver->setAllowedTypes('host', ['string']);
        $resolver->setAllowedTypes('port', ['string', 'int']);
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle;

/**
 * @author Aaron Scherer <aequasi@gmail.com>
 *
 * @see    https://github.com/snc/SncRedisBundle/blob/master/DependencyInjection/Configuration/RedisDsn.php
 */
final class DSN
{
    private static $PORTS = [
        'redis' => 6379,
        'mongodb' => 27017,
        'tcp' => 6379,
    ];

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

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

    /**
     * @var array
     */
    protected $authentication;

    /**
     * @var array
     */
    protected $hosts;

    /**
     * @var int
     */
    protected $database;

    /**
     * @var array
     */
    protected $parameters = [];

    /**
     * Constructor.
     *
     * @param string $dsn
     */
    public function __construct($dsn)
    {
        $this->dsn = $dsn;
        $this->parseDsn($dsn);
    }

    /**
     * @return string
     */
    public function getDsn()
    {
        return $this->dsn;
    }

    /**
     * @return string
     */
    public function getProtocol()
    {
        return $this->protocol;
    }

    /**
     * @return int|null
     */
    public function getDatabase()
    {
        return $this->database;
    }

    /**
     * @return array
     */
    public function getHosts()
    {
        return $this->hosts;
    }

    /**
     * @return null|string
     */
    public function getFirstHost()
    {
        return $this->hosts[0]['host'];
    }

    /**
     * @return null|int
     */
    public function getFirstPort()
    {
        return $this->hosts[0]['port'];
    }

    /**
     * @return array
     */
    public function getAuthentication()
    {
        return $this->authentication;
    }

    public function getUsername()
    {
        return $this->authentication['username'];
    }

    public function getPassword()
    {
        return $this->authentication['password'];
    }

    /**
     * @return array
     */
    public function getParameters()
    {
        return $this->parameters;
    }

    /**
     * @return bool
     */
    public function isValid()
    {
        if (null === $this->getProtocol()) {
            return false;
        }

        if (!in_array($this->getProtocol(), ['redis', 'mongodb', 'tcp'])) {
            return false;
        }

        if (empty($this->getHosts())) {
            return false;
        }

        return true;
    }

    private function parseProtocol($dsn)
    {
        $regex = '/^(\w+):\/\//i';

        preg_match($regex, $dsn, $matches);

        if (isset($matches[1])) {
            $protocol = $matches[1];
            if (!in_array($protocol, ['redis', 'mongodb', 'tcp'])) {
                return false;
            }

            $this->protocol = $protocol;
        }
    }

    /**
     * @param string $dsn
     */
    private function parseDsn($dsn)
    {
        $this->parseProtocol($dsn);
        if (null === $this->getProtocol()) {
            return;
        }

        // Remove the protocol
        $dsn = str_replace($this->protocol.'://', '', $dsn);

        // Parse and remove auth if they exist
        if (false !== $pos = strrpos($dsn, '@')) {
            $temp = explode(':', str_replace('\@', '@', substr($dsn, 0, $pos)));
            $dsn = substr($dsn, $pos + 1);

            $auth = [];
            if (2 === count($temp)) {
                $auth['username'] = $temp[0];
                $auth['password'] = $temp[1];
            } else {
                $auth['password'] = $temp[0];
            }

            $this->authentication = $auth;
        }

        if (false !== strpos($dsn, '?')) {
            if (false === strpos($dsn, '/')) {
                $dsn = str_replace('?', '/?', $dsn);
            }
        }

        $temp = explode('/', $dsn);
        $this->parseHosts($temp[0]);

        if (isset($temp[1])) {
            $params = $temp[1];
            $temp = explode('?', $params);
            $this->database = empty($temp[0]) ? null : $temp[0];
            if (isset($temp[1])) {
                $this->parseParameters($temp[1]);
            }
        }
    }

    private function parseHosts($hostString)
    {
        preg_match_all('/(?P<host>[\w\-._]+)(?::(?P<port>\d+))?/mi', $hostString, $matches);

        $hosts = [];
        foreach ($matches['host'] as $index => $match) {
            $port = !empty($matches['port'][$index])
                ? (int) $matches['port'][$index]
                : self::$PORTS[$this->protocol];
            $hosts[] = ['host' => $match, 'port' => $port];
        }

        $this->hosts = $hosts;
    }

    /**
     * @param string $params
     *
     * @return string
     */
    protected function parseParameters($params)
    {
        $parameters = explode('&', $params);

        foreach ($parameters as $parameter) {
            $kv = explode('=', $parameter, 2);
            $this->parameters[$kv[0]] = isset($kv[1]) ? $kv[1] : null;
        }

        return '';
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\Factory;

use Cache\Adapter\Doctrine\DoctrineCachePool;
use Doctrine\Common\Cache\MemcacheCache;
use Memcache;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
final class DoctrineMemcacheFactory extends AbstractDoctrineAdapterFactory
{
    /**
     * {@inheritdoc}
     */
    public function getAdapter(array $config)
    {
        $memcache = new Memcache();
        $memcache->connect($config['host'], $config['port']);

        $client = new MemcacheCache();
        $client->setMemcache($memcache);

        return new DoctrineCachePool($client);
    }

    /**
     * {@inheritdoc}
     */
    protected static function configureOptionResolver(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'host' => '127.0.0.1',
            'port' => '11211',
        ]);

        $resolver->setAllowedTypes('host', ['string']);
        $resolver->setAllowedTypes('port', ['string', 'int']);
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\Factory;

use Cache\Adapter\Doctrine\DoctrineCachePool;
use Couchbase;
use Doctrine\Common\Cache\CouchbaseCache;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
final class DoctrineCouchbaseFactory extends AbstractDoctrineAdapterFactory
{
    /**
     * {@inheritdoc}
     */
    public function getAdapter(array $config)
    {
        $couchbase = new Couchbase($config['host'], $config['user'], $config['password'], $config['bucket']);

        $client = new CouchbaseCache();
        $client->setCouchbase($couchbase);

        return new DoctrineCachePool($client);
    }

    /**
     * {@inheritdoc}
     */
    protected static function configureOptionResolver(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'host' => '127.0.0.1',
            'user' => 'Administrator',
            'password' => 'password',
            'bucket' => 'default',
        ]);

        $resolver->setAllowedTypes('host', ['string']);
        $resolver->setAllowedTypes('user', ['string']);
        $resolver->setAllowedTypes('password', ['string']);
        $resolver->setAllowedTypes('bucket', ['string']);
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\DependencyInjection;

use Cache\AdapterBundle\DummyAdapter;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\Alias;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
class CacheAdapterExtension extends Extension
{
    /**
     * Loads the configs for Cache and puts data into the container.
     *
     * @param array            $configs   Array of configs
     * @param ContainerBuilder $container Container Object
     */
    public function load(array $configs, ContainerBuilder $container)
    {
        $configuration = new Configuration();
        $config = $this->processConfiguration($configuration, $configs);

        $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
        $loader->load('services.yml');

        // Configure client services
        $first = isset($config['providers']['default']) ? 'default' : null;
        foreach ($config['providers'] as $name => $arguments) {
            if (null === $first) {
                $first = $name;
            }

            $factoryClass = $container->getDefinition($arguments['factory'])->getClass();
            $factoryClass::validate($arguments['options'], $name);

            // See if any option has a service reference
            $arguments['options'] = $this->findReferences($arguments['options']);

            $def = $container->register('cache.provider.'.$name, DummyAdapter::class);
            $def->setFactory([new Reference($arguments['factory']), 'createAdapter'])
                ->addArgument($arguments['options'])
                ->setPublic(true);

            $def->addTag('cache.provider');
            foreach ($arguments['aliases'] as $alias) {
                $container->setAlias($alias, new Alias('cache.provider.'.$name, true));
            }
        }

        if (null !== $first) {
            $container->setAlias('cache', 'cache.provider.'.$first);
            $container->setAlias('php_cache', 'cache.provider.'.$first);
        }
    }

    /**
     * @param array $options
     *
     * @return array
     */
    private function findReferences(array $options)
    {
        foreach ($options as $key => $value) {
            if (is_array($value)) {
                $options[$key] = $this->findReferences($value);
            } elseif ('_service' === substr($key, -8) || 0 === strpos($value, '@') || 'service' === $key) {
                $options[$key] = new Reference(ltrim($value, '@'));
            }
        }

        return $options;
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\Factory;

use Cache\Adapter\Doctrine\DoctrineCachePool;
use Doctrine\Common\Cache\PredisCache;
use Predis\Client;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
final class DoctrinePredisFactory extends AbstractDoctrineAdapterFactory
{
    protected static $dependencies = [
        ['requiredClass' => 'Cache\Adapter\Doctrine\DoctrineCachePool', 'packageName' => 'cache/doctrine-adapter'],
        ['requiredClass' => 'Predis\Client', 'packageName' => 'predis/predis'],
    ];

    /**
     * {@inheritdoc}
     */
    public function getAdapter(array $config)
    {
        $client = new Client([
            'scheme' => $config['scheme'],
            'host' => $config['host'],
            'port' => $config['port'],
        ]);

        return new DoctrineCachePool(new PredisCache($client));
    }

    /**
     * {@inheritdoc}
     */
    protected static function configureOptionResolver(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'host' => '127.0.0.1',
            'port' => '6379',
            'scheme' => 'tcp',
        ]);

        $resolver->setAllowedTypes('host', ['string']);
        $resolver->setAllowedTypes('port', ['string', 'int']);
        $resolver->setAllowedTypes('scheme', ['string']);
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\Factory;

use Cache\AdapterBundle\DSN;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Aaron Scherer <aequasi@gmail.com>
 */
abstract class AbstractDsnAdapterFactory extends AbstractAdapterFactory
{
    /**
     * @var DSN
     */
    private $DSN;

    /**
     * @return DSN
     */
    protected function getDsn()
    {
        return $this->DSN;
    }

    /**
     * {@inheritdoc}
     */
    protected static function configureOptionResolver(OptionsResolver $resolver)
    {
        $resolver->setDefaults(['dsn' => '']);
        $resolver->setAllowedTypes('dsn', ['string']);
    }

    /**
     * {@inheritdoc}
     */
    public static function validate(array $options, $adapterName)
    {
        parent::validate($options, $adapterName);

        if (empty($options['dsn'])) {
            return;
        }

        $dsn = new DSN($options['dsn']);
        if (!$dsn->isValid()) {
            throw new \InvalidArgumentException('Invalid DSN: '.$options['dsn']);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function createAdapter(array $options = [])
    {
        if (!empty($options['dsn'])) {
            $dsn = new DSN($options['dsn']);
            if (!$dsn->isValid()) {
                throw new \InvalidArgumentException('Invalid DSN: '.$options['dsn']);
            }

            $this->DSN = $dsn;
        }

        return parent::createAdapter($options);
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\Factory;

use Cache\Adapter\Memcache\MemcacheCachePool;
use Memcache;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Nicholas Ruunu <nicholas@ruu.nu>
 */
final class MemcacheFactory extends AbstractAdapterFactory
{
    protected static $dependencies = [
        ['requiredClass' => 'Cache\Adapter\Memcache\MemcacheCachePool', 'packageName' => 'cache/memcache-adapter'],
    ];

    /**
     * {@inheritdoc}
     */
    public function getAdapter(array $config)
    {
        $client = new Memcache();
        $client->connect($config['host'], $config['port']);

        foreach ($config['redundant_servers'] as $server) {
            if (!isset($server['host'])) {
                continue;
            }
            $port = $config['port'];
            if (isset($server['port'])) {
                $port = $server['port'];
            }
            $client->addserver($server['host'], $port);
        }

        return new MemcacheCachePool($client);
    }

    /**
     * {@inheritdoc}
     */
    protected static function configureOptionResolver(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'host' => '127.0.0.1',
            'port' => 11211,
            'redundant_servers' => [],
        ]);

        $resolver->setAllowedTypes('host', ['string']);
        $resolver->setAllowedTypes('port', ['string', 'int']);
        $resolver->setAllowedTypes('redundant_servers', ['array']);
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\Factory;

use Cache\Adapter\MongoDB\MongoDBCachePool;
use MongoDB\Driver\Manager;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 * @author Aaron Scherer <aequasi@gmail.com>
 */
final class MongoDBFactory extends AbstractDsnAdapterFactory
{
    protected static $dependencies = [
        ['requiredClass' => 'Cache\Adapter\MongoDB\MongoDBCachePool', 'packageName' => 'cache/mongodb-adapter'],
    ];

    /**
     * {@inheritdoc}
     */
    public function getAdapter(array $config)
    {
        $dsn = $this->getDsn();
        if (empty($dsn)) {
            $manager = new Manager(sprintf('mongodb://%s:%s', $config['host'], $config['port']));
        } else {
            $manager = new Manager($dsn->getDsn());
        }

        $collection = MongoDBCachePool::createCollection($manager, $config['namespace']);

        return new MongoDBCachePool($collection);
    }

    /**
     * {@inheritdoc}
     */
    protected static function configureOptionResolver(OptionsResolver $resolver)
    {
        parent::configureOptionResolver($resolver);

        $resolver->setDefaults(
            [
                'host' => '127.0.0.1',
                'port' => 11211,
                'namespace' => 'cache',
            ]
        );

        $resolver->setAllowedTypes('host', ['string']);
        $resolver->setAllowedTypes('port', ['string', 'int']);
        $resolver->setAllowedTypes('namespace', ['string']);
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\DependencyInjection;

use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

/**
 * Class Configuration.
 *
 * @author Aaron Scherer <aequasi@gmail.com>
 */
class Configuration implements ConfigurationInterface
{
    /**
     * Generates the configuration tree builder.
     *
     * @return TreeBuilder The tree builder
     */
    public function getConfigTreeBuilder()
    {
        $treeBuilder = new TreeBuilder('cache_adapter');

        if (method_exists($treeBuilder, 'getRootNode')) {
            $rootNode = $treeBuilder->getRootNode();
        } else {
            $rootNode = $treeBuilder->root('cache_adapter');
        }

        $rootNode->children()
            ->append($this->getClustersNode())
            ->end();

        return $treeBuilder;
    }

    /**
     * @return ArrayNodeDefinition
     */
    private function getClustersNode()
    {
        $treeBuilder = new TreeBuilder('providers');

        if (method_exists($treeBuilder, 'getRootNode')) {
            $node = $treeBuilder->getRootNode();
        } else {
            $node = $treeBuilder->root('providers');
        }

        $node
            ->requiresAtLeastOneElement()
            ->useAttributeAsKey('name')
            ->prototype('array')
                ->children()
                    ->scalarNode('factory')->isRequired()->cannotBeEmpty()->end()
                    ->variableNode('options')->defaultValue([])->end()
                    ->arrayNode('aliases')
                        ->prototype('scalar')->end()
                    ->end()
                ->end()
            ->end();

        return $node;
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\Factory;

use Cache\AdapterBundle\Exception\ConfigurationException;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * An abstract factory that makes it easier to implement new factories. A class that extend the AbstractAdapterFactory
 * should override AbstractAdapterFactory::$dependencies and AbstractAdapterFactory::configureOptionResolver().
 *
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
abstract class AbstractAdapterFactory implements AdapterFactoryInterface
{
    protected static $dependencies = [];

    /**
     * @param array $config
     *
     * @return CacheItemPoolInterface
     */
    abstract protected function getAdapter(array $config);

    /**
     * {@inheritdoc}
     */
    public function createAdapter(array $options = [])
    {
        $this->verifyDependencies();

        $resolver = new OptionsResolver();
        static::configureOptionResolver($resolver);
        $config = $resolver->resolve($options);

        return $this->getAdapter($config);
    }

    /**
     * {@inheritdoc}
     */
    public static function validate(array $options, $adapterName)
    {
        static::verifyDependencies();

        $resolver = new OptionsResolver();
        static::configureOptionResolver($resolver);

        try {
            $resolver->resolve($options);
        } catch (\Exception $e) {
            $message = sprintf(
                'Error while configure adapter %s. Verify your configuration at "cache_adapter.providers.%s.options". %s',
                $adapterName,
                $adapterName,
                $e->getMessage()
            );

            throw new ConfigurationException($message, $e->getCode(), $e);
        }
    }

    /**
     * Make sure that we have the required class and throw and exception if we don't.
     *
     * @throws \LogicException
     */
    protected static function verifyDependencies()
    {
        foreach (static::$dependencies as $dependency) {
            if (!class_exists($dependency['requiredClass'])) {
                throw new \LogicException(
                    sprintf(
                        'You must install the "%s" package to use the "%s" factory.',
                        $dependency['packageName'],
                        static::class
                    )
                );
            }
        }
    }

    /**
     * By default we do not have any options to configure. A factory should override this function and confgure
     * the options resolver.
     *
     * @param OptionsResolver $resolver
     */
    protected static function configureOptionResolver(OptionsResolver $resolver)
    {
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\Factory;

use Cache\Namespaced\NamespacedCachePool;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
final class NamespacedFactory extends AbstractAdapterFactory
{
    protected static $dependencies = [
        ['requiredClass' => 'Cache\Namespaced\NamespacedCachePool', 'packageName' => 'cache/namespaced-cache'],
    ];

    /**
     * {@inheritdoc}
     */
    public function getAdapter(array $config)
    {
        return new NamespacedCachePool($config['service'], $config['namespace']);
    }

    /**
     * {@inheritdoc}
     */
    protected static function configureOptionResolver(OptionsResolver $resolver)
    {
        parent::configureOptionResolver($resolver);

        $resolver->setRequired(['namespace', 'service']);
        $resolver->setAllowedTypes('namespace', ['string']);
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\Factory;

use Cache\Adapter\Chain\CachePoolChain;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Aaron Scherer <aequasi@gmail.com>
 */
final class ChainFactory extends AbstractAdapterFactory
{
    protected static $dependencies = [
        ['requiredClass' => 'Cache\Adapter\Chain\CachePoolChain', 'packageName' => 'cache/chain-adapter'],
    ];

    /**
     * {@inheritdoc}
     */
    public function getAdapter(array $config)
    {
        return new CachePoolChain($config['services'], ['skip_on_failure' => $config['skip_on_failure']]);
    }

    /**
     * {@inheritdoc}
     */
    protected static function configureOptionResolver(OptionsResolver $resolver)
    {
        parent::configureOptionResolver($resolver);

        $resolver->setRequired('services');
        $resolver->setAllowedTypes('services', ['array']);

        $resolver->setDefault('skip_on_failure', false);
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\Factory;

use Cache\Adapter\Doctrine\DoctrineCachePool;
use Doctrine\Common\Cache\FilesystemCache;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
final class DoctrineFilesystemFactory extends AbstractDoctrineAdapterFactory
{
    /**
     * {@inheritdoc}
     */
    public function getAdapter(array $config)
    {
        $client = new FilesystemCache($config['directory'], $config['extension'], (int) $config['umask']);

        return new DoctrineCachePool($client);
    }

    /**
     * {@inheritdoc}
     */
    protected static function configureOptionResolver(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'extension' => FilesystemCache::EXTENSION,
            'umask' => '0002',
        ]);

        $resolver->setRequired(['directory']);

        $resolver->setAllowedTypes('directory', ['string']);
        $resolver->setAllowedTypes('extension', ['string']);
        $resolver->setAllowedTypes('umask', ['string', 'int']);
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\Factory;

use Cache\Adapter\Doctrine\DoctrineCachePool;
use Doctrine\Common\Cache\RiakCache;
use Riak\Bucket;
use Riak\Connection;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
final class DoctrineRiakFactory extends AbstractDoctrineAdapterFactory
{
    /**
     * {@inheritdoc}
     */
    public function getAdapter(array $config)
    {
        $connection = new Connection($config['host'], $config['port']);
        $bucket = new Bucket($connection, $config['type']);

        return new DoctrineCachePool(new RiakCache($bucket));
    }

    /**
     * {@inheritdoc}
     */
    protected static function configureOptionResolver(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'host' => '127.0.0.1',
            'port' => '8087',
            'type' => null,
        ]);

        $resolver->setAllowedTypes('host', ['string']);
        $resolver->setAllowedTypes('port', ['string', 'int']);
        $resolver->setAllowedTypes('type', ['string', 'null']);
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\Factory;

use Cache\Adapter\Memcached\MemcachedCachePool;
use Cache\AdapterBundle\ProviderHelper\Memcached;
use Cache\Namespaced\NamespacedCachePool;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
final class MemcachedFactory extends AbstractAdapterFactory
{
    protected static $dependencies = [
        ['requiredClass' => 'Cache\Adapter\Memcached\MemcachedCachePool', 'packageName' => 'cache/memcached-adapter'],
    ];

    /**
     * {@inheritdoc}
     */
    public function getAdapter(array $config)
    {
        $client = new Memcached($config['persistent_id']);
        $client->addServer($config['host'], $config['port']);

        foreach ($config['redundant_servers'] as $server) {
            if (!isset($server['host'])) {
                continue;
            }
            $port = $config['port'];
            if (isset($server['port'])) {
                $port = $server['port'];
            }
            $client->addServer($server['host'], $port);
        }

        foreach ($config['driver_options'] as $constant => $value) {
            $client->setOption(constant($constant), $value);
        }

        $pool = new MemcachedCachePool($client);

        if (null !== $config['pool_namespace']) {
            $pool = new NamespacedCachePool($pool, $config['pool_namespace']);
        }

        return $pool;
    }

    /**
     * {@inheritdoc}
     */
    protected static function configureOptionResolver(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'persistent_id' => null,
            'host' => '127.0.0.1',
            'port' => 11211,
            'pool_namespace' => null,
            'redundant_servers' => [],
            'driver_options' => [],
        ]);

        $resolver->setAllowedTypes('persistent_id', ['string', 'null']);
        $resolver->setAllowedTypes('host', ['string']);
        $resolver->setAllowedTypes('port', ['string', 'int']);
        $resolver->setAllowedTypes('pool_namespace', ['string', 'null']);
        $resolver->setAllowedTypes('redundant_servers', ['array']);
        $resolver->setAllowedTypes('driver_options', ['array']);
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\Factory;

use Cache\Adapter\Predis\PredisCachePool;
use Cache\Namespaced\NamespacedCachePool;
use Predis\Client;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 * @author Aaron Scherer <aequasi@gmail.com>
 */
final class PredisFactory extends AbstractDsnAdapterFactory
{
    protected static $dependencies = [
        ['requiredClass' => 'Cache\Adapter\Predis\PredisCachePool', 'packageName' => 'cache/predis-adapter'],
    ];

    /**
     * {@inheritdoc}
     */
    public function getAdapter(array $config)
    {
        $dsn = $this->getDsn();
        if (empty($dsn)) {
            $client = new Client(
                [
                    'scheme' => $config['scheme'],
                    'host' => $config['host'],
                    'port' => $config['port'],
                    'persistent' => $config['persistent'],
                ]
            );
        } else {
            $client = new Client($dsn->getDsn());
        }

        $pool = new PredisCachePool($client);

        if (null !== $config['pool_namespace']) {
            $pool = new NamespacedCachePool($pool, $config['pool_namespace']);
        }

        return $pool;
    }

    /**
     * {@inheritdoc}
     */
    protected static function configureOptionResolver(OptionsResolver $resolver)
    {
        parent::configureOptionResolver($resolver);

        $resolver->setDefaults(
            [
                'host' => '127.0.0.1',
                'port' => '6379',
                'scheme' => 'tcp',
                'pool_namespace' => null,
                'persistent' => false,
            ]
        );

        $resolver->setAllowedTypes('host', ['string']);
        $resolver->setAllowedTypes('port', ['string', 'int']);
        $resolver->setAllowedTypes('scheme', ['string']);
        $resolver->setAllowedTypes('pool_namespace', ['string', 'null']);
        $resolver->setAllowedTypes('persistent', ['bool']);
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\ProviderHelper;

/**
 * Class Memcached.
 *
 * @author Aaron Scherer <aequasi@gmail.com>
 */
class Memcached extends \Memcached
{
    /**
     * {@inheritdoc}
     */
    public function addServer($host, $port, $weight = 0)
    {
        $serverList = $this->getServerList();
        foreach ($serverList as $server) {
            if ($server['host'] === $host && $server['port'] === $port) {
                return false;
            }
        }

        return parent::addServer($host, $port, $weight);
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\Factory;

use Cache\Adapter\Doctrine\DoctrineCachePool;
use Doctrine\Common\Cache\MemcachedCache;
use Memcached;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
final class DoctrineMemcachedFactory extends AbstractDoctrineAdapterFactory
{
    /**
     * {@inheritdoc}
     */
    public function getAdapter(array $config)
    {
        $memcached = new Memcached();
        $memcached->addServer($config['host'], $config['port']);

        $client = new MemcachedCache();
        $client->setMemcached($memcached);

        return new DoctrineCachePool($client);
    }

    /**
     * {@inheritdoc}
     */
    protected static function configureOptionResolver(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'host' => '127.0.0.1',
            'port' => '11211',
        ]);

        $resolver->setAllowedTypes('host', ['string']);
        $resolver->setAllowedTypes('port', ['string', 'int']);
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\DependencyInjection\CompilerPass;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
 * @author Aaron Scherer <aequasi@gmail.com>
 *
 * ServiceAliasCompilerPass Class
 */
class ServiceAliasCompilerPass implements CompilerPassInterface
{
    /**
     * You can modify the container here before it is dumped to PHP code.
     *
     * @param ContainerBuilder $container
     */
    public function process(ContainerBuilder $container)
    {
        $serviceIds = array_keys($container->findTaggedServiceIds('cache.provider'));
        foreach ($serviceIds as $serviceId) {
            $instance = $container->get($serviceId);
            $class = get_class($instance);

            $container->setAlias($class, $serviceId);
        }
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\Factory;

use Cache\Adapter\Doctrine\DoctrineCachePool;
use Doctrine\Common\Cache\MongoDBCache;
use MongoClient;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
final class DoctrineMongodDBFactory extends AbstractDoctrineAdapterFactory
{
    /**
     * {@inheritdoc}
     */
    public function getAdapter(array $config)
    {
        $mongo = new MongoClient();
        $collection = $mongo->selectCollection($config['host'], $config['collection']);

        return new DoctrineCachePool(new MongoDBCache($collection));
    }

    /**
     * {@inheritdoc}
     */
    protected static function configureOptionResolver(OptionsResolver $resolver)
    {
        $resolver->setRequired(['host', 'collection']);

        $resolver->setAllowedTypes('host', ['string']);
        $resolver->setAllowedTypes('collection', ['string']);
    }

    protected static function verifyDependencies()
    {
        if (!version_compare(phpversion('mongo'), '1.3.0', '>=')) {
            throw new \LogicException('Mongo >= 1.3.0 is required.');
        }

        parent::verifyDependencies();
    }
}


<?php

/*
 * This file is part of php-cache organization.
 *
 * (c) 2015 Aaron Scherer <aequasi@gmail.com>, Tobias Nyholm <tobias.nyholm@gmail.com>
 *
 * This source file is subject to the MIT license that is bundled
 * with this source code in the file LICENSE.
 */

namespace Cache\AdapterBundle\Factory;

use Cache\Adapter\PHPArray\ArrayCachePool;
use Cache\Namespaced\NamespacedCachePool;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
 * @author Tobias Nyholm <tobias.nyholm@gmail.com>
 */
final class ArrayFactory extends AbstractAdapterFactory
{
    protected static $dependencies = [
        ['requiredClass' => 'Cache\Adapter\PHPArray\ArrayCachePool', 'packageName' => 'cache/array-adapter'],
    ];

    /**
     * {@inheritdoc}
     */
    public function getAdapter(array $config)
    {
        $pool = new ArrayCachePool();

        if (null !== $config['pool_namespace']) {
            $pool = new NamespacedCachePool($pool, $config['pool_namespace']);
        }

        return $pool;
    }

    /**
     * {@inheritdoc}
     */
    protected static function configureOptionResolver(OptionsResolver $resolver)
    {
        parent::configureOptionResolver($resolver);

        $resolver->setDefaults(
          [
            'pool_namespace' => null,
          ]
        );

        $resolver->setAllowedTypes('pool_namespace', ['string', 'null']);
    }
}


<?php

namespace ZuluCrypto\StellarSdk\History;

use GuzzleHttp\Client;
use Symfony\Component\Filesystem\Filesystem;
use ZuluCrypto\StellarSdk\Util\Json;

/**
 * Manages downloading from a HTTP history archive
 *
 * References:
 *  https://github.com/stellar/stellar-core/blob/master/docs/history.md
 */
class HttpHistoryArchive
{
    /**
     * @var string
     */
    protected $rootUrl;

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

    /**
     * @var Client
     */
    protected $httpClient;

    /**
     * https://github.com/stellar/stellar-core/blob/master/docs/history.md#root-has
     *
     * @var array
     */
    protected $rootHas;

    public function __construct($rootUrl, $storageRoot)
    {
        // Root URL must end in a slash
        if (substr($rootUrl, -1, 1) != '/') $rootUrl .= '/';
        // Storage url must end in the appropriate directory separator
        if (substr($storageRoot, -1, 1) != DIRECTORY_SEPARATOR) $storageRoot .= DIRECTORY_SEPARATOR;

        $this->rootUrl = $rootUrl;
        $this->storageRoot = $storageRoot;

        $this->httpClient = new Client([
            'base_uri' => $rootUrl,
        ]);
    }

    public function downloadAll()
    {
        $this->syncRootHas();

        $startingLedger = 64-1;
        $ledgerIncrement = 64;
        $currLedger = $startingLedger;
        $percentComplete = 0;
        while ($currLedger < $this->rootHas['currentLedger']) {
            $ledgerHex = sprintf("%x", $currLedger);
            $ledgerHex = str_pad($ledgerHex, 8, '0', STR_PAD_LEFT);

            // Get HAS file
            $hasPath = $this->getHashedLedgerSubdirectory('history.json', $ledgerHex);
            $localHasPath = $this->syncFile($hasPath);

            $archiveState = HistoryArchiveState::fromFile($localHasPath);

            // Bucket files
            foreach ($archiveState->getUniqueBucketHashes() as $bucketHash) {
                $bucketPath = $this->getHashedBucketSubdirectory($bucketHash);
                $this->syncFile($bucketPath);
            }

            // Other files
            $this->syncFile($this->getHashedLedgerSubdirectory('ledger.xdr.gz', $ledgerHex));
            $this->syncFile($this->getHashedLedgerSubdirectory('transactions.xdr.gz', $ledgerHex));
            $this->syncFile($this->getHashedLedgerSubdirectory('results.xdr.gz', $ledgerHex));
            $this->syncFile($this->getHashedLedgerSubdirectory('scp.xdr.gz', $ledgerHex), false, true);

            $currLedger += $ledgerIncrement;
            $percentComplete = round(($currLedger / $this->rootHas['currentLedger']) * 100);
        }
    }

    public function syncRootHas()
    {
        $response = $this->httpClient->get('.well-known/stellar-history.json');

        $this->rootHas = Json::mustDecode($response->getBody());
    }

    /**
     * Downloads $relativeUrl to the corresponding location on the local filesystem
     *
     * @param      $relativeUrl
     * @param bool $forceDownload force download even if the file is present locally
     * @param bool $allowMissing allow 404 responses
     * @return string
     * @throws \ErrorException
     */
    public function syncFile($relativeUrl, $forceDownload = false, $allowMissing = false)
    {
        $fs = new Filesystem();

        // Target file to save to will match the relativeUrl being requested, but
        // with the correct directory separator
        $targetFile = str_replace('/', DIRECTORY_SEPARATOR, $relativeUrl);
        $targetFile = $this->storageRoot . $targetFile;

        // Files won't change and are only written after a successful download, so
        // if it exists there's no need to download it again
        if (!$forceDownload && $fs->exists($targetFile)) return $targetFile;

        $tmpFile = $fs->tempnam(sys_get_temp_dir(), 'stellar-sync-tmp');
        $response = $this->httpClient->request('GET', $relativeUrl, [
            'sink' => $tmpFile,
            'http_errors' => false,
        ]);

        // Response succeeded
        if ($response->getStatusCode() == 200) {
            // 200 is always OK
        }
        // 404, but missing files are ok
        elseif ($allowMissing && $response->getStatusCode() == 404) {
            // this is OK
        }
        // Error response or 404 with $allowMissing false
        else {
            throw new \ErrorException(sprintf('HTTP %s: %s', $response->getStatusCode(), $response->getBody()));
        }

        // Move temporary file into its final location
        $fs->mkdir(dirname($targetFile), 0700);
        $fs->rename($tmpFile, $targetFile);

        // Basic rate limiting
        usleep(20000);
        return $targetFile;
    }

    protected function getHashedLedgerSubdirectory($filename, $prefixHex)
    {
        // Parse the filename (eg: ledger.xdr.gz) into category (ledger) and extension (.xdr.gz)
        $parts = explode(".", $filename);
        $category = array_shift($parts);
        $extension = join('.', $parts);
        $subdir = $category;

        // Three levels of subdirectories
        $subdir .= '/' . substr($prefixHex, 0, 2);
        $subdir .= '/' . substr($prefixHex, 2, 2);
        $subdir .= '/' . substr($prefixHex, 4, 2);

        // Then the filename + hex + extension
        return sprintf('%s/%s-%s.%s',
            $subdir,
            $category, $prefixHex, $extension
        );
    }

    protected function getHashedBucketSubdirectory($prefixHex)
    {
        $subdir = 'bucket';

        // Three levels of subdirectories
        $subdir .= '/' . substr($prefixHex, 0, 2);
        $subdir .= '/' . substr($prefixHex, 2, 2);
        $subdir .= '/' . substr($prefixHex, 4, 2);

        return sprintf('%s/bucket-%s.xdr.gz',
            $subdir,
            $prefixHex
        );
    }
}


<?php

namespace ZuluCrypto\StellarSdk\XdrModel;

use ZuluCrypto\StellarSdk\Xdr\XdrBuffer;

class ChangeTrustResult extends OperationResult
{
    // https://github.com/stellar/stellar-core/blob/3c4e356803175f6c2645e4437881cf07522df94d/src/xdr/Stellar-transaction.x#L539
    const MALFORMED                 = 'change_trust_malformed';
    const NO_ISSUER                 = 'change_trust_no_issuer';
    const INVALID_LIMIT             = 'change_trust_invalid_limit';
    const LOW_RESERVE               = 'change_trust_low_reserve';
    const SELF_NOT_ALLOWED          = 'change_trust_self_not_allowed';

    /**
     * @deprecated Do not call this method directly. Instead, use OperationResult::fromXdr
     *
     * @param XdrBuffer $xdr
     * @return OperationResult|PaymentResult
     * @throws \ErrorException
     */
    public static function fromXdr(XdrBuffer $xdr)
    {
        $model = new ChangeTrustResult();

        $rawErrorCode = $xdr->readInteger();
        $errorCodeMap = [
            '0' => 'success',
            '-1' => static::MALFORMED,
            '-2' => static::NO_ISSUER,
            '-3' => static::INVALID_LIMIT,
            '-4' => static::LOW_RESERVE,
            '-5' => static::SELF_NOT_ALLOWED,
        ];
        if (!isset($errorCodeMap[$rawErrorCode])) {
            throw new \ErrorException(sprintf('Unknown error code %s', $rawErrorCode));
        }

        // Do not store the "success" error code
        if ($errorCodeMap[$rawErrorCode] !== 'success') {
            $model->errorCode = $errorCodeMap[$rawErrorCode];
        }

        return $model;
    }
}


<?php

namespace ZuluCrypto\StellarSdk\Derivation;

use ZuluCrypto\StellarSdk\Util\MathSafety;

/**
 * A Hierarchical Deterministic node for use with Stellar
 *
 */
class HdNode
{
    const HARDENED_MINIMUM_INDEX = 0x80000000;

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

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

    /**
     * Returns a new master node that can be used to derive subnodes
     *
     * @param $entropy
     * @return HdNode
     */
    public static function newMasterNode($entropy)
    {
        $hmac = hash_hmac('sha512', $entropy, 'ed25519 seed', true);

        return new HdNode(
            substr($hmac, 0, 32),
            substr($hmac, 32, 32)
        );
    }

    /**
     * HdNode constructor.
     *
     * @param $privateKeyBytes (string) 32 bytes of randomly generated data for the private key
     * @param $chainCodeBytes (string) 32 bytes of randomly generated data for deriving additional keys
     */
    public function __construct($privateKeyBytes, $chainCodeBytes)
    {
        MathSafety::require64Bit();

        if (strlen($privateKeyBytes) != 32) throw new \InvalidArgumentException('Private key must be 32 bytes');
        if (strlen($chainCodeBytes) != 32) throw new \InvalidArgumentException('Chain code must be 32 bytes');

        $this->privateKeyBytes = $privateKeyBytes;
        $this->chainCodeBytes = $chainCodeBytes;
    }

    /**
     * @param $index int automatically converted to a hardened index
     * @return HdNode
     */
    public function derive($index)
    {
        $index = intval($index) + intval(static::HARDENED_MINIMUM_INDEX);
        if ($index < static::HARDENED_MINIMUM_INDEX) throw new \InvalidArgumentException('Only hardened indexes are supported');

        // big-endian unsigned long (4 bytes)
        $indexBytes = pack('N', $index);
        $key = pack('C', 0x00) . $this->privateKeyBytes . $indexBytes;

        $hmac = hash_hmac('sha512', $key, $this->chainCodeBytes, true);

        return new HdNode(
            substr($hmac, 0, 32),
            substr($hmac, 32, 32)
        );
    }

    /**
     * Derives a path like m/0'/1'
     * @param $path
     * @return HdNode
     */
    public function derivePath($path)
    {
        $pathParts = $this->parseDerivationPath($path);

        $derived = $this;
        foreach ($pathParts as $index) {
            $derived = $derived->derive($index);
        }

        return $derived;
    }

    /**
     * Takes a path like "m/0'/1'" and returns an array of indexes to derive
     *
     * Note that since this class assumes all indexes are hardened, the returned
     * array for the above example would be:
     *  [0, 1]
     *
     * @param $path
     * @return array
     */
    protected function parseDerivationPath($path)
    {
        $parsed = [];
        $parts = explode('/', $path);
        if (strtolower($parts[0]) != 'm') throw new \InvalidArgumentException('Path must start with "m"');

        // Remove initial 'm' since it refers to the current HdNode
        array_shift($parts);

        // Add each part to the return value
        foreach ($parts as $part) {
            // Each subsequent node must be hardened
            if (strpos($part, "'") != (strlen($part)-1)) throw new \InvalidArgumentException('Path can only contain hardened indexes');
            $part = str_replace("'", '', $part);

            if (!is_numeric($part)) throw new \InvalidArgumentException('Path must be numeric');

            $parsed[] = intval($part);
        }

        return $parsed;
    }

    /**
     * @return string
     */
    public function getPrivateKeyBytes()
    {
        return $this->privateKeyBytes;
    }

    /**
     * @return string
     */
    public function getChainCodeBytes()
    {
        return $this->chainCodeBytes;
    }
}


<?php

namespace ZuluCrypto\StellarSdk\XdrModel;

use ZuluCrypto\StellarSdk\Xdr\XdrBuffer;

class InflationResult extends OperationResult
{
    // https://github.com/stellar/stellar-core/blob/3c4e356803175f6c2645e4437881cf07522df94d/src/xdr/Stellar-transaction.x#L606
    const NOT_TIME                  = 'inflation_not_time';

    /**
     * @var InflationPayout[]
     */
    protected $payouts = array();

    /**
     * @deprecated Do not call this method directly. Instead, use OperationResult::fromXdr
     *
     * @param XdrBuffer $xdr
     * @return OperationResult|PaymentResult
     * @throws \ErrorException
     */
    public static function fromXdr(XdrBuffer $xdr)
    {
        $model = new InflationResult();

        $rawErrorCode = $xdr->readInteger();
        $errorCodeMap = [
            '0' => 'success',
            '-1' => static::NOT_TIME,
        ];
        if (!isset($errorCodeMap[$rawErrorCode])) {
            throw new \ErrorException(sprintf('Unknown error code %s', $rawErrorCode));
        }

        // Do not store the "success" error code
        if ($errorCodeMap[$rawErrorCode] !== 'success') {
            $model->errorCode = $errorCodeMap[$rawErrorCode];
            return $model;
        }

        $numPayouts = $xdr->readUnsignedInteger();
        for ($i=0; $i < $numPayouts; $i++) {
            $model->payouts[] = InflationPayout::fromXdr($xdr);
        }

        return $model;
    }
}


<?php

namespace ZuluCrypto\StellarSdk\XdrModel;

use ZuluCrypto\StellarSdk\Xdr\XdrBuffer;
use ZuluCrypto\StellarSdk\XdrModel\Operation\Operation;

class OperationResult
{
    const BAD_AUTH      = 'bad_auth';   // too few signatures or wrong network
    const NO_ACCOUNT    = 'no_account'; // source account doesn't exist

    /**
     * A string describing the error
     * @var string
     */
    protected $errorCode;

    /**
     * Generic error codes: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L651
     *
     * @param XdrBuffer $xdr
     * @return OperationResult
     */
    public static function fromXdr(XdrBuffer $xdr)
    {
        $opResultCode = $xdr->readInteger();

        // Early return for operations that failed
        if ($opResultCode === -1) {
            $opResult = new OperationResult();
            $opResult->errorCode = static::BAD_AUTH;

            return $opResult;
        }
        if ($opResultCode === -2) {
            $opResult = new OperationResult();
            $opResult->errorCode = static::NO_ACCOUNT;

            return $opResult;
        }

        // Past this point there can be a variety of operation results
        $opType = $xdr->readInteger();
        switch ($opType) {
            case Operation::TYPE_CREATE_ACCOUNT:
                return CreateAccountResult::fromXdr($xdr);
                break;
            case Operation::TYPE_PAYMENT:
                return PaymentResult::fromXdr($xdr);
                break;
            case Operation::TYPE_PATH_PAYMENT;
                return PathPaymentResult::fromXdr($xdr);
                break;
            case Operation::TYPE_MANAGE_OFFER:
                return ManageOfferResult::fromXdr($xdr);
                break;
            case Operation::TYPE_SET_OPTIONS:
                return SetOptionsResult::fromXdr($xdr);
                break;
            case Operation::TYPE_CHANGE_TRUST:
                return ChangeTrustResult::fromXdr($xdr);
                break;
            case Operation::TYPE_ALLOW_TRUST:
                return AllowTrustResult::fromXdr($xdr);
                break;
            case Operation::TYPE_ACCOUNT_MERGE:
                return AccountMergeResult::fromXdr($xdr);
                break;
            case Operation::TYPE_INFLATION:
                return InflationResult::fromXdr($xdr);
                break;
            case Operation::TYPE_MANAGE_DATA:
                return ManageDataResult::fromXdr($xdr);
                break;
            case Operation::TYPE_BUMP_SEQUENCE:
                return BumpSequenceResult::fromXdr($xdr);
                break;
            default:
                throw new \ErrorException(sprintf('Unknown operation type: %s', $opType));
        }
    }

    public function __construct()
    {
        $this->errorCode = null;
    }

    /**
     * Returns true if this operation succeeded
     *
     * @return bool
     */
    public function succeeded()
    {
        return $this->errorCode === null;
    }

    /**
     * Returns true if this operation failed
     *
     * @return bool
     */
    public function failed()
    {
        return !$this->succeeded();
    }

    /**
     * @return null|string
     */
    public function getErrorCode()
    {
        return $this->errorCode;
    }
}


<?php

namespace ZuluCrypto\StellarSdk\Model;

use phpseclib\Math\BigInteger;
use ZuluCrypto\StellarSdk\Horizon\Api\HorizonResponse;
use ZuluCrypto\StellarSdk\Keypair;
use ZuluCrypto\StellarSdk\Transaction\TransactionBuilder;
use ZuluCrypto\StellarSdk\Util\MathSafety;
use ZuluCrypto\StellarSdk\XdrModel\Asset;
use ZuluCrypto\StellarSdk\XdrModel\Operation\PaymentOp;

/**
 * See: https://www.stellar.org/developers/horizon/reference/resources/account.html
 *
 * Account viewer:
 *  https://www.stellar.org/laboratory/#explorer
 */
class Account extends RestApiModel
{
    protected $id;

    private $accountId;

    /**
     * NOTE: for the BigInteger representation of this, see $this->getSequenceAsBigInteger()
     * @var string
     */
    private $sequence;

    private $subentryCount;

    /**
     * @var array|AssetAmount[]
     */
    private $balances;

    private $thresholds;

    private $signers;

    private $data;

    /**
     * @param HorizonResponse $response
     * @return Account
     */
    public static function fromHorizonResponse(HorizonResponse $response)
    {
        $rawData = $response->getRawData();

        // 404 means the account does not currently exist (it may have been merged)
        if (isset($rawData['status']) && $rawData['status'] == 404) {
            throw new \InvalidArgumentException('Account does not exist');
        }

        // Generic catch for other errors
        if (isset($rawData['status']) && $rawData['status'] !== 200) {
            throw new \InvalidArgumentException('Cannot create account due to error response');
        }

        $object = new Account($rawData['id']);

        $object->accountId = $rawData['account_id'];
        $object->sequence = $rawData['sequence'];
        $object->subentryCount = $rawData['subentry_count'];
        $object->thresholds = $rawData['thresholds'];
        $object->data = [];
        if (isset($rawData['data'])) {
            foreach ($rawData['data'] as $key => $value) {
                $object->data[$key] = base64_decode($value);
            }
        }

        if (isset($rawData['balances'])) {
            foreach ($rawData['balances'] as $rawBalance) {
                $balance = new AssetAmount($rawBalance['balance'], $rawBalance['asset_type']);

                if (!$balance->isNativeAsset()) {
                    $balance->setAssetCode($rawBalance['asset_code']);
                    $balance->setAssetIssuerAccountId($rawBalance['asset_issuer']);
                    $balance->setLimit($rawBalance['limit']);
                }

                $object->balances[] = $balance;
            }
        }

        return $object;
    }

    /**
     * Returns true if the specified account ID (G....) passes basic validity checks
     *
     * Note that this doesn't necessarily mean the account is funded or exists
     * on the network. To check that, use the Server::getAccount() method.
     *
     * @param $accountId
     * @return bool
     */
    public static function isValidAccount($accountId)
    {
        // Validate that keypair passes checksum
        try {
            $keypair = Keypair::newFromPublicKey($accountId);
        } catch (\Exception $e) {
            return false;
        }

        return true;
    }

    public function __construct($id)
    {
        $this->id = $id;

        $this->balances = [];
    }

    /**
     * @param                 $toAccountId
     * @param                 $amount
     * @param string|string[] $signingKeys
     * @return HorizonResponse
     * @throws \ErrorException
     */
    public function sendNativeAsset($toAccountId, $amount, $signingKeys)
    {
        $payment = Payment::newNativeAssetPayment($toAccountId, $amount, $this->accountId);

        return $this->sendPayment($payment, $signingKeys);
    }

    /**
     * @param Payment $payment
     * @param         $signingKeys
     * @return HorizonResponse
     * @throws \ErrorException
     */
    public function sendPayment(Payment $payment, $signingKeys)
    {
        if ($payment->isNativeAsset()) {
            $paymentOp = PaymentOp::newNativePayment($payment->getDestinationAccountId(), $payment->getAmount()->getBalanceAsStroops());
        }
        else {
            throw new \ErrorException('Not implemented');
        }

        $transaction = (new TransactionBuilder($this->accountId))
            ->setApiClient($this->apiClient)
            ->addOperation(
                $paymentOp
            )
        ;

        return $transaction->submit($signingKeys);
    }

    /**
     * @param null $sinceCursor
     * @param int  $limit
     * @return Transaction[]
     */
    public function getTransactions($sinceCursor = null, $limit = 50)
    {
        $transactions = [];

        $url = sprintf('/accounts/%s/transactions', $this->accountId);
        $params = [];

        if ($sinceCursor) $params['cursor'] = $sinceCursor;
        if ($limit) $params['limit'] = $limit;

        if ($params) {
            $url .= '?' . http_build_query($params);
        }

        $response = $this->apiClient->get($url);
        $rawTransactions = $response->getRecords();

        foreach ($rawTransactions as $rawTransaction) {
            $transaction = Transaction::fromRawResponseData($rawTransaction);
            $transaction->setApiClient($this->getApiClient());

            $transactions[] = $transaction;
        }

        return $transactions;
    }

    /**
     * @param null $sinceCursor
     * @param int  $limit
     * @return array
     * @throws \ZuluCrypto\StellarSdk\Horizon\Exception\HorizonException
     */
    public function getEffects($sinceCursor = null, $limit = 50)
    {
        $effects = [];
        $url = sprintf('/accounts/%s/effects', $this->accountId);
        $params = [];

        if ($sinceCursor) $params['cursor'] = $sinceCursor;
        if ($limit) $params['limit'] = $limit;

        if ($params) {
            $url .= '?' . http_build_query($params);
        }

        $response = $this->apiClient->get($url);
        $raw = $response->getRecords();

        foreach ($raw as $rawEffect) {
            $effect = Effect::fromRawResponseData($rawEffect);
            $effect->setApiClient($this->getApiClient());

            $effects[] = $effect;
        }

        return $effects;
    }

    /**
     * @param null $sinceCursor
     * @param int  $limit
     * @return array|AssetTransferInterface[]|RestApiModel[]
     */
    public function getPayments($sinceCursor = null, $limit = 50)
    {
        $results = [];

        $url = sprintf('/accounts/%s/payments', $this->accountId);
        $params = [];

        if ($sinceCursor) $params['cursor'] = $sinceCursor;
        if ($limit) $params['limit'] = $limit;

        if ($params) {
            $url .= '?' . http_build_query($params);
        }

        $response = $this->apiClient->get($url);
        $rawRecords = $response->getRecords($limit);

        foreach ($rawRecords as $rawRecord) {
            switch ($rawRecord['type']) {
                case 'create_account':
                    $result = CreateAccountOperation::fromRawResponseData($rawRecord);
                    break;
                case 'payment':
                    $result = Payment::fromRawResponseData($rawRecord);
                    break;
                case 'account_merge':
                    $result = AccountMergeOperation::fromRawResponseData($rawRecord);
                    break;
                case 'path_payment':
                    $result = PathPayment::fromRawResponseData($rawRecord);
                    break;
            }

            $result->setApiClient($this->getApiClient());

            $results[] = $result;
        }

        return $results;
    }

    /**
     * See ApiClient::streamPayments
     *
     * @param null $sinceCursor
     * @param callable $callback
     */
    public function streamPayments($sinceCursor = 'now', callable $callback = null)
    {
        $this->apiClient->streamPayments($sinceCursor, $callback);
    }

    /**
     * Returns a string representing the native balance
     *
     * @return string
     * @throws \ErrorException
     */
    public function getNativeBalance()
    {
        MathSafety::require64Bit();

        foreach ($this->getBalances() as $balance) {
            if ($balance->isNativeAsset()) return $balance->getBalance();
        }

        return 0;
    }

    /**
     * Returns the balance in stroops
     *
     * @return string
     * @throws \ErrorException
     */
    public function getNativeBalanceStroops()
    {
        MathSafety::require64Bit();

        foreach ($this->getBalances() as $balance) {
            if ($balance->isNativeAsset()) return $balance->getUnscaledBalance();
        }

        return "0";
    }

    /**
     * Returns the numeric balance of the given asset
     *
     * @param Asset $asset
     * @return null|string
     */
    public function getCustomAssetBalanceValue(Asset $asset)
    {
        foreach ($this->getBalances() as $balance) {
            if ($balance->getAssetCode() !== $asset->getAssetCode()) continue;
            if ($balance->getAssetIssuerAccountId() != $asset->getIssuer()->getAccountIdString()) continue;

            return $balance->getBalance();
        }

        return null;
    }

    /**
     * Returns an AssetAmount representing the balance of this asset
     *
     * @param Asset $asset
     * @return null|AssetAmount
     */
    public function getCustomAssetBalance(Asset $asset)
    {
        foreach ($this->getBalances() as $balance) {
            if ($balance->getAssetCode() !== $asset->getAssetCode()) continue;
            if ($balance->getAssetIssuerAccountId() != $asset->getIssuer()->getAccountIdString()) continue;

            return $balance;
        }

        return null;
    }

    /**
     * Returns the balance of a custom asset in stroops
     *
     * @param Asset $asset
     * @return null|string
     * @throws \ErrorException
     */
    public function getCustomAssetBalanceStroops(Asset $asset)
    {
        MathSafety::require64Bit();

        foreach ($this->getBalances() as $balance) {
            if ($balance->getAssetCode() !== $asset->getAssetCode()) continue;
            if ($balance->getAssetIssuerAccountId() != $asset->getIssuer()->getAccountIdString()) continue;

            return $balance->getUnscaledBalance();
        }

        return null;
    }

    /**
     * Returns an array holding account thresholds.
     *
     * @return array
     */
    public function getThresholds()
    {
        return $this->thresholds;
    }

    /**
     * This returns the sequence exactly as it comes back from the Horizon API
     *
     * See getSequenceAsBigInteger if you need to use this value in a transaction
     * or other 64-bit safe location.
     *
     * @return string
     */
    public function getSequence()
    {
        return $this->sequence;
    }

    /**
     * @return BigInteger
     */
    public function getSequenceAsBigInteger()
    {
        return new BigInteger($this->sequence);
    }

    /**
     * @return array|AssetAmount[]
     */
    public function getBalances()
    {
        return $this->balances;
    }

    /**
     * Returns an array of key => value pairs
     *
     * Note that the values have been base64-decoded and may be binary data
     *
     * @return array
     */
    public function getData()
    {
        return $this->data;
    }
}


<?php

namespace ZuluCrypto\StellarSdk\Derivation\Bip39;

use ZuluCrypto\StellarSdk\Util\Debug;

/**
 * Minimal BIP-39 implementation (only what's necessary to generate seeds for
 * Stellar wallets)
 *
 * The default wordlist is english. To use a different wordlist, pass its path
 * to the constructor. The wordlist must be formatted with one word on each
 * line. See: https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md
 */
class Bip39
{
    protected $words;

    /**
     * Returns the hex-encoded checksum for the raw bytes in the entropy
     *
     * @param $entropyBytes
     * @return string
     */
    public static function getEntropyChecksumHex($entropyBytes)
    {
        $checksumLengthBits = (strlen($entropyBytes)*8) / 32;
        $hashBytes = hash('sha256', $entropyBytes, true);

        // base_convert can only handle up to 64 bits, so we have to reduce the
        // length of data that gets sent to it
        $checksumLengthBytes = ceil($checksumLengthBits / 8);
        $reducedBytesToChecksum = substr($hashBytes, 0, $checksumLengthBytes);

        $reducedChecksumHex = bin2hex($reducedBytesToChecksum);
        $reducedChecksumBits = str_pad(base_convert($reducedChecksumHex, 16, 2), $checksumLengthBytes * 8, '0', STR_PAD_LEFT);

        $checksumBitstring = substr($reducedChecksumBits, 0, $checksumLengthBits);
        $checksumHex = static::bitstringToHex($checksumBitstring);

        return $checksumHex;
    }

    /**
     * Utility method to convert a bitstring to hex.
     *
     * Primarily a workaround to avoid requiring a real math library
     *
     * @param $bitstring
     * @return string
     */
    public static function bitstringToHex($bitstring)
    {
        $chunkSizeBits = 8;

        // If the string is shorter than the chunk size it can be 0-padded
        if (strlen($bitstring) < $chunkSizeBits) {
            $bitstring = str_pad($bitstring, $chunkSizeBits, '0', STR_PAD_LEFT);
        }

        if (strlen($bitstring) % $chunkSizeBits !== 0) throw new \InvalidArgumentException(sprintf('Got bitstring of length %s, but it must be divisible by %s', strlen($bitstring), $chunkSizeBits));

        $finalHex = '';
        for ($i=0; $i < strlen($bitstring); $i += $chunkSizeBits) {
            $bitstringPart = substr($bitstring, $i, $chunkSizeBits);
            $hex = base_convert($bitstringPart, 2, 16);
            // Ensure hex is always two characters
            $hex = str_pad($hex, 2, '0', STR_PAD_LEFT);

            $finalHex .= $hex;
        }

        return $finalHex;
    }

    /**
     * Bip39 constructor.
     *
     * @param null $wordlistPath
     */
    public function __construct($wordlistPath = null)
    {
        if (null === $wordlistPath) $wordlistPath = __DIR__ . '/wordlists/en.txt';

        $this->words = $this->loadWordlist($wordlistPath);
    }

    /**
     * Converts a mnemonic to raw bytes
     *
     * NOTE: this is NOT the raw bytes used for a Stellar key! See mnemonicToSeedBytes
     *
     * @param $mnenomic
     * @return bool|string
     */
    public function mnemonicToEntropy($mnenomic)
    {
        $bitstring = $this->parseMnemonic($mnenomic);

        // Calculate expected lengths
        $numChecksumBits = strlen($bitstring) / 33;
        $numEntropyBits = strlen($bitstring) - $numChecksumBits;

        // Get checksum bits from the end of the string
        $checksumBits = substr($bitstring, -1 * $numChecksumBits);
        $checksumHex = static::bitstringToHex($checksumBits);

        // Remaining bits are the entropy
        $entropyBits = substr($bitstring, 0, $numEntropyBits);
        $entropyHex = static::bitstringToHex($entropyBits);

        $entropyBytes = hex2bin($entropyHex);

        if ($checksumHex !== static::getEntropyChecksumHex($entropyBytes)) {
            throw new \InvalidArgumentException('Invalid checksum');
        }

        return $entropyBytes;
    }

    /**
     * Converts a mnemonic and optional passphrase to a 64-byte string for use
     * as entropy.
     *
     * Note that this is specific to the wordlist being used and is NOT portable
     * across wordlists.
     *
     * In most cases, mnemonicToSeedBytesWithErrorChecking should be used since
     * it will fail if there's a checksum error in the mnemonic
     *
     * @param        $mnemonic
     * @param string $passphrase
     * @return string
     */
    public function mnemonicToSeedBytes($mnemonic, $passphrase = '')
    {
        $salt = 'mnemonic' . $passphrase;
        return hash_pbkdf2("sha512", $mnemonic, $salt, 2048, 64, true);
    }

    /**
     * Converts $mnemonic to seed bytes suitable for creating a new HDNode
     *
     * If the mnemonic is invalid, an exception is thrown
     *
     * @param        $mnemonic
     * @param string $passphrase
     * @return string raw bytes
     */
    public function mnemonicToSeedBytesWithErrorChecking($mnemonic, $passphrase = '')
    {
        // This will throw an exception if the embedded checksum is incorrect
        $this->mnemonicToEntropy($mnemonic);

        return $this->mnemonicToSeedBytes($mnemonic, $passphrase);
    }

    /**
     * Parses a string of words and returns a string representing the binary
     * encoding of the mnemonic (including checksum)
     *
     * Note that this is a literal string of "101101110" and not raw bytes!
     *
     * @param $mnemonic
     * @return string
     */
    protected function parseMnemonic($mnemonic)
    {
        $words = explode(' ', $mnemonic);
        if (count($words) %3 !== 0) throw new \InvalidArgumentException('Invalid mnemonic (number of words must be a multiple of 3)');

        $wordBitstrings = [];
        foreach ($words as $word) {
            $wordIdx = $this->getWordIndex($word);

            // Convert $wordIdx to an 11-bit number (preserving 0s)
            $wordBitstrings[] = str_pad(decbin($wordIdx), 11, '0', STR_PAD_LEFT);
        }

        // Return a string representing each bit
        return join('', $wordBitstrings);
    }

    /**
     * @param $word
     * @return int
     * @throws \InvalidArgumentException
     */
    protected function getWordIndex($word)
    {
        $index = 0;

        foreach ($this->words as $wordInList) {
            if ($wordInList === $word) return $index;

            $index++;
        }

        throw new \InvalidArgumentException(sprintf('Word "%s" not found in wordlist', $word));
    }

    /**
     * @param $wordlistPath
     * @return array
     */
    protected function loadWordlist($wordlistPath)
    {
        $this->words = [];
        if (!file_exists($wordlistPath)) throw new \InvalidArgumentException('Cannot load wordlist from "%s"', $wordlistPath);

        foreach (file($wordlistPath) as $word) {
            $this->words[] = trim($word);
        }

        return $this->words;
    }
}