??????????????
PK       ! H=      flysystem/src/Directory.phpnu         <?php

namespace League\Flysystem;

class Directory extends Handler
{
    /**
     * Delete the directory.
     *
     * @return bool
     */
    public function delete()
    {
        return $this->filesystem->deleteDir($this->path);
    }

    /**
     * List the directory contents.
     *
     * @param bool $recursive
     *
     * @return array|bool directory contents or false
     */
    public function getContents($recursive = false)
    {
        return $this->filesystem->listContents($this->path, $recursive);
    }
}
PK       ! X    !  flysystem/src/Plugin/EmptyDir.phpnu         <?php

namespace League\Flysystem\Plugin;

class EmptyDir extends AbstractPlugin
{
    /**
     * Get the method name.
     *
     * @return string
     */
    public function getMethod()
    {
        return 'emptyDir';
    }

    /**
     * Empty a directory's contents.
     *
     * @param $dirname
     */
    public function handle($dirname)
    {
        $listing = $this->filesystem->listContents($dirname, false);

        foreach ($listing as $item) {
            if ($item['type'] === 'dir') {
                $this->filesystem->deleteDir($item['path']);
            } else {
                $this->filesystem->delete($item['path']);
            }
        }
    }
}
PK       ! 0W    #  flysystem/src/Plugin/ForcedCopy.phpnu         <?php

namespace League\Flysystem\Plugin;

use League\Flysystem\FileNotFoundException;

class ForcedCopy extends AbstractPlugin
{
    /**
     * @inheritdoc
     */
    public function getMethod()
    {
        return 'forceCopy';
    }

    /**
     * Copies a file, overwriting any existing files.
     *
     * @param string $path    Path to the existing file.
     * @param string $newpath The new path of the file.
     *
     * @throws FileNotFoundException Thrown if $path does not exist.
     *
     * @return bool True on success, false on failure.
     */
    public function handle($path, $newpath)
    {
        try {
            $deleted = $this->filesystem->delete($newpath);
        } catch (FileNotFoundException $e) {
            // The destination path does not exist. That's ok.
            $deleted = true;
        }

        if ($deleted) {
            return $this->filesystem->copy($path, $newpath);
        }

        return false;
    }
}
PK       ! N    '  flysystem/src/Plugin/PluggableTrait.phpnu         <?php

namespace League\Flysystem\Plugin;

use BadMethodCallException;
use League\Flysystem\FilesystemInterface;
use League\Flysystem\PluginInterface;
use LogicException;

trait PluggableTrait
{
    /**
     * @var array
     */
    protected $plugins = [];

    /**
     * Register a plugin.
     *
     * @param PluginInterface $plugin
     *
     * @throws LogicException
     *
     * @return $this
     */
    public function addPlugin(PluginInterface $plugin)
    {
        if ( ! method_exists($plugin, 'handle')) {
            throw new LogicException(get_class($plugin) . ' does not have a handle method.');
        }

        $this->plugins[$plugin->getMethod()] = $plugin;

        return $this;
    }

    /**
     * Find a specific plugin.
     *
     * @param string $method
     *
     * @throws PluginNotFoundException
     *
     * @return PluginInterface
     */
    protected function findPlugin($method)
    {
        if ( ! isset($this->plugins[$method])) {
            throw new PluginNotFoundException('Plugin not found for method: ' . $method);
        }

        return $this->plugins[$method];
    }

    /**
     * Invoke a plugin by method name.
     *
     * @param string              $method
     * @param array               $arguments
     * @param FilesystemInterface $filesystem
     *
     * @throws PluginNotFoundException
     *
     * @return mixed
     */
    protected function invokePlugin($method, array $arguments, FilesystemInterface $filesystem)
    {
        $plugin = $this->findPlugin($method);
        $plugin->setFilesystem($filesystem);
        $callback = [$plugin, 'handle'];

        return call_user_func_array($callback, $arguments);
    }

    /**
     * Plugins pass-through.
     *
     * @param string $method
     * @param array  $arguments
     *
     * @throws BadMethodCallException
     *
     * @return mixed
     */
    public function __call($method, array $arguments)
    {
        try {
            return $this->invokePlugin($method, $arguments, $this);
        } catch (PluginNotFoundException $e) {
            throw new BadMethodCallException(
                'Call to undefined method '
                . get_class($this)
                . '::' . $method
            );
        }
    }
}
PK       ! ٘    %  flysystem/src/Plugin/ForcedRename.phpnu         <?php

namespace League\Flysystem\Plugin;

use League\Flysystem\FileNotFoundException;

class ForcedRename extends AbstractPlugin
{
    /**
     * @inheritdoc
     */
    public function getMethod()
    {
        return 'forceRename';
    }

    /**
     * Renames a file, overwriting the destination if it exists.
     *
     * @param string $path    Path to the existing file.
     * @param string $newpath The new path of the file.
     *
     * @throws FileNotFoundException Thrown if $path does not exist.
     *
     * @return bool True on success, false on failure.
     */
    public function handle($path, $newpath)
    {
        try {
            $deleted = $this->filesystem->delete($newpath);
        } catch (FileNotFoundException $e) {
            // The destination path does not exist. That's ok.
            $deleted = true;
        }

        if ($deleted) {
            return $this->filesystem->rename($path, $newpath);
        }

        return false;
    }
}
PK       ! X4U    !  flysystem/src/Plugin/ListWith.phpnu         <?php

namespace League\Flysystem\Plugin;

class ListWith extends AbstractPlugin
{
    /**
     * Get the method name.
     *
     * @return string
     */
    public function getMethod()
    {
        return 'listWith';
    }

    /**
     * List contents with metadata.
     *
     * @param array  $keys
     * @param string $directory
     * @param bool   $recursive
     *
     * @return array listing with metadata
     */
    public function handle(array $keys = [], $directory = '', $recursive = false)
    {
        $contents = $this->filesystem->listContents($directory, $recursive);

        foreach ($contents as $index => $object) {
            if ($object['type'] === 'file') {
                $missingKeys = array_diff($keys, array_keys($object));
                $contents[$index] = array_reduce($missingKeys, [$this, 'getMetadataByName'], $object);
            }
        }

        return $contents;
    }

    /**
     * Get a meta-data value by key name.
     *
     * @param array $object
     * @param       $key
     *
     * @return array
     */
    protected function getMetadataByName(array $object, $key)
    {
        $method = 'get' . ucfirst($key);

        if ( ! method_exists($this->filesystem, $method)) {
            throw new \InvalidArgumentException('Could not get meta-data for key: ' . $key);
        }

        $object[$key] = $this->filesystem->{$method}($object['path']);

        return $object;
    }
}
PK       !       0  flysystem/src/Plugin/PluginNotFoundException.phpnu         <?php

namespace League\Flysystem\Plugin;

use LogicException;

class PluginNotFoundException extends LogicException
{
    // This exception doesn't require additional information.
}
PK       ! \J    "  flysystem/src/Plugin/ListPaths.phpnu         <?php

namespace League\Flysystem\Plugin;

class ListPaths extends AbstractPlugin
{
    /**
     * Get the method name.
     *
     * @return string
     */
    public function getMethod()
    {
        return 'listPaths';
    }

    /**
     * List all paths.
     *
     * @param string $directory
     * @param bool   $recursive
     *
     * @return array paths
     */
    public function handle($directory = '', $recursive = false)
    {
        $result = [];
        $contents = $this->filesystem->listContents($directory, $recursive);

        foreach ($contents as $object) {
            $result[] = $object['path'];
        }

        return $result;
    }
}
PK       ! 
N    "  flysystem/src/Plugin/ListFiles.phpnu         <?php

namespace League\Flysystem\Plugin;

class ListFiles extends AbstractPlugin
{
    /**
     * Get the method name.
     *
     * @return string
     */
    public function getMethod()
    {
        return 'listFiles';
    }

    /**
     * List all files in the directory.
     *
     * @param string $directory
     * @param bool   $recursive
     *
     * @return array
     */
    public function handle($directory = '', $recursive = false)
    {
        $contents = $this->filesystem->listContents($directory, $recursive);

        $filter = function ($object) {
            return $object['type'] === 'file';
        };

        return array_values(array_filter($contents, $filter));
    }
}
PK       ! LgR  R  (  flysystem/src/Plugin/GetWithMetadata.phpnu         <?php

namespace League\Flysystem\Plugin;

use InvalidArgumentException;

class GetWithMetadata extends AbstractPlugin
{
    /**
     * Get the method name.
     *
     * @return string
     */
    public function getMethod()
    {
        return 'getWithMetadata';
    }

    /**
     * Get metadata for an object with required metadata.
     *
     * @param string $path     path to file
     * @param array  $metadata metadata keys
     *
     * @throws InvalidArgumentException
     *
     * @return array|false metadata
     */
    public function handle($path, array $metadata)
    {
        $object = $this->filesystem->getMetadata($path);

        if ( ! $object) {
            return false;
        }

        $keys = array_diff($metadata, array_keys($object));

        foreach ($keys as $key) {
            if ( ! method_exists($this->filesystem, $method = 'get' . ucfirst($key))) {
                throw new InvalidArgumentException('Could not fetch metadata: ' . $key);
            }

            $object[$key] = $this->filesystem->{$method}($path);
        }

        return $object;
    }
}
PK       ! $    '  flysystem/src/Plugin/AbstractPlugin.phpnu         <?php

namespace League\Flysystem\Plugin;

use League\Flysystem\FilesystemInterface;
use League\Flysystem\PluginInterface;

abstract class AbstractPlugin implements PluginInterface
{
    /**
     * @var FilesystemInterface
     */
    protected $filesystem;

    /**
     * Set the Filesystem object.
     *
     * @param FilesystemInterface $filesystem
     */
    public function setFilesystem(FilesystemInterface $filesystem)
    {
        $this->filesystem = $filesystem;
    }
}
PK       ! u:    %  flysystem/src/FileExistsException.phpnu         <?php

namespace League\Flysystem;

use Exception as BaseException;

class FileExistsException extends Exception
{
    /**
     * @var string
     */
    protected $path;

    /**
     * Constructor.
     *
     * @param string        $path
     * @param int           $code
     * @param BaseException $previous
     */
    public function __construct($path, $code = 0, BaseException $previous = null)
    {
        $this->path = $path;

        parent::__construct('File already exists at path: ' . $this->getPath(), $code, $previous);
    }

    /**
     * Get the path which was found.
     *
     * @return string
     */
    public function getPath()
    {
        return $this->path;
    }
}
PK       ! h<h'  h'    flysystem/src/Filesystem.phpnu         <?php

namespace League\Flysystem;

use InvalidArgumentException;
use League\Flysystem\Adapter\CanOverwriteFiles;
use League\Flysystem\Plugin\PluggableTrait;
use League\Flysystem\Util\ContentListingFormatter;

/**
 * @method array getWithMetadata(string $path, array $metadata)
 * @method bool  forceCopy(string $path, string $newpath)
 * @method bool  forceRename(string $path, string $newpath)
 * @method array listFiles(string $path = '', boolean $recursive = false)
 * @method array listPaths(string $path = '', boolean $recursive = false)
 * @method array listWith(array $keys = [], $directory = '', $recursive = false)
 */
class Filesystem implements FilesystemInterface
{
    use PluggableTrait;
    use ConfigAwareTrait;

    /**
     * @var AdapterInterface
     */
    protected $adapter;

    /**
     * Constructor.
     *
     * @param AdapterInterface $adapter
     * @param Config|array     $config
     */
    public function __construct(AdapterInterface $adapter, $config = null)
    {
        $this->adapter = $adapter;
        $this->setConfig($config);
    }

    /**
     * Get the Adapter.
     *
     * @return AdapterInterface adapter
     */
    public function getAdapter()
    {
        return $this->adapter;
    }

    /**
     * @inheritdoc
     */
    public function has($path)
    {
        $path = Util::normalizePath($path);

        return strlen($path) === 0 ? false : (bool) $this->getAdapter()->has($path);
    }

    /**
     * @inheritdoc
     */
    public function write($path, $contents, array $config = [])
    {
        $path = Util::normalizePath($path);
        $this->assertAbsent($path);
        $config = $this->prepareConfig($config);

        return (bool) $this->getAdapter()->write($path, $contents, $config);
    }

    /**
     * @inheritdoc
     */
    public function writeStream($path, $resource, array $config = [])
    {
        if ( ! is_resource($resource)) {
            throw new InvalidArgumentException(__METHOD__ . ' expects argument #2 to be a valid resource.');
        }

        $path = Util::normalizePath($path);
        $this->assertAbsent($path);
        $config = $this->prepareConfig($config);

        Util::rewindStream($resource);

        return (bool) $this->getAdapter()->writeStream($path, $resource, $config);
    }

    /**
     * @inheritdoc
     */
    public function put($path, $contents, array $config = [])
    {
        $path = Util::normalizePath($path);
        $config = $this->prepareConfig($config);

        if ( ! $this->adapter instanceof CanOverwriteFiles && $this->has($path)) {
            return (bool) $this->getAdapter()->update($path, $contents, $config);
        }

        return (bool) $this->getAdapter()->write($path, $contents, $config);
    }

    /**
     * @inheritdoc
     */
    public function putStream($path, $resource, array $config = [])
    {
        if ( ! is_resource($resource)) {
            throw new InvalidArgumentException(__METHOD__ . ' expects argument #2 to be a valid resource.');
        }

        $path = Util::normalizePath($path);
        $config = $this->prepareConfig($config);
        Util::rewindStream($resource);

        if ( ! $this->adapter instanceof CanOverwriteFiles &&$this->has($path)) {
            return (bool) $this->getAdapter()->updateStream($path, $resource, $config);
        }

        return (bool) $this->getAdapter()->writeStream($path, $resource, $config);
    }

    /**
     * @inheritdoc
     */
    public function readAndDelete($path)
    {
        $path = Util::normalizePath($path);
        $this->assertPresent($path);
        $contents = $this->read($path);

        if ($contents === false) {
            return false;
        }

        $this->delete($path);

        return $contents;
    }

    /**
     * @inheritdoc
     */
    public function update($path, $contents, array $config = [])
    {
        $path = Util::normalizePath($path);
        $config = $this->prepareConfig($config);

        $this->assertPresent($path);

        return (bool) $this->getAdapter()->update($path, $contents, $config);
    }

    /**
     * @inheritdoc
     */
    public function updateStream($path, $resource, array $config = [])
    {
        if ( ! is_resource($resource)) {
            throw new InvalidArgumentException(__METHOD__ . ' expects argument #2 to be a valid resource.');
        }

        $path = Util::normalizePath($path);
        $config = $this->prepareConfig($config);
        $this->assertPresent($path);
        Util::rewindStream($resource);

        return (bool) $this->getAdapter()->updateStream($path, $resource, $config);
    }

    /**
     * @inheritdoc
     */
    public function read($path)
    {
        $path = Util::normalizePath($path);
        $this->assertPresent($path);

        if ( ! ($object = $this->getAdapter()->read($path))) {
            return false;
        }

        return $object['contents'];
    }

    /**
     * @inheritdoc
     */
    public function readStream($path)
    {
        $path = Util::normalizePath($path);
        $this->assertPresent($path);

        if ( ! $object = $this->getAdapter()->readStream($path)) {
            return false;
        }

        return $object['stream'];
    }

    /**
     * @inheritdoc
     */
    public function rename($path, $newpath)
    {
        $path = Util::normalizePath($path);
        $newpath = Util::normalizePath($newpath);
        $this->assertPresent($path);
        $this->assertAbsent($newpath);

        return (bool) $this->getAdapter()->rename($path, $newpath);
    }

    /**
     * @inheritdoc
     */
    public function copy($path, $newpath)
    {
        $path = Util::normalizePath($path);
        $newpath = Util::normalizePath($newpath);
        $this->assertPresent($path);
        $this->assertAbsent($newpath);

        return $this->getAdapter()->copy($path, $newpath);
    }

    /**
     * @inheritdoc
     */
    public function delete($path)
    {
        $path = Util::normalizePath($path);
        $this->assertPresent($path);

        return $this->getAdapter()->delete($path);
    }

    /**
     * @inheritdoc
     */
    public function deleteDir($dirname)
    {
        $dirname = Util::normalizePath($dirname);

        if ($dirname === '') {
            throw new RootViolationException('Root directories can not be deleted.');
        }

        return (bool) $this->getAdapter()->deleteDir($dirname);
    }

    /**
     * @inheritdoc
     */
    public function createDir($dirname, array $config = [])
    {
        $dirname = Util::normalizePath($dirname);
        $config = $this->prepareConfig($config);

        return (bool) $this->getAdapter()->createDir($dirname, $config);
    }

    /**
     * @inheritdoc
     */
    public function listContents($directory = '', $recursive = false)
    {
        $directory = Util::normalizePath($directory);
        $contents = $this->getAdapter()->listContents($directory, $recursive);

        return (new ContentListingFormatter($directory, $recursive))->formatListing($contents);
    }

    /**
     * @inheritdoc
     */
    public function getMimetype($path)
    {
        $path = Util::normalizePath($path);
        $this->assertPresent($path);

        if (( ! $object = $this->getAdapter()->getMimetype($path)) || ! array_key_exists('mimetype', $object)) {
            return false;
        }

        return $object['mimetype'];
    }

    /**
     * @inheritdoc
     */
    public function getTimestamp($path)
    {
        $path = Util::normalizePath($path);
        $this->assertPresent($path);

        if (( ! $object = $this->getAdapter()->getTimestamp($path)) || ! array_key_exists('timestamp', $object)) {
            return false;
        }

        return $object['timestamp'];
    }

    /**
     * @inheritdoc
     */
    public function getVisibility($path)
    {
        $path = Util::normalizePath($path);
        $this->assertPresent($path);

        if (( ! $object = $this->getAdapter()->getVisibility($path)) || ! array_key_exists('visibility', $object)) {
            return false;
        }

        return $object['visibility'];
    }

    /**
     * @inheritdoc
     */
    public function getSize($path)
    {
        $path = Util::normalizePath($path);

        if (( ! $object = $this->getAdapter()->getSize($path)) || ! array_key_exists('size', $object)) {
            return false;
        }

        return (int) $object['size'];
    }

    /**
     * @inheritdoc
     */
    public function setVisibility($path, $visibility)
    {
        $path = Util::normalizePath($path);

        return (bool) $this->getAdapter()->setVisibility($path, $visibility);
    }

    /**
     * @inheritdoc
     */
    public function getMetadata($path)
    {
        $path = Util::normalizePath($path);
        $this->assertPresent($path);

        return $this->getAdapter()->getMetadata($path);
    }

    /**
     * @inheritdoc
     */
    public function get($path, Handler $handler = null)
    {
        $path = Util::normalizePath($path);

        if ( ! $handler) {
            $metadata = $this->getMetadata($path);
            $handler = $metadata['type'] === 'file' ? new File($this, $path) : new Directory($this, $path);
        }

        $handler->setPath($path);
        $handler->setFilesystem($this);

        return $handler;
    }

    /**
     * Assert a file is present.
     *
     * @param string $path path to file
     *
     * @throws FileNotFoundException
     *
     * @return void
     */
    public function assertPresent($path)
    {
        if ($this->config->get('disable_asserts', false) === false && ! $this->has($path)) {
            throw new FileNotFoundException($path);
        }
    }

    /**
     * Assert a file is absent.
     *
     * @param string $path path to file
     *
     * @throws FileExistsException
     *
     * @return void
     */
    public function assertAbsent($path)
    {
        if ($this->config->get('disable_asserts', false) === false && $this->has($path)) {
            throw new FileExistsException($path);
        }
    }
}
PK       ! $<      flysystem/src/Config.phpnu         <?php

namespace League\Flysystem;

class Config
{
    /**
     * @var array
     */
    protected $settings = [];

    /**
     * @var Config
     */
    protected $fallback;

    /**
     * Constructor.
     *
     * @param array $settings
     */
    public function __construct(array $settings = [])
    {
        $this->settings = $settings;
    }

    /**
     * Get a setting.
     *
     * @param string $key
     * @param mixed  $default
     *
     * @return mixed config setting or default when not found
     */
    public function get($key, $default = null)
    {
        if ( ! array_key_exists($key, $this->settings)) {
            return $this->getDefault($key, $default);
        }

        return $this->settings[$key];
    }

    /**
     * Check if an item exists by key.
     *
     * @param string $key
     *
     * @return bool
     */
    public function has($key)
    {
        if (array_key_exists($key, $this->settings)) {
            return true;
        }

        return $this->fallback instanceof Config
            ? $this->fallback->has($key)
            : false;
    }

    /**
     * Try to retrieve a default setting from a config fallback.
     *
     * @param string $key
     * @param mixed  $default
     *
     * @return mixed config setting or default when not found
     */
    protected function getDefault($key, $default)
    {
        if ( ! $this->fallback) {
            return $default;
        }

        return $this->fallback->get($key, $default);
    }

    /**
     * Set a setting.
     *
     * @param string $key
     * @param mixed  $value
     *
     * @return $this
     */
    public function set($key, $value)
    {
        $this->settings[$key] = $value;

        return $this;
    }

    /**
     * Set the fallback.
     *
     * @param Config $fallback
     *
     * @return $this
     */
    public function setFallback(Config $fallback)
    {
        $this->fallback = $fallback;

        return $this;
    }
}
PK       ! ^_S  S  "  flysystem/src/ConfigAwareTrait.phpnu         <?php

namespace League\Flysystem;

/**
 * @internal
 */
trait ConfigAwareTrait
{
    /**
     * @var Config
     */
    protected $config;

    /**
     * Set the config.
     *
     * @param Config|array|null $config
     */
    protected function setConfig($config)
    {
        $this->config = $config ? Util::ensureConfig($config) : new Config;
    }

    /**
     * Get the Config.
     *
     * @return Config config object
     */
    public function getConfig()
    {
        return $this->config;
    }

    /**
     * Convert a config array to a Config object with the correct fallback.
     *
     * @param array $config
     *
     * @return Config
     */
    protected function prepareConfig(array $config)
    {
        $config = new Config($config);
        $config->setFallback($this->getConfig());

        return $config;
    }
}
PK       !       flysystem/src/Util.phpnu         <?php

namespace League\Flysystem;

use League\Flysystem\Util\MimeType;
use LogicException;

class Util
{
    /**
     * Get normalized pathinfo.
     *
     * @param string $path
     *
     * @return array pathinfo
     */
    public static function pathinfo($path)
    {
        $pathinfo = pathinfo($path) + compact('path');
        $pathinfo['dirname'] = array_key_exists('dirname', $pathinfo)
            ? static::normalizeDirname($pathinfo['dirname']) : '';

        return $pathinfo;
    }

    /**
     * Normalize a dirname return value.
     *
     * @param string $dirname
     *
     * @return string normalized dirname
     */
    public static function normalizeDirname($dirname)
    {
        return $dirname === '.' ? '' : $dirname;
    }

    /**
     * Get a normalized dirname from a path.
     *
     * @param string $path
     *
     * @return string dirname
     */
    public static function dirname($path)
    {
        return static::normalizeDirname(dirname($path));
    }

    /**
     * Map result arrays.
     *
     * @param array $object
     * @param array $map
     *
     * @return array mapped result
     */
    public static function map(array $object, array $map)
    {
        $result = [];

        foreach ($map as $from => $to) {
            if ( ! isset($object[$from])) {
                continue;
            }

            $result[$to] = $object[$from];
        }

        return $result;
    }

    /**
     * Normalize path.
     *
     * @param string $path
     *
     * @throws LogicException
     *
     * @return string
     */
    public static function normalizePath($path)
    {
        return static::normalizeRelativePath($path);
    }

    /**
     * Normalize relative directories in a path.
     *
     * @param string $path
     *
     * @throws LogicException
     *
     * @return string
     */
    public static function normalizeRelativePath($path)
    {
        $path = str_replace('\\', '/', $path);
        $path = static::removeFunkyWhiteSpace($path);

        $parts = [];

        foreach (explode('/', $path) as $part) {
            switch ($part) {
                case '':
                case '.':
                break;

            case '..':
                if (empty($parts)) {
                    throw new LogicException(
                        'Path is outside of the defined root, path: [' . $path . ']'
                    );
                }
                array_pop($parts);
                break;

            default:
                $parts[] = $part;
                break;
            }
        }

        return implode('/', $parts);
    }

    /**
     * Removes unprintable characters and invalid unicode characters.
     *
     * @param string $path
     *
     * @return string $path
     */
    protected static function removeFunkyWhiteSpace($path) {
        // We do this check in a loop, since removing invalid unicode characters
        // can lead to new characters being created.
        while (preg_match('#\p{C}+|^\./#u', $path)) {
            $path = preg_replace('#\p{C}+|^\./#u', '', $path);
        }

        return $path;
    }

    /**
     * Normalize prefix.
     *
     * @param string $prefix
     * @param string $separator
     *
     * @return string normalized path
     */
    public static function normalizePrefix($prefix, $separator)
    {
        return rtrim($prefix, $separator) . $separator;
    }

    /**
     * Get content size.
     *
     * @param string $contents
     *
     * @return int content size
     */
    public static function contentSize($contents)
    {
        return defined('MB_OVERLOAD_STRING') ? mb_strlen($contents, '8bit') : strlen($contents);
    }

    /**
     * Guess MIME Type based on the path of the file and it's content.
     *
     * @param string $path
     * @param string|resource $content
     *
     * @return string|null MIME Type or NULL if no extension detected
     */
    public static function guessMimeType($path, $content)
    {
        $mimeType = MimeType::detectByContent($content);

        if ( ! (empty($mimeType) || in_array($mimeType, ['application/x-empty', 'text/plain', 'text/x-asm']))) {
            return $mimeType;
        }

        return MimeType::detectByFilename($path);
    }

    /**
     * Emulate directories.
     *
     * @param array $listing
     *
     * @return array listing with emulated directories
     */
    public static function emulateDirectories(array $listing)
    {
        $directories = [];
        $listedDirectories = [];

        foreach ($listing as $object) {
            list($directories, $listedDirectories) = static::emulateObjectDirectories(
                $object,
                $directories,
                $listedDirectories
            );
        }

        $directories = array_diff(array_unique($directories), array_unique($listedDirectories));

        foreach ($directories as $directory) {
            $listing[] = static::pathinfo($directory) + ['type' => 'dir'];
        }

        return $listing;
    }

    /**
     * Ensure a Config instance.
     *
     * @param null|array|Config $config
     *
     * @return Config config instance
     *
     * @throw  LogicException
     */
    public static function ensureConfig($config)
    {
        if ($config === null) {
            return new Config();
        }

        if ($config instanceof Config) {
            return $config;
        }

        if (is_array($config)) {
            return new Config($config);
        }

        throw new LogicException('A config should either be an array or a Flysystem\Config object.');
    }

    /**
     * Rewind a stream.
     *
     * @param resource $resource
     */
    public static function rewindStream($resource)
    {
        if (ftell($resource) !== 0 && static::isSeekableStream($resource)) {
            rewind($resource);
        }
    }

    public static function isSeekableStream($resource)
    {
        $metadata = stream_get_meta_data($resource);

        return $metadata['seekable'];
    }

    /**
     * Get the size of a stream.
     *
     * @param resource $resource
     *
     * @return int stream size
     */
    public static function getStreamSize($resource)
    {
        $stat = fstat($resource);

        return $stat['size'];
    }

    /**
     * Emulate the directories of a single object.
     *
     * @param array $object
     * @param array $directories
     * @param array $listedDirectories
     *
     * @return array
     */
    protected static function emulateObjectDirectories(array $object, array $directories, array $listedDirectories)
    {
        if ($object['type'] === 'dir') {
            $listedDirectories[] = $object['path'];
        }

        if (empty($object['dirname'])) {
            return [$directories, $listedDirectories];
        }

        $parent = $object['dirname'];

        while ( ! empty($parent) && ! in_array($parent, $directories)) {
            $directories[] = $parent;
            $parent = static::dirname($parent);
        }

        if (isset($object['type']) && $object['type'] === 'dir') {
            $listedDirectories[] = $object['path'];

            return [$directories, $listedDirectories];
        }

        return [$directories, $listedDirectories];
    }
}
PK       ! >i      flysystem/src/ReadInterface.phpnu         <?php

namespace League\Flysystem;

interface ReadInterface
{
    /**
     * Check whether a file exists.
     *
     * @param string $path
     *
     * @return array|bool|null
     */
    public function has($path);

    /**
     * Read a file.
     *
     * @param string $path
     *
     * @return array|false
     */
    public function read($path);

    /**
     * Read a file as a stream.
     *
     * @param string $path
     *
     * @return array|false
     */
    public function readStream($path);

    /**
     * List contents of a directory.
     *
     * @param string $directory
     * @param bool   $recursive
     *
     * @return array
     */
    public function listContents($directory = '', $recursive = false);

    /**
     * Get all the meta data of a file or directory.
     *
     * @param string $path
     *
     * @return array|false
     */
    public function getMetadata($path);

    /**
     * Get the size of a file.
     *
     * @param string $path
     *
     * @return array|false
     */
    public function getSize($path);

    /**
     * Get the mimetype of a file.
     *
     * @param string $path
     *
     * @return array|false
     */
    public function getMimetype($path);

    /**
     * Get the timestamp of a file.
     *
     * @param string $path
     *
     * @return array|false
     */
    public function getTimestamp($path);

    /**
     * Get the visibility of a file.
     *
     * @param string $path
     *
     * @return array|false
     */
    public function getVisibility($path);
}
PK       ! "    '  flysystem/src/NotSupportedException.phpnu         <?php

namespace League\Flysystem;

use RuntimeException;
use SplFileInfo;

class NotSupportedException extends RuntimeException
{
    /**
     * Create a new exception for a link.
     *
     * @param SplFileInfo $file
     *
     * @return static
     */
    public static function forLink(SplFileInfo $file)
    {
        $message = 'Links are not supported, encountered link at ';

        return new static($message . $file->getPathname());
    }

    /**
     * Create a new exception for a link.
     *
     * @param string $systemType
     *
     * @return static
     */
    public static function forFtpSystemType($systemType)
    {
        $message = "The FTP system type '$systemType' is currently not supported.";

        return new static($message);
    }
}
PK       ! 5H  H  #  flysystem/src/Util/StreamHasher.phpnu         <?php

namespace League\Flysystem\Util;

class StreamHasher
{
    /**
     * @var string
     */
    private $algo;

    /**
     * StreamHasher constructor.
     *
     * @param string $algo
     */
    public function __construct($algo)
    {
        $this->algo = $algo;
    }

    /**
     * @param $resource
     *
     * @return string
     */
    public function hash($resource)
    {
        rewind($resource);
        $context = hash_init($this->algo);
        hash_update_stream($context, $resource);
        fclose($resource);

        return hash_final($context);
    }
}
PK       ! &]
   
     flysystem/src/Util/MimeType.phpnu         <?php

namespace League\Flysystem\Util;

use Finfo;
use ErrorException;

/**
 * @internal
 */
class MimeType
{
    /**
     * Detects MIME Type based on given content.
     *
     * @param mixed $content
     *
     * @return string|null MIME Type or NULL if no mime type detected
     */
    public static function detectByContent($content)
    {
        if ( ! class_exists('Finfo') || ! is_string($content)) {
            return;
        }
        try {
            $finfo = new Finfo(FILEINFO_MIME_TYPE);

            return $finfo->buffer($content) ?: null;
        } catch( ErrorException $e ) {
            // This is caused by an array to string conversion error.
        }
    }

    /**
     * Detects MIME Type based on file extension.
     *
     * @param string $extension
     *
     * @return string|null MIME Type or NULL if no extension detected
     */
    public static function detectByFileExtension($extension)
    {
        static $extensionToMimeTypeMap;

        if (! $extensionToMimeTypeMap) {
            $extensionToMimeTypeMap = static::getExtensionToMimeTypeMap();
        }

        if (isset($extensionToMimeTypeMap[$extension])) {
            return $extensionToMimeTypeMap[$extension];
        }

        return 'text/plain';
    }

    /**
     * @param string $filename
     *
     * @return string
     */
    public static function detectByFilename($filename)
    {
        $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));

        return empty($extension) ? 'text/plain' : static::detectByFileExtension($extension);
    }

    /**
     * @return array Map of file extension to MIME Type
     */
    public static function getExtensionToMimeTypeMap()
    {
        return [
            'hqx'   => 'application/mac-binhex40',
            'cpt'   => 'application/mac-compactpro',
            'csv'   => 'text/x-comma-separated-values',
            'bin'   => 'application/octet-stream',
            'dms'   => 'application/octet-stream',
            'lha'   => 'application/octet-stream',
            'lzh'   => 'application/octet-stream',
            'exe'   => 'application/octet-stream',
            'class' => 'application/octet-stream',
            'psd'   => 'application/x-photoshop',
            'so'    => 'application/octet-stream',
            'sea'   => 'application/octet-stream',
            'dll'   => 'application/octet-stream',
            'oda'   => 'application/oda',
            'pdf'   => 'application/pdf',
            'ai'    => 'application/pdf',
            'eps'   => 'application/postscript',
            'ps'    => 'application/postscript',
            'smi'   => 'application/smil',
            'smil'  => 'application/smil',
            'mif'   => 'application/vnd.mif',
            'xls'   => 'application/vnd.ms-excel',
            'ppt'   => 'application/powerpoint',
            'pptx'  => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
            'wbxml' => 'application/wbxml',
            'wmlc'  => 'application/wmlc',
            'dcr'   => 'application/x-director',
            'dir'   => 'application/x-director',
            'dxr'   => 'application/x-director',
            'dvi'   => 'application/x-dvi',
            'gtar'  => 'application/x-gtar',
            'gz'    => 'application/x-gzip',
            'gzip'  => 'application/x-gzip',
            'php'   => 'application/x-httpd-php',
            'php4'  => 'application/x-httpd-php',
            'php3'  => 'application/x-httpd-php',
            'phtml' => 'application/x-httpd-php',
            'phps'  => 'application/x-httpd-php-source',
            'js'    => 'application/javascript',
            'swf'   => 'application/x-shockwave-flash',
            'sit'   => 'application/x-stuffit',
            'tar'   => 'application/x-tar',
            'tgz'   => 'application/x-tar',
            'z'     => 'application/x-compress',
            'xhtml' => 'application/xhtml+xml',
            'xht'   => 'application/xhtml+xml',
            'zip'   => 'application/x-zip',
            'rar'   => 'application/x-rar',
            'mid'   => 'audio/midi',
            'midi'  => 'audio/midi',
            'mpga'  => 'audio/mpeg',
            'mp2'   => 'audio/mpeg',
            'mp3'   => 'audio/mpeg',
            'aif'   => 'audio/x-aiff',
            'aiff'  => 'audio/x-aiff',
            'aifc'  => 'audio/x-aiff',
            'ram'   => 'audio/x-pn-realaudio',
            'rm'    => 'audio/x-pn-realaudio',
            'rpm'   => 'audio/x-pn-realaudio-plugin',
            'ra'    => 'audio/x-realaudio',
            'rv'    => 'video/vnd.rn-realvideo',
            'wav'   => 'audio/x-wav',
            'jpg'   => 'image/jpeg',
            'jpeg'  => 'image/jpeg',
            'jpe'   => 'image/jpeg',
            'png'   => 'image/png',
            'gif'   => 'image/gif',
            'bmp'   => 'image/bmp',
            'tiff'  => 'image/tiff',
            'tif'   => 'image/tiff',
            'svg'   => 'image/svg+xml',
            'css'   => 'text/css',
            'html'  => 'text/html',
            'htm'   => 'text/html',
            'shtml' => 'text/html',
            'txt'   => 'text/plain',
            'text'  => 'text/plain',
            'log'   => 'text/plain',
            'rtx'   => 'text/richtext',
            'rtf'   => 'text/rtf',
            'xml'   => 'application/xml',
            'xsl'   => 'application/xml',
            'mpeg'  => 'video/mpeg',
            'mpg'   => 'video/mpeg',
            'mpe'   => 'video/mpeg',
            'qt'    => 'video/quicktime',
            'mov'   => 'video/quicktime',
            'avi'   => 'video/x-msvideo',
            'movie' => 'video/x-sgi-movie',
            'doc'   => 'application/msword',
            'docx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'dot'   => 'application/msword',
            'dotx'  => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'xlsx'  => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
            'word'  => 'application/msword',
            'xl'    => 'application/excel',
            'eml'   => 'message/rfc822',
            'json'  => 'application/json',
            'pem'   => 'application/x-x509-user-cert',
            'p10'   => 'application/x-pkcs10',
            'p12'   => 'application/x-pkcs12',
            'p7a'   => 'application/x-pkcs7-signature',
            'p7c'   => 'application/pkcs7-mime',
            'p7m'   => 'application/pkcs7-mime',
            'p7r'   => 'application/x-pkcs7-certreqresp',
            'p7s'   => 'application/pkcs7-signature',
            'crt'   => 'application/x-x509-ca-cert',
            'crl'   => 'application/pkix-crl',
            'der'   => 'application/x-x509-ca-cert',
            'kdb'   => 'application/octet-stream',
            'pgp'   => 'application/pgp',
            'gpg'   => 'application/gpg-keys',
            'sst'   => 'application/octet-stream',
            'csr'   => 'application/octet-stream',
            'rsa'   => 'application/x-pkcs7',
            'cer'   => 'application/pkix-cert',
            '3g2'   => 'video/3gpp2',
            '3gp'   => 'video/3gp',
            'mp4'   => 'video/mp4',
            'm4a'   => 'audio/x-m4a',
            'f4v'   => 'video/mp4',
            'webm'  => 'video/webm',
            'aac'   => 'audio/x-acc',
            'm4u'   => 'application/vnd.mpegurl',
            'm3u'   => 'text/plain',
            'xspf'  => 'application/xspf+xml',
            'vlc'   => 'application/videolan',
            'wmv'   => 'video/x-ms-wmv',
            'au'    => 'audio/x-au',
            'ac3'   => 'audio/ac3',
            'flac'  => 'audio/x-flac',
            'ogg'   => 'audio/ogg',
            'kmz'   => 'application/vnd.google-earth.kmz',
            'kml'   => 'application/vnd.google-earth.kml+xml',
            'ics'   => 'text/calendar',
            'zsh'   => 'text/x-scriptzsh',
            '7zip'  => 'application/x-7z-compressed',
            'cdr'   => 'application/cdr',
            'wma'   => 'audio/x-ms-wma',
            'jar'   => 'application/java-archive',
        ];
    }
}
PK       ! 텁+	  +	  .  flysystem/src/Util/ContentListingFormatter.phpnu         <?php

namespace League\Flysystem\Util;

use League\Flysystem\Util;

/**
 * @internal
 */
class ContentListingFormatter
{
    /**
     * @var string
     */
    private $directory;
    /**
     * @var bool
     */
    private $recursive;

    /**
     * @param string $directory
     * @param bool   $recursive
     */
    public function __construct($directory, $recursive)
    {
        $this->directory = $directory;
        $this->recursive = $recursive;
    }

    /**
     * Format contents listing.
     *
     * @param array $listing
     *
     * @return array
     */
    public function formatListing(array $listing)
    {
        $listing = array_values(
            array_map(
                [$this, 'addPathInfo'],
                array_filter($listing, [$this, 'isEntryOutOfScope'])
            )
        );

        return $this->sortListing($listing);
    }

    private function addPathInfo(array $entry)
    {
        return $entry + Util::pathinfo($entry['path']);
    }

    /**
     * Determine if the entry is out of scope.
     *
     * @param array $entry
     *
     * @return bool
     */
    private function isEntryOutOfScope(array $entry)
    {
        if (empty($entry['path']) && $entry['path'] !== '0') {
            return false;
        }

        if ($this->recursive) {
            return $this->residesInDirectory($entry);
        }

        return $this->isDirectChild($entry);
    }

    /**
     * Check if the entry resides within the parent directory.
     *
     * @param $entry
     *
     * @return bool
     */
    private function residesInDirectory(array $entry)
    {
        if ($this->directory === '') {
            return true;
        }

        return strpos($entry['path'], $this->directory . '/') === 0;
    }

    /**
     * Check if the entry is a direct child of the directory.
     *
     * @param $entry
     *
     * @return bool
     */
    private function isDirectChild(array $entry)
    {
        return Util::dirname($entry['path']) === $this->directory;
    }

    /**
     * @param array $listing
     *
     * @return array
     */
    private function sortListing(array $listing)
    {
        usort(
            $listing,
            function ($a, $b) {
                return strcasecmp($a['path'], $b['path']);
            }
        );

        return $listing;
    }
}
PK       ! '\      -  flysystem/src/FilesystemNotFoundException.phpnu         <?php

namespace League\Flysystem;

use LogicException;

/**
 * Thrown when the MountManager cannot find a filesystem.
 */
class FilesystemNotFoundException extends LogicException
{
}
PK       ! y  y  %  flysystem/src/FilesystemInterface.phpnu         <?php

namespace League\Flysystem;

interface FilesystemInterface
{
    /**
     * Check whether a file exists.
     *
     * @param string $path
     *
     * @return bool
     */
    public function has($path);

    /**
     * Read a file.
     *
     * @param string $path The path to the file.
     *
     * @throws FileNotFoundException
     *
     * @return string|false The file contents or false on failure.
     */
    public function read($path);

    /**
     * Retrieves a read-stream for a path.
     *
     * @param string $path The path to the file.
     *
     * @throws FileNotFoundException
     *
     * @return resource|false The path resource or false on failure.
     */
    public function readStream($path);

    /**
     * List contents of a directory.
     *
     * @param string $directory The directory to list.
     * @param bool   $recursive Whether to list recursively.
     *
     * @return array A list of file metadata.
     */
    public function listContents($directory = '', $recursive = false);

    /**
     * Get a file's metadata.
     *
     * @param string $path The path to the file.
     *
     * @throws FileNotFoundException
     *
     * @return array|false The file metadata or false on failure.
     */
    public function getMetadata($path);

    /**
     * Get a file's size.
     *
     * @param string $path The path to the file.
     *
     * @return int|false The file size or false on failure.
     */
    public function getSize($path);

    /**
     * Get a file's mime-type.
     *
     * @param string $path The path to the file.
     *
     * @throws FileNotFoundException
     *
     * @return string|false The file mime-type or false on failure.
     */
    public function getMimetype($path);

    /**
     * Get a file's timestamp.
     *
     * @param string $path The path to the file.
     *
     * @throws FileNotFoundException
     *
     * @return string|false The timestamp or false on failure.
     */
    public function getTimestamp($path);

    /**
     * Get a file's visibility.
     *
     * @param string $path The path to the file.
     *
     * @throws FileNotFoundException
     *
     * @return string|false The visibility (public|private) or false on failure.
     */
    public function getVisibility($path);

    /**
     * Write a new file.
     *
     * @param string $path     The path of the new file.
     * @param string $contents The file contents.
     * @param array  $config   An optional configuration array.
     *
     * @throws FileExistsException
     *
     * @return bool True on success, false on failure.
     */
    public function write($path, $contents, array $config = []);

    /**
     * Write a new file using a stream.
     *
     * @param string   $path     The path of the new file.
     * @param resource $resource The file handle.
     * @param array    $config   An optional configuration array.
     *
     * @throws \InvalidArgumentException If $resource is not a file handle.
     * @throws FileExistsException
     *
     * @return bool True on success, false on failure.
     */
    public function writeStream($path, $resource, array $config = []);

    /**
     * Update an existing file.
     *
     * @param string $path     The path of the existing file.
     * @param string $contents The file contents.
     * @param array  $config   An optional configuration array.
     *
     * @throws FileNotFoundException
     *
     * @return bool True on success, false on failure.
     */
    public function update($path, $contents, array $config = []);

    /**
     * Update an existing file using a stream.
     *
     * @param string   $path     The path of the existing file.
     * @param resource $resource The file handle.
     * @param array    $config   An optional configuration array.
     *
     * @throws \InvalidArgumentException If $resource is not a file handle.
     * @throws FileNotFoundException
     *
     * @return bool True on success, false on failure.
     */
    public function updateStream($path, $resource, array $config = []);

    /**
     * Rename a file.
     *
     * @param string $path    Path to the existing file.
     * @param string $newpath The new path of the file.
     *
     * @throws FileExistsException   Thrown if $newpath exists.
     * @throws FileNotFoundException Thrown if $path does not exist.
     *
     * @return bool True on success, false on failure.
     */
    public function rename($path, $newpath);

    /**
     * Copy a file.
     *
     * @param string $path    Path to the existing file.
     * @param string $newpath The new path of the file.
     *
     * @throws FileExistsException   Thrown if $newpath exists.
     * @throws FileNotFoundException Thrown if $path does not exist.
     *
     * @return bool True on success, false on failure.
     */
    public function copy($path, $newpath);

    /**
     * Delete a file.
     *
     * @param string $path
     *
     * @throws FileNotFoundException
     *
     * @return bool True on success, false on failure.
     */
    public function delete($path);

    /**
     * Delete a directory.
     *
     * @param string $dirname
     *
     * @throws RootViolationException Thrown if $dirname is empty.
     *
     * @return bool True on success, false on failure.
     */
    public function deleteDir($dirname);

    /**
     * Create a directory.
     *
     * @param string $dirname The name of the new directory.
     * @param array  $config  An optional configuration array.
     *
     * @return bool True on success, false on failure.
     */
    public function createDir($dirname, array $config = []);

    /**
     * Set the visibility for a file.
     *
     * @param string $path       The path to the file.
     * @param string $visibility One of 'public' or 'private'.
     *
     * @return bool True on success, false on failure.
     */
    public function setVisibility($path, $visibility);

    /**
     * Create a file or update if exists.
     *
     * @param string $path     The path to the file.
     * @param string $contents The file contents.
     * @param array  $config   An optional configuration array.
     *
     * @return bool True on success, false on failure.
     */
    public function put($path, $contents, array $config = []);

    /**
     * Create a file or update if exists.
     *
     * @param string   $path     The path to the file.
     * @param resource $resource The file handle.
     * @param array    $config   An optional configuration array.
     *
     * @throws \InvalidArgumentException Thrown if $resource is not a resource.
     *
     * @return bool True on success, false on failure.
     */
    public function putStream($path, $resource, array $config = []);

    /**
     * Read and delete a file.
     *
     * @param string $path The path to the file.
     *
     * @throws FileNotFoundException
     *
     * @return string|false The file contents, or false on failure.
     */
    public function readAndDelete($path);

    /**
     * Get a file/directory handler.
     *
     * @param string  $path    The path to the file.
     * @param Handler $handler An optional existing handler to populate.
     *
     * @return Handler Either a file or directory handler.
     */
    public function get($path, Handler $handler = null);

    /**
     * Register a plugin.
     *
     * @param PluginInterface $plugin The plugin to register.
     *
     * @return $this
     */
    public function addPlugin(PluginInterface $plugin);
}
PK       ! LYm	  	    flysystem/src/Handler.phpnu         <?php

namespace League\Flysystem;

use BadMethodCallException;

abstract class Handler
{
    /**
     * @var string
     */
    protected $path;

    /**
     * @var FilesystemInterface
     */
    protected $filesystem;

    /**
     * Constructor.
     *
     * @param FilesystemInterface $filesystem
     * @param string              $path
     */
    public function __construct(FilesystemInterface $filesystem = null, $path = null)
    {
        $this->path = $path;
        $this->filesystem = $filesystem;
    }

    /**
     * Check whether the entree is a directory.
     *
     * @return bool
     */
    public function isDir()
    {
        return $this->getType() === 'dir';
    }

    /**
     * Check whether the entree is a file.
     *
     * @return bool
     */
    public function isFile()
    {
        return $this->getType() === 'file';
    }

    /**
     * Retrieve the entree type (file|dir).
     *
     * @return string file or dir
     */
    public function getType()
    {
        $metadata = $this->filesystem->getMetadata($this->path);

        return $metadata['type'];
    }

    /**
     * Set the Filesystem object.
     *
     * @param FilesystemInterface $filesystem
     *
     * @return $this
     */
    public function setFilesystem(FilesystemInterface $filesystem)
    {
        $this->filesystem = $filesystem;

        return $this;
    }
    
    /**
     * Retrieve the Filesystem object.
     *
     * @return FilesystemInterface
     */
    public function getFilesystem()
    {
        return $this->filesystem;
    }

    /**
     * Set the entree path.
     *
     * @param string $path
     *
     * @return $this
     */
    public function setPath($path)
    {
        $this->path = $path;

        return $this;
    }

    /**
     * Retrieve the entree path.
     *
     * @return string path
     */
    public function getPath()
    {
        return $this->path;
    }

    /**
     * Plugins pass-through.
     *
     * @param string $method
     * @param array  $arguments
     *
     * @return mixed
     */
    public function __call($method, array $arguments)
    {
        array_unshift($arguments, $this->path);
        $callback = [$this->filesystem, $method];

        try {
            return call_user_func_array($callback, $arguments);
        } catch (BadMethodCallException $e) {
            throw new BadMethodCallException(
                'Call to undefined method '
                . get_called_class()
                . '::' . $method
            );
        }
    }
}
PK       ! ]-/=      flysystem/src/Adapter/Ftpd.phpnu         <?php

namespace League\Flysystem\Adapter;

class Ftpd extends Ftp
{
    /**
     * @inheritdoc
     */
    public function getMetadata($path)
    {
        if ($path === '') {
            return ['type' => 'dir', 'path' => ''];
        }

        if (! ($object = ftp_raw($this->getConnection(), 'STAT ' . $path)) || count($object) < 3) {
            return false;
        }

        if (substr($object[1], 0, 5) === "ftpd:") {
            return false;
        }

        return $this->normalizeObject($object[1], '');
    }

    /**
     * @inheritdoc
     */
    protected function listDirectoryContents($directory, $recursive = true)
    {
        $listing = ftp_rawlist($this->getConnection(), $directory, $recursive);

        if ($listing === false || ( ! empty($listing) && substr($listing[0], 0, 5) === "ftpd:")) {
            return [];
        }

        return $this->normalizeListing($listing, $directory);
    }
}
PK       ! tU!  !  7  flysystem/src/Adapter/Polyfill/StreamedWritingTrait.phpnu         <?php

namespace League\Flysystem\Adapter\Polyfill;

use League\Flysystem\Config;
use League\Flysystem\Util;

trait StreamedWritingTrait
{
    /**
     * Stream fallback delegator.
     *
     * @param string   $path
     * @param resource $resource
     * @param Config   $config
     * @param string   $fallback
     *
     * @return mixed fallback result
     */
    protected function stream($path, $resource, Config $config, $fallback)
    {
        Util::rewindStream($resource);
        $contents = stream_get_contents($resource);
        $fallbackCall = [$this, $fallback];

        return call_user_func($fallbackCall, $path, $contents, $config);
    }

    /**
     * Write using a stream.
     *
     * @param string   $path
     * @param resource $resource
     * @param Config   $config
     *
     * @return mixed false or file metadata
     */
    public function writeStream($path, $resource, Config $config)
    {
        return $this->stream($path, $resource, $config, 'write');
    }

    /**
     * Update a file using a stream.
     *
     * @param string   $path
     * @param resource $resource
     * @param Config   $config   Config object or visibility setting
     *
     * @return mixed false of file metadata
     */
    public function updateStream($path, $resource, Config $config)
    {
        return $this->stream($path, $resource, $config, 'update');
    }

    // Required abstract methods
    abstract public function write($pash, $contents, Config $config);
    abstract public function update($pash, $contents, Config $config);
}
PK       ! {E}  }  7  flysystem/src/Adapter/Polyfill/StreamedReadingTrait.phpnu         <?php

namespace League\Flysystem\Adapter\Polyfill;

/**
 * A helper for adapters that only handle strings to provide read streams.
 */
trait StreamedReadingTrait
{
    /**
     * Reads a file as a stream.
     *
     * @param string $path
     *
     * @return array|false
     *
     * @see League\Flysystem\ReadInterface::readStream()
     */
    public function readStream($path)
    {
        if ( ! $data = $this->read($path)) {
            return false;
        }

        $stream = fopen('php://temp', 'w+b');
        fwrite($stream, $data['contents']);
        rewind($stream);
        $data['stream'] = $stream;
        unset($data['contents']);

        return $data;
    }

    /**
     * Reads a file.
     *
     * @param string $path
     *
     * @return array|false
     *
     * @see League\Flysystem\ReadInterface::read()
     */
    abstract public function read($path);
}
PK       ! U    4  flysystem/src/Adapter/Polyfill/StreamedCopyTrait.phpnu         <?php

namespace League\Flysystem\Adapter\Polyfill;

use League\Flysystem\Config;

trait StreamedCopyTrait
{
    /**
     * Copy a file.
     *
     * @param string $path
     * @param string $newpath
     *
     * @return bool
     */
    public function copy($path, $newpath)
    {
        $response = $this->readStream($path);

        if ($response === false || ! is_resource($response['stream'])) {
            return false;
        }

        $result = $this->writeStream($newpath, $response['stream'], new Config());

        if ($result !== false && is_resource($response['stream'])) {
            fclose($response['stream']);
        }

        return $result !== false;
    }

    // Required abstract method

    /**
     * @param  string   $path
     * @return resource
     */
    abstract public function readStream($path);

    /**
     * @param  string   $path
     * @param  resource $resource
     * @param  Config   $config
     * @return resource
     */
    abstract public function writeStream($path, $resource, Config $config);
}
PK       ! :      0  flysystem/src/Adapter/Polyfill/StreamedTrait.phpnu         <?php

namespace League\Flysystem\Adapter\Polyfill;

trait StreamedTrait
{
    use StreamedReadingTrait;
    use StreamedWritingTrait;
}
PK       ! \I    ?  flysystem/src/Adapter/Polyfill/NotSupportingVisibilityTrait.phpnu         <?php

namespace League\Flysystem\Adapter\Polyfill;

use LogicException;

trait NotSupportingVisibilityTrait
{
    /**
     * Get the visibility of a file.
     *
     * @param string $path
     *
     * @throws LogicException
     */
    public function getVisibility($path)
    {
        throw new LogicException(get_class($this) . ' does not support visibility. Path: ' . $path);
    }

    /**
     * Set the visibility for a file.
     *
     * @param string $path
     * @param string $visibility
     *
     * @throws LogicException
     */
    public function setVisibility($path, $visibility)
    {
        throw new LogicException(get_class($this) . ' does not support visibility. Path: ' . $path . ', visibility: ' . $visibility);
    }
}
PK       ! .Q4  4    flysystem/src/Adapter/Ftp.phpnu         <?php

namespace League\Flysystem\Adapter;

use ErrorException;
use League\Flysystem\Adapter\Polyfill\StreamedCopyTrait;
use League\Flysystem\AdapterInterface;
use League\Flysystem\Config;
use League\Flysystem\Util;
use League\Flysystem\Util\MimeType;
use RuntimeException;

class Ftp extends AbstractFtpAdapter
{
    use StreamedCopyTrait;

    /**
     * @var int
     */
    protected $transferMode = FTP_BINARY;

    /**
     * @var null|bool
     */
    protected $ignorePassiveAddress = null;

    /**
     * @var bool
     */
    protected $recurseManually = false;

    /**
     * @var bool
     */
    protected $utf8 = false;

    /**
     * @var array
     */
    protected $configurable = [
        'host',
        'port',
        'username',
        'password',
        'ssl',
        'timeout',
        'root',
        'permPrivate',
        'permPublic',
        'passive',
        'transferMode',
        'systemType',
        'ignorePassiveAddress',
        'recurseManually',
        'utf8',
    ];

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

    /**
     * Set the transfer mode.
     *
     * @param int $mode
     *
     * @return $this
     */
    public function setTransferMode($mode)
    {
        $this->transferMode = $mode;

        return $this;
    }

    /**
     * Set if Ssl is enabled.
     *
     * @param bool $ssl
     *
     * @return $this
     */
    public function setSsl($ssl)
    {
        $this->ssl = (bool) $ssl;

        return $this;
    }

    /**
     * Set if passive mode should be used.
     *
     * @param bool $passive
     */
    public function setPassive($passive = true)
    {
        $this->passive = $passive;
    }

    /**
     * @param bool $ignorePassiveAddress
     */
    public function setIgnorePassiveAddress($ignorePassiveAddress)
    {
        $this->ignorePassiveAddress = $ignorePassiveAddress;
    }

    /**
     * @param bool $recurseManually
     */
    public function setRecurseManually($recurseManually)
    {
        $this->recurseManually = $recurseManually;
    }

    /**
     * @param bool $utf8
     */
    public function setUtf8($utf8)
    {
        $this->utf8 = (bool) $utf8;
    }

    /**
     * Connect to the FTP server.
     */
    public function connect()
    {
        if ($this->ssl) {
            $this->connection = ftp_ssl_connect($this->getHost(), $this->getPort(), $this->getTimeout());
        } else {
            $this->connection = ftp_connect($this->getHost(), $this->getPort(), $this->getTimeout());
        }

        if ( ! $this->connection) {
            throw new RuntimeException('Could not connect to host: ' . $this->getHost() . ', port:' . $this->getPort());
        }

        $this->login();
        $this->setUtf8Mode();
        $this->setConnectionPassiveMode();
        $this->setConnectionRoot();
        $this->isPureFtpd = $this->isPureFtpdServer();
    }

    /**
     * Set the connection to UTF-8 mode.
     */
    protected function setUtf8Mode()
    {
        if ($this->utf8) {
            $response = ftp_raw($this->connection, "OPTS UTF8 ON");
            if (substr($response[0], 0, 3) !== '200') {
                throw new RuntimeException(
                    'Could not set UTF-8 mode for connection: ' . $this->getHost() . '::' . $this->getPort()
                );
            }
        }
    }

    /**
     * Set the connections to passive mode.
     *
     * @throws RuntimeException
     */
    protected function setConnectionPassiveMode()
    {
        if (is_bool($this->ignorePassiveAddress) && defined('FTP_USEPASVADDRESS')) {
            ftp_set_option($this->connection, FTP_USEPASVADDRESS, ! $this->ignorePassiveAddress);
        }

        if ( ! ftp_pasv($this->connection, $this->passive)) {
            throw new RuntimeException(
                'Could not set passive mode for connection: ' . $this->getHost() . '::' . $this->getPort()
            );
        }
    }

    /**
     * Set the connection root.
     */
    protected function setConnectionRoot()
    {
        $root = $this->getRoot();
        $connection = $this->connection;

        if (isset($root) && ! ftp_chdir($connection, $root)) {
            throw new RuntimeException('Root is invalid or does not exist: ' . $this->getRoot());
        }

        // Store absolute path for further reference.
        // This is needed when creating directories and
        // initial root was a relative path, else the root
        // would be relative to the chdir'd path.
        $this->root = ftp_pwd($connection);
    }

    /**
     * Login.
     *
     * @throws RuntimeException
     */
    protected function login()
    {
        set_error_handler(function () {});
        $isLoggedIn = ftp_login(
            $this->connection,
            $this->getUsername(),
            $this->getPassword()
        );
        restore_error_handler();

        if ( ! $isLoggedIn) {
            $this->disconnect();
            throw new RuntimeException(
                'Could not login with connection: ' . $this->getHost() . '::' . $this->getPort(
                ) . ', username: ' . $this->getUsername()
            );
        }
    }

    /**
     * Disconnect from the FTP server.
     */
    public function disconnect()
    {
        if (is_resource($this->connection)) {
            ftp_close($this->connection);
        }

        $this->connection = null;
    }

    /**
     * @inheritdoc
     */
    public function write($path, $contents, Config $config)
    {
        $stream = fopen('php://temp', 'w+b');
        fwrite($stream, $contents);
        rewind($stream);
        $result = $this->writeStream($path, $stream, $config);
        fclose($stream);

        if ($result === false) {
            return false;
        }

        $result['contents'] = $contents;
        $result['mimetype'] = Util::guessMimeType($path, $contents);

        return $result;
    }

    /**
     * @inheritdoc
     */
    public function writeStream($path, $resource, Config $config)
    {
        $this->ensureDirectory(Util::dirname($path));

        if ( ! ftp_fput($this->getConnection(), $path, $resource, $this->transferMode)) {
            return false;
        }

        if ($visibility = $config->get('visibility')) {
            $this->setVisibility($path, $visibility);
        }

        $type = 'file';

        return compact('type', 'path', 'visibility');
    }

    /**
     * @inheritdoc
     */
    public function update($path, $contents, Config $config)
    {
        return $this->write($path, $contents, $config);
    }

    /**
     * @inheritdoc
     */
    public function updateStream($path, $resource, Config $config)
    {
        return $this->writeStream($path, $resource, $config);
    }

    /**
     * @inheritdoc
     */
    public function rename($path, $newpath)
    {
        return ftp_rename($this->getConnection(), $path, $newpath);
    }

    /**
     * @inheritdoc
     */
    public function delete($path)
    {
        return ftp_delete($this->getConnection(), $path);
    }

    /**
     * @inheritdoc
     */
    public function deleteDir($dirname)
    {
        $connection = $this->getConnection();
        $contents = array_reverse($this->listDirectoryContents($dirname));

        foreach ($contents as $object) {
            if ($object['type'] === 'file') {
                if ( ! ftp_delete($connection, $object['path'])) {
                    return false;
                }
            } elseif ( ! ftp_rmdir($connection, $object['path'])) {
                return false;
            }
        }

        return ftp_rmdir($connection, $dirname);
    }

    /**
     * @inheritdoc
     */
    public function createDir($dirname, Config $config)
    {
        $connection = $this->getConnection();
        $directories = explode('/', $dirname);

        foreach ($directories as $directory) {
            if (false === $this->createActualDirectory($directory, $connection)) {
                $this->setConnectionRoot();

                return false;
            }

            ftp_chdir($connection, $directory);
        }

        $this->setConnectionRoot();

        return ['type' => 'dir', 'path' => $dirname];
    }

    /**
     * Create a directory.
     *
     * @param string   $directory
     * @param resource $connection
     *
     * @return bool
     */
    protected function createActualDirectory($directory, $connection)
    {
        // List the current directory
        $listing = ftp_nlist($connection, '.') ?: [];

        foreach ($listing as $key => $item) {
            if (preg_match('~^\./.*~', $item)) {
                $listing[$key] = substr($item, 2);
            }
        }

        if (in_array($directory, $listing, true)) {
            return true;
        }

        return (boolean) ftp_mkdir($connection, $directory);
    }

    /**
     * @inheritdoc
     */
    public function getMetadata($path)
    {
        $connection = $this->getConnection();

        if ($path === '') {
            return ['type' => 'dir', 'path' => ''];
        }

        if (@ftp_chdir($connection, $path) === true) {
            $this->setConnectionRoot();

            return ['type' => 'dir', 'path' => $path];
        }

        $listing = $this->ftpRawlist('-A', str_replace('*', '\\*', $path));

        if (empty($listing) || in_array('total 0', $listing, true)) {
            return false;
        }

        if (preg_match('/.* not found/', $listing[0])) {
            return false;
        }

        if (preg_match('/^total [0-9]*$/', $listing[0])) {
            array_shift($listing);
        }

        return $this->normalizeObject($listing[0], '');
    }

    /**
     * @inheritdoc
     */
    public function getMimetype($path)
    {
        if ( ! $metadata = $this->getMetadata($path)) {
            return false;
        }

        $metadata['mimetype'] = MimeType::detectByFilename($path);

        return $metadata;
    }

    /**
     * @inheritdoc
     */
    public function getTimestamp($path)
    {
        $timestamp = ftp_mdtm($this->getConnection(), $path);

        return ($timestamp !== -1) ? ['path' => $path, 'timestamp' => $timestamp] : false;
    }

    /**
     * @inheritdoc
     */
    public function read($path)
    {
        if ( ! $object = $this->readStream($path)) {
            return false;
        }

        $object['contents'] = stream_get_contents($object['stream']);
        fclose($object['stream']);
        unset($object['stream']);

        return $object;
    }

    /**
     * @inheritdoc
     */
    public function readStream($path)
    {
        $stream = fopen('php://temp', 'w+b');
        $result = ftp_fget($this->getConnection(), $stream, $path, $this->transferMode);
        rewind($stream);

        if ( ! $result) {
            fclose($stream);

            return false;
        }

        return ['type' => 'file', 'path' => $path, 'stream' => $stream];
    }

    /**
     * @inheritdoc
     */
    public function setVisibility($path, $visibility)
    {
        $mode = $visibility === AdapterInterface::VISIBILITY_PUBLIC ? $this->getPermPublic() : $this->getPermPrivate();

        if ( ! ftp_chmod($this->getConnection(), $mode, $path)) {
            return false;
        }

        return compact('path', 'visibility');
    }

    /**
     * @inheritdoc
     *
     * @param string $directory
     */
    protected function listDirectoryContents($directory, $recursive = true)
    {
        $directory = str_replace('*', '\\*', $directory);

        if ($recursive && $this->recurseManually) {
            return $this->listDirectoryContentsRecursive($directory);
        }

        $options = $recursive ? '-alnR' : '-aln';
        $listing = $this->ftpRawlist($options, $directory);

        return $listing ? $this->normalizeListing($listing, $directory) : [];
    }

    /**
     * @inheritdoc
     *
     * @param string $directory
     */
    protected function listDirectoryContentsRecursive($directory)
    {
        $listing = $this->normalizeListing($this->ftpRawlist('-aln', $directory) ?: []);
        $output = [];

        foreach ($listing as $directory) {
            $output[] = $directory;
            if ($directory['type'] !== 'dir') continue;

            $output = array_merge($output, $this->listDirectoryContentsRecursive($directory['path']));
        }

        return $output;
    }

    /**
     * Check if the connection is open.
     *
     * @return bool
     * @throws ErrorException
     */
    public function isConnected()
    {
        try {
            return is_resource($this->connection) && ftp_rawlist($this->connection, '/') !== false;
        } catch (ErrorException $e) {
            if (strpos($e->getMessage(), 'ftp_rawlist') === false) {
                throw $e;
            }

            return false;
        }
    }

    /**
     * @return null|string
     */
    protected function isPureFtpdServer()
    {
        $response = ftp_raw($this->connection, 'HELP');

        return stripos(implode(' ', $response), 'Pure-FTPd') !== false;
    }

    /**
     * The ftp_rawlist function with optional escaping.
     *
     * @param string $options
     * @param string $path
     *
     * @return array
     */
    protected function ftpRawlist($options, $path)
    {
        $connection = $this->getConnection();

        if ($this->isPureFtpd) {
            $path = str_replace(' ', '\ ', $path);
        }
        return ftp_rawlist($connection, $options . ' ' . $path);
    }
}
PK       ! ,A  A  +  flysystem/src/Adapter/CanOverwriteFiles.phpnu         <?php


namespace League\Flysystem\Adapter;

/**
 * Adapters that implement this interface let the Filesystem know that it files can be overwritten using the write
 * functions and don't need the update function to be called. This can help improve performance when asserts are disabled.
 */
interface CanOverwriteFiles {}PK       !  e@/  /    flysystem/src/Adapter/Local.phpnu         <?php

namespace League\Flysystem\Adapter;

use DirectoryIterator;
use FilesystemIterator;
use finfo as Finfo;
use League\Flysystem\AdapterInterface;
use League\Flysystem\Config;
use League\Flysystem\Exception;
use League\Flysystem\NotSupportedException;
use League\Flysystem\UnreadableFileException;
use League\Flysystem\Util;
use LogicException;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use SplFileInfo;

class Local extends AbstractAdapter
{
    /**
     * @var int
     */
    const SKIP_LINKS = 0001;

    /**
     * @var int
     */
    const DISALLOW_LINKS = 0002;

    /**
     * @var array
     */
    protected static $permissions = [
        'file' => [
            'public' => 0644,
            'private' => 0600,
        ],
        'dir' => [
            'public' => 0755,
            'private' => 0700,
        ]
    ];

    /**
     * @var string
     */
    protected $pathSeparator = DIRECTORY_SEPARATOR;

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

    /**
     * @var int
     */
    protected $writeFlags;
    /**
     * @var int
     */
    private $linkHandling;

    /**
     * Constructor.
     *
     * @param string $root
     * @param int    $writeFlags
     * @param int    $linkHandling
     * @param array  $permissions
     *
     * @throws LogicException
     */
    public function __construct($root, $writeFlags = LOCK_EX, $linkHandling = self::DISALLOW_LINKS, array $permissions = [])
    {
        $root = is_link($root) ? realpath($root) : $root;
        $this->permissionMap = array_replace_recursive(static::$permissions, $permissions);
        $this->ensureDirectory($root);

        if ( ! is_dir($root) || ! is_readable($root)) {
            throw new LogicException('The root path ' . $root . ' is not readable.');
        }

        $this->setPathPrefix($root);
        $this->writeFlags = $writeFlags;
        $this->linkHandling = $linkHandling;
    }

    /**
     * Ensure the root directory exists.
     *
     * @param string $root root directory path
     *
     * @return void
     *
     * @throws Exception in case the root directory can not be created
     */
    protected function ensureDirectory($root)
    {
        if ( ! is_dir($root)) {
            $umask = umask(0);
            @mkdir($root, $this->permissionMap['dir']['public'], true);
            umask($umask);

            if ( ! is_dir($root)) {
                throw new Exception(sprintf('Impossible to create the root directory "%s".', $root));
            }
        }
    }

    /**
     * @inheritdoc
     */
    public function has($path)
    {
        $location = $this->applyPathPrefix($path);

        return file_exists($location);
    }

    /**
     * @inheritdoc
     */
    public function write($path, $contents, Config $config)
    {
        $location = $this->applyPathPrefix($path);
        $this->ensureDirectory(dirname($location));

        if (($size = file_put_contents($location, $contents, $this->writeFlags)) === false) {
            return false;
        }

        $type = 'file';
        $result = compact('contents', 'type', 'size', 'path');

        if ($visibility = $config->get('visibility')) {
            $result['visibility'] = $visibility;
            $this->setVisibility($path, $visibility);
        }

        return $result;
    }

    /**
     * @inheritdoc
     */
    public function writeStream($path, $resource, Config $config)
    {
        $location = $this->applyPathPrefix($path);
        $this->ensureDirectory(dirname($location));
        $stream = fopen($location, 'w+b');

        if ( ! $stream) {
            return false;
        }

        stream_copy_to_stream($resource, $stream);

        if ( ! fclose($stream)) {
            return false;
        }

        if ($visibility = $config->get('visibility')) {
            $this->setVisibility($path, $visibility);
        }

        $type = 'file';

        return compact('type', 'path', 'visibility');
    }

    /**
     * @inheritdoc
     */
    public function readStream($path)
    {
        $location = $this->applyPathPrefix($path);
        $stream = fopen($location, 'rb');

        return ['type' => 'file', 'path' => $path, 'stream' => $stream];
    }

    /**
     * @inheritdoc
     */
    public function updateStream($path, $resource, Config $config)
    {
        return $this->writeStream($path, $resource, $config);
    }

    /**
     * @inheritdoc
     */
    public function update($path, $contents, Config $config)
    {
        $location = $this->applyPathPrefix($path);
        $mimetype = Util::guessMimeType($path, $contents);
        $size = file_put_contents($location, $contents, $this->writeFlags);

        if ($size === false) {
            return false;
        }

        $type = 'file';

        return compact('type', 'path', 'size', 'contents', 'mimetype');
    }

    /**
     * @inheritdoc
     */
    public function read($path)
    {
        $location = $this->applyPathPrefix($path);
        $contents = file_get_contents($location);

        if ($contents === false) {
            return false;
        }

        return ['type' => 'file', 'path' => $path, 'contents' => $contents];
    }

    /**
     * @inheritdoc
     */
    public function rename($path, $newpath)
    {
        $location = $this->applyPathPrefix($path);
        $destination = $this->applyPathPrefix($newpath);
        $parentDirectory = $this->applyPathPrefix(Util::dirname($newpath));
        $this->ensureDirectory($parentDirectory);

        return rename($location, $destination);
    }

    /**
     * @inheritdoc
     */
    public function copy($path, $newpath)
    {
        $location = $this->applyPathPrefix($path);
        $destination = $this->applyPathPrefix($newpath);
        $this->ensureDirectory(dirname($destination));

        return copy($location, $destination);
    }

    /**
     * @inheritdoc
     */
    public function delete($path)
    {
        $location = $this->applyPathPrefix($path);

        return unlink($location);
    }

    /**
     * @inheritdoc
     */
    public function listContents($directory = '', $recursive = false)
    {
        $result = [];
        $location = $this->applyPathPrefix($directory);

        if ( ! is_dir($location)) {
            return [];
        }

        $iterator = $recursive ? $this->getRecursiveDirectoryIterator($location) : $this->getDirectoryIterator($location);

        foreach ($iterator as $file) {
            $path = $this->getFilePath($file);

            if (preg_match('#(^|/|\\\\)\.{1,2}$#', $path)) {
                continue;
            }

            $result[] = $this->normalizeFileInfo($file);
        }

        return array_filter($result);
    }

    /**
     * @inheritdoc
     */
    public function getMetadata($path)
    {
        $location = $this->applyPathPrefix($path);
        $info = new SplFileInfo($location);

        return $this->normalizeFileInfo($info);
    }

    /**
     * @inheritdoc
     */
    public function getSize($path)
    {
        return $this->getMetadata($path);
    }

    /**
     * @inheritdoc
     */
    public function getMimetype($path)
    {
        $location = $this->applyPathPrefix($path);
        $finfo = new Finfo(FILEINFO_MIME_TYPE);
        $mimetype = $finfo->file($location);

        if (in_array($mimetype, ['application/octet-stream', 'inode/x-empty'])) {
            $mimetype = Util\MimeType::detectByFilename($location);
        }

        return ['path' => $path, 'type' => 'file', 'mimetype' => $mimetype];
    }

    /**
     * @inheritdoc
     */
    public function getTimestamp($path)
    {
        return $this->getMetadata($path);
    }

    /**
     * @inheritdoc
     */
    public function getVisibility($path)
    {
        $location = $this->applyPathPrefix($path);
        clearstatcache(false, $location);
        $permissions = octdec(substr(sprintf('%o', fileperms($location)), -4));
        $visibility = $permissions & 0044 ? AdapterInterface::VISIBILITY_PUBLIC : AdapterInterface::VISIBILITY_PRIVATE;

        return compact('path', 'visibility');
    }

    /**
     * @inheritdoc
     */
    public function setVisibility($path, $visibility)
    {
        $location = $this->applyPathPrefix($path);
        $type = is_dir($location) ? 'dir' : 'file';
        $success = chmod($location, $this->permissionMap[$type][$visibility]);

        if ($success === false) {
            return false;
        }

        return compact('path', 'visibility');
    }

    /**
     * @inheritdoc
     */
    public function createDir($dirname, Config $config)
    {
        $location = $this->applyPathPrefix($dirname);
        $umask = umask(0);
        $visibility = $config->get('visibility', 'public');

        if ( ! is_dir($location) && ! mkdir($location, $this->permissionMap['dir'][$visibility], true)) {
            $return = false;
        } else {
            $return = ['path' => $dirname, 'type' => 'dir'];
        }

        umask($umask);

        return $return;
    }

    /**
     * @inheritdoc
     */
    public function deleteDir($dirname)
    {
        $location = $this->applyPathPrefix($dirname);

        if ( ! is_dir($location)) {
            return false;
        }

        $contents = $this->getRecursiveDirectoryIterator($location, RecursiveIteratorIterator::CHILD_FIRST);

        /** @var SplFileInfo $file */
        foreach ($contents as $file) {
            $this->guardAgainstUnreadableFileInfo($file);
            $this->deleteFileInfoObject($file);
        }

        return rmdir($location);
    }

    /**
     * @param SplFileInfo $file
     */
    protected function deleteFileInfoObject(SplFileInfo $file)
    {
        switch ($file->getType()) {
            case 'dir':
                rmdir($file->getRealPath());
                break;
            case 'link':
                unlink($file->getPathname());
                break;
            default:
                unlink($file->getRealPath());
        }
    }

    /**
     * Normalize the file info.
     *
     * @param SplFileInfo $file
     *
     * @return array|void
     *
     * @throws NotSupportedException
     */
    protected function normalizeFileInfo(SplFileInfo $file)
    {
        if ( ! $file->isLink()) {
            return $this->mapFileInfo($file);
        }

        if ($this->linkHandling & self::DISALLOW_LINKS) {
            throw NotSupportedException::forLink($file);
        }
    }

    /**
     * Get the normalized path from a SplFileInfo object.
     *
     * @param SplFileInfo $file
     *
     * @return string
     */
    protected function getFilePath(SplFileInfo $file)
    {
        $location = $file->getPathname();
        $path = $this->removePathPrefix($location);

        return trim(str_replace('\\', '/', $path), '/');
    }

    /**
     * @param string $path
     * @param int    $mode
     *
     * @return RecursiveIteratorIterator
     */
    protected function getRecursiveDirectoryIterator($path, $mode = RecursiveIteratorIterator::SELF_FIRST)
    {
        return new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($path, FilesystemIterator::SKIP_DOTS),
            $mode
        );
    }

    /**
     * @param string $path
     *
     * @return DirectoryIterator
     */
    protected function getDirectoryIterator($path)
    {
        $iterator = new DirectoryIterator($path);

        return $iterator;
    }

    /**
     * @param SplFileInfo $file
     *
     * @return array
     */
    protected function mapFileInfo(SplFileInfo $file)
    {
        $normalized = [
            'type' => $file->getType(),
            'path' => $this->getFilePath($file),
        ];

        $normalized['timestamp'] = $file->getMTime();

        if ($normalized['type'] === 'file') {
            $normalized['size'] = $file->getSize();
        }

        return $normalized;
    }

    /**
     * @param SplFileInfo $file
     *
     * @throws UnreadableFileException
     */
    protected function guardAgainstUnreadableFileInfo(SplFileInfo $file)
    {
        if ( ! $file->isReadable()) {
            throw UnreadableFileException::forFileInfo($file);
        }
    }
}
PK       ! ~   ~   %  flysystem/src/Adapter/SynologyFtp.phpnu         <?php

namespace League\Flysystem\Adapter;

class SynologyFtp extends Ftpd
{
    // This class merely exists because of BC.
}
PK       !  AD%  %  )  flysystem/src/Adapter/AbstractAdapter.phpnu         <?php

namespace League\Flysystem\Adapter;

use League\Flysystem\AdapterInterface;

abstract class AbstractAdapter implements AdapterInterface
{
    /**
     * @var string path prefix
     */
    protected $pathPrefix;

    /**
     * @var string
     */
    protected $pathSeparator = '/';

    /**
     * Set the path prefix.
     *
     * @param string $prefix
     *
     * @return void
     */
    public function setPathPrefix($prefix)
    {
        $prefix = (string) $prefix;

        if ($prefix === '') {
            $this->pathPrefix = null;
            return;
        }

        $this->pathPrefix = rtrim($prefix, '\\/') . $this->pathSeparator;
    }

    /**
     * Get the path prefix.
     *
     * @return string path prefix
     */
    public function getPathPrefix()
    {
        return $this->pathPrefix;
    }

    /**
     * Prefix a path.
     *
     * @param string $path
     *
     * @return string prefixed path
     */
    public function applyPathPrefix($path)
    {
        return $this->getPathPrefix() . ltrim($path, '\\/');
    }

    /**
     * Remove a path prefix.
     *
     * @param string $path
     *
     * @return string path without the prefix
     */
    public function removePathPrefix($path)
    {
        return substr($path, strlen($this->getPathPrefix()));
    }
}
PK       ! mh3  3  ,  flysystem/src/Adapter/AbstractFtpAdapter.phpnu         <?php

namespace League\Flysystem\Adapter;

use DateTime;
use League\Flysystem\AdapterInterface;
use League\Flysystem\Config;
use League\Flysystem\NotSupportedException;
use League\Flysystem\SafeStorage;
use RuntimeException;

abstract class AbstractFtpAdapter extends AbstractAdapter
{
    /**
     * @var mixed
     */
    protected $connection;

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

    /**
     * @var int
     */
    protected $port = 21;

    /**
     * @var bool
     */
    protected $ssl = false;

    /**
     * @var int
     */
    protected $timeout = 90;

    /**
     * @var bool
     */
    protected $passive = true;

    /**
     * @var string
     */
    protected $separator = '/';

    /**
     * @var string|null
     */
    protected $root;

    /**
     * @var int
     */
    protected $permPublic = 0744;

    /**
     * @var int
     */
    protected $permPrivate = 0700;

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

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

    /**
     * @var bool
     */
    protected $alternativeRecursion = false;

    /**
     * @var SafeStorage
     */
    protected $safeStorage;

    /**
     * Constructor.
     *
     * @param array $config
     */
    public function __construct(array $config)
    {
        $this->safeStorage = new SafeStorage();
        $this->setConfig($config);
    }

    /**
     * Set the config.
     *
     * @param array $config
     *
     * @return $this
     */
    public function setConfig(array $config)
    {
        foreach ($this->configurable as $setting) {
            if ( ! isset($config[$setting])) {
                continue;
            }

            $method = 'set' . ucfirst($setting);

            if (method_exists($this, $method)) {
                $this->$method($config[$setting]);
            }
        }

        return $this;
    }

    /**
     * Returns the host.
     *
     * @return string
     */
    public function getHost()
    {
        return $this->host;
    }

    /**
     * Set the host.
     *
     * @param string $host
     *
     * @return $this
     */
    public function setHost($host)
    {
        $this->host = $host;

        return $this;
    }

    /**
     * Set the public permission value.
     *
     * @param int $permPublic
     *
     * @return $this
     */
    public function setPermPublic($permPublic)
    {
        $this->permPublic = $permPublic;

        return $this;
    }

    /**
     * Set the private permission value.
     *
     * @param int $permPrivate
     *
     * @return $this
     */
    public function setPermPrivate($permPrivate)
    {
        $this->permPrivate = $permPrivate;

        return $this;
    }

    /**
     * Returns the ftp port.
     *
     * @return int
     */
    public function getPort()
    {
        return $this->port;
    }

    /**
     * Returns the root folder to work from.
     *
     * @return string
     */
    public function getRoot()
    {
        return $this->root;
    }

    /**
     * Set the ftp port.
     *
     * @param int|string $port
     *
     * @return $this
     */
    public function setPort($port)
    {
        $this->port = (int) $port;

        return $this;
    }

    /**
     * Set the root folder to work from.
     *
     * @param string $root
     *
     * @return $this
     */
    public function setRoot($root)
    {
        $this->root = rtrim($root, '\\/') . $this->separator;

        return $this;
    }

    /**
     * Returns the ftp username.
     *
     * @return string username
     */
    public function getUsername()
    {
        $username = $this->safeStorage->retrieveSafely('username');

        return $username !== null ? $username : 'anonymous';
    }

    /**
     * Set ftp username.
     *
     * @param string $username
     *
     * @return $this
     */
    public function setUsername($username)
    {
        $this->safeStorage->storeSafely('username', $username);

        return $this;
    }

    /**
     * Returns the password.
     *
     * @return string password
     */
    public function getPassword()
    {
        return $this->safeStorage->retrieveSafely('password');
    }

    /**
     * Set the ftp password.
     *
     * @param string $password
     *
     * @return $this
     */
    public function setPassword($password)
    {
        $this->safeStorage->storeSafely('password', $password);

        return $this;
    }

    /**
     * Returns the amount of seconds before the connection will timeout.
     *
     * @return int
     */
    public function getTimeout()
    {
        return $this->timeout;
    }

    /**
     * Set the amount of seconds before the connection should timeout.
     *
     * @param int $timeout
     *
     * @return $this
     */
    public function setTimeout($timeout)
    {
        $this->timeout = (int) $timeout;

        return $this;
    }

    /**
     * Return the FTP system type.
     *
     * @return string
     */
    public function getSystemType()
    {
        return $this->systemType;
    }

    /**
     * Set the FTP system type (windows or unix).
     *
     * @param string $systemType
     *
     * @return $this
     */
    public function setSystemType($systemType)
    {
        $this->systemType = strtolower($systemType);

        return $this;
    }

    /**
     * @inheritdoc
     */
    public function listContents($directory = '', $recursive = false)
    {
        return $this->listDirectoryContents($directory, $recursive);
    }

    abstract protected function listDirectoryContents($directory, $recursive = false);

    /**
     * Normalize a directory listing.
     *
     * @param array  $listing
     * @param string $prefix
     *
     * @return array directory listing
     */
    protected function normalizeListing(array $listing, $prefix = '')
    {
        $base = $prefix;
        $result = [];
        $listing = $this->removeDotDirectories($listing);

        while ($item = array_shift($listing)) {
            if (preg_match('#^.*:$#', $item)) {
                $base = trim($item, ':');
                continue;
            }

            $result[] = $this->normalizeObject($item, $base);
        }

        return $this->sortListing($result);
    }

    /**
     * Sort a directory listing.
     *
     * @param array $result
     *
     * @return array sorted listing
     */
    protected function sortListing(array $result)
    {
        $compare = function ($one, $two) {
            return strnatcmp($one['path'], $two['path']);
        };

        usort($result, $compare);

        return $result;
    }

    /**
     * Normalize a file entry.
     *
     * @param string $item
     * @param string $base
     *
     * @return array normalized file array
     *
     * @throws NotSupportedException
     */
    protected function normalizeObject($item, $base)
    {
        $systemType = $this->systemType ?: $this->detectSystemType($item);

        if ($systemType === 'unix') {
            return $this->normalizeUnixObject($item, $base);
        } elseif ($systemType === 'windows') {
            return $this->normalizeWindowsObject($item, $base);
        }

        throw NotSupportedException::forFtpSystemType($systemType);
    }

    /**
     * Normalize a Unix file entry.
     *
     * @param string $item
     * @param string $base
     *
     * @return array normalized file array
     */
    protected function normalizeUnixObject($item, $base)
    {
        $item = preg_replace('#\s+#', ' ', trim($item), 7);

        if (count(explode(' ', $item, 9)) !== 9) {
            throw new RuntimeException("Metadata can't be parsed from item '$item' , not enough parts.");
        }

        list($permissions, /* $number */, /* $owner */, /* $group */, $size, /* $month */, /* $day */, /* $time*/, $name) = explode(' ', $item, 9);
        $type = $this->detectType($permissions);
        $path = $base === '' ? $name : $base . $this->separator . $name;

        if ($type === 'dir') {
            return compact('type', 'path');
        }

        $permissions = $this->normalizePermissions($permissions);
        $visibility = $permissions & 0044 ? AdapterInterface::VISIBILITY_PUBLIC : AdapterInterface::VISIBILITY_PRIVATE;
        $size = (int) $size;

        return compact('type', 'path', 'visibility', 'size');
    }

    /**
     * Normalize a Windows/DOS file entry.
     *
     * @param string $item
     * @param string $base
     *
     * @return array normalized file array
     */
    protected function normalizeWindowsObject($item, $base)
    {
        $item = preg_replace('#\s+#', ' ', trim($item), 3);

        if (count(explode(' ', $item, 4)) !== 4) {
            throw new RuntimeException("Metadata can't be parsed from item '$item' , not enough parts.");
        }

        list($date, $time, $size, $name) = explode(' ', $item, 4);
        $path = $base === '' ? $name : $base . $this->separator . $name;

        // Check for the correct date/time format
        $format = strlen($date) === 8 ? 'm-d-yH:iA' : 'Y-m-dH:i';
        $dt = DateTime::createFromFormat($format, $date . $time);
        $timestamp = $dt ? $dt->getTimestamp() : (int) strtotime("$date $time");

        if ($size === '<DIR>') {
            $type = 'dir';

            return compact('type', 'path', 'timestamp');
        }

        $type = 'file';
        $visibility = AdapterInterface::VISIBILITY_PUBLIC;
        $size = (int) $size;

        return compact('type', 'path', 'visibility', 'size', 'timestamp');
    }

    /**
     * Get the system type from a listing item.
     *
     * @param string $item
     *
     * @return string the system type
     */
    protected function detectSystemType($item)
    {
        return preg_match('/^[0-9]{2,4}-[0-9]{2}-[0-9]{2}/', $item) ? 'windows' : 'unix';
    }

    /**
     * Get the file type from the permissions.
     *
     * @param string $permissions
     *
     * @return string file type
     */
    protected function detectType($permissions)
    {
        return substr($permissions, 0, 1) === 'd' ? 'dir' : 'file';
    }

    /**
     * Normalize a permissions string.
     *
     * @param string $permissions
     *
     * @return int
     */
    protected function normalizePermissions($permissions)
    {
        // remove the type identifier
        $permissions = substr($permissions, 1);

        // map the string rights to the numeric counterparts
        $map = ['-' => '0', 'r' => '4', 'w' => '2', 'x' => '1'];
        $permissions = strtr($permissions, $map);

        // split up the permission groups
        $parts = str_split($permissions, 3);

        // convert the groups
        $mapper = function ($part) {
            return array_sum(str_split($part));
        };

        // converts to decimal number
        return octdec(implode('', array_map($mapper, $parts)));
    }

    /**
     * Filter out dot-directories.
     *
     * @param array $list
     *
     * @return array
     */
    public function removeDotDirectories(array $list)
    {
        $filter = function ($line) {
            if ( $line !== '' && ! preg_match('#.* \.(\.)?$|^total#', $line)) {
                return true;
            }

            return false;
        };

        return array_filter($list, $filter);
    }

    /**
     * @inheritdoc
     */
    public function has($path)
    {
        return $this->getMetadata($path);
    }

    /**
     * @inheritdoc
     */
    public function getSize($path)
    {
        return $this->getMetadata($path);
    }

    /**
     * @inheritdoc
     */
    public function getVisibility($path)
    {
        return $this->getMetadata($path);
    }

    /**
     * Ensure a directory exists.
     *
     * @param string $dirname
     */
    public function ensureDirectory($dirname)
    {
        $dirname = (string) $dirname;

        if ($dirname !== '' && ! $this->has($dirname)) {
            $this->createDir($dirname, new Config());
        }
    }

    /**
     * @return mixed
     */
    public function getConnection()
    {
        $tries = 0;

        while ( ! $this->isConnected() && $tries < 3) {
            $tries++;
            $this->disconnect();
            $this->connect();
        }

        return $this->connection;
    }

    /**
     * Get the public permission value.
     *
     * @return int
     */
    public function getPermPublic()
    {
        return $this->permPublic;
    }

    /**
     * Get the private permission value.
     *
     * @return int
     */
    public function getPermPrivate()
    {
        return $this->permPrivate;
    }

    /**
     * Disconnect on destruction.
     */
    public function __destruct()
    {
        $this->disconnect();
    }

    /**
     * Establish a connection.
     */
    abstract public function connect();

    /**
     * Close the connection.
     */
    abstract public function disconnect();

    /**
     * Check if a connection is active.
     *
     * @return bool
     */
    abstract public function isConnected();
}
PK       ! B	  	  %  flysystem/src/Adapter/NullAdapter.phpnu         <?php

namespace League\Flysystem\Adapter;

use League\Flysystem\Adapter\Polyfill\StreamedCopyTrait;
use League\Flysystem\Adapter\Polyfill\StreamedTrait;
use League\Flysystem\Config;

class NullAdapter extends AbstractAdapter
{
    use StreamedTrait;
    use StreamedCopyTrait;

    /**
     * Check whether a file is present.
     *
     * @param string $path
     *
     * @return bool
     */
    public function has($path)
    {
        return false;
    }

    /**
     * @inheritdoc
     */
    public function write($path, $contents, Config $config)
    {
        $type = 'file';
        $result = compact('contents', 'type', 'path');

        if ($visibility = $config->get('visibility')) {
            $result['visibility'] = $visibility;
        }

        return $result;
    }

    /**
     * @inheritdoc
     */
    public function update($path, $contents, Config $config)
    {
        return false;
    }

    /**
     * @inheritdoc
     */
    public function read($path)
    {
        return false;
    }

    /**
     * @inheritdoc
     */
    public function rename($path, $newpath)
    {
        return false;
    }

    /**
     * @inheritdoc
     */
    public function delete($path)
    {
        return false;
    }

    /**
     * @inheritdoc
     */
    public function listContents($directory = '', $recursive = false)
    {
        return [];
    }

    /**
     * @inheritdoc
     */
    public function getMetadata($path)
    {
        return false;
    }

    /**
     * @inheritdoc
     */
    public function getSize($path)
    {
        return false;
    }

    /**
     * @inheritdoc
     */
    public function getMimetype($path)
    {
        return false;
    }

    /**
     * @inheritdoc
     */
    public function getTimestamp($path)
    {
        return false;
    }

    /**
     * @inheritdoc
     */
    public function getVisibility($path)
    {
        return false;
    }

    /**
     * @inheritdoc
     */
    public function setVisibility($path, $visibility)
    {
        return compact('visibility');
    }

    /**
     * @inheritdoc
     */
    public function createDir($dirname, Config $config)
    {
        return ['path' => $dirname, 'type' => 'dir'];
    }

    /**
     * @inheritdoc
     */
    public function deleteDir($dirname)
    {
        return false;
    }
}
PK       ! R   R     flysystem/src/Exception.phpnu         <?php

namespace League\Flysystem;

class Exception extends \Exception
{
    //
}
PK       ! e0      flysystem/src/SafeStorage.phpnu         <?php

namespace League\Flysystem;

final class SafeStorage
{
    /**
     * @var string
     */
    private $hash;

    /**
     * @var array
     */
    protected static $safeStorage = [];

    public function __construct()
    {
        $this->hash = spl_object_hash($this);
        static::$safeStorage[$this->hash] = [];
    }

    public function storeSafely($key, $value)
    {
        static::$safeStorage[$this->hash][$key] = $value;
    }

    public function retrieveSafely($key)
    {
        if (array_key_exists($key, static::$safeStorage[$this->hash])) {
            return static::$safeStorage[$this->hash][$key];
        }
    }

    public function __destruct()
    {
        unset(static::$safeStorage[$this->hash]);
    }
}
PK       ! '7s  s    flysystem/src/File.phpnu         <?php

namespace League\Flysystem;

class File extends Handler
{
    /**
     * Check whether the file exists.
     *
     * @return bool
     */
    public function exists()
    {
        return $this->filesystem->has($this->path);
    }

    /**
     * Read the file.
     *
     * @return string file contents
     */
    public function read()
    {
        return $this->filesystem->read($this->path);
    }

    /**
     * Read the file as a stream.
     *
     * @return resource file stream
     */
    public function readStream()
    {
        return $this->filesystem->readStream($this->path);
    }

    /**
     * Write the new file.
     *
     * @param string $content
     *
     * @return bool success boolean
     */
    public function write($content)
    {
        return $this->filesystem->write($this->path, $content);
    }

    /**
     * Write the new file using a stream.
     *
     * @param resource $resource
     *
     * @return bool success boolean
     */
    public function writeStream($resource)
    {
        return $this->filesystem->writeStream($this->path, $resource);
    }

    /**
     * Update the file contents.
     *
     * @param string $content
     *
     * @return bool success boolean
     */
    public function update($content)
    {
        return $this->filesystem->update($this->path, $content);
    }

    /**
     * Update the file contents with a stream.
     *
     * @param resource $resource
     *
     * @return bool success boolean
     */
    public function updateStream($resource)
    {
        return $this->filesystem->updateStream($this->path, $resource);
    }

    /**
     * Create the file or update if exists.
     *
     * @param string $content
     *
     * @return bool success boolean
     */
    public function put($content)
    {
        return $this->filesystem->put($this->path, $content);
    }

    /**
     * Create the file or update if exists using a stream.
     *
     * @param resource $resource
     *
     * @return bool success boolean
     */
    public function putStream($resource)
    {
        return $this->filesystem->putStream($this->path, $resource);
    }

    /**
     * Rename the file.
     *
     * @param string $newpath
     *
     * @return bool success boolean
     */
    public function rename($newpath)
    {
        if ($this->filesystem->rename($this->path, $newpath)) {
            $this->path = $newpath;

            return true;
        }

        return false;
    }

    /**
     * Copy the file.
     *
     * @param string $newpath
     *
     * @return File|false new file or false
     */
    public function copy($newpath)
    {
        if ($this->filesystem->copy($this->path, $newpath)) {
            return new File($this->filesystem, $newpath);
        }

        return false;
    }

    /**
     * Get the file's timestamp.
     *
     * @return int unix timestamp
     */
    public function getTimestamp()
    {
        return $this->filesystem->getTimestamp($this->path);
    }

    /**
     * Get the file's mimetype.
     *
     * @return string mimetime
     */
    public function getMimetype()
    {
        return $this->filesystem->getMimetype($this->path);
    }

    /**
     * Get the file's visibility.
     *
     * @return string visibility
     */
    public function getVisibility()
    {
        return $this->filesystem->getVisibility($this->path);
    }

    /**
     * Get the file's metadata.
     *
     * @return array
     */
    public function getMetadata()
    {
        return $this->filesystem->getMetadata($this->path);
    }

    /**
     * Get the file size.
     *
     * @return int file size
     */
    public function getSize()
    {
        return $this->filesystem->getSize($this->path);
    }

    /**
     * Delete the file.
     *
     * @return bool success boolean
     */
    public function delete()
    {
        return $this->filesystem->delete($this->path);
    }
}
PK       ! wx   x   (  flysystem/src/RootViolationException.phpnu         <?php

namespace League\Flysystem;

use LogicException;

class RootViolationException extends LogicException
{
    //
}
PK       ! [!  !    flysystem/src/MountManager.phpnu         <?php

namespace League\Flysystem;

use InvalidArgumentException;
use League\Flysystem\FilesystemNotFoundException;
use League\Flysystem\Plugin\PluggableTrait;
use League\Flysystem\Plugin\PluginNotFoundException;

/**
 * Class MountManager.
 *
 * Proxies methods to Filesystem (@see __call):
 *
 * @method AdapterInterface getAdapter($prefix)
 * @method Config getConfig($prefix)
 * @method bool has($path)
 * @method bool write($path, $contents, array $config = [])
 * @method bool writeStream($path, $resource, array $config = [])
 * @method bool put($path, $contents, $config = [])
 * @method bool putStream($path, $contents, $config = [])
 * @method string readAndDelete($path)
 * @method bool update($path, $contents, $config = [])
 * @method bool updateStream($path, $resource, $config = [])
 * @method string|false read($path)
 * @method resource|false readStream($path)
 * @method bool rename($path, $newpath)
 * @method bool delete($path)
 * @method bool deleteDir($dirname)
 * @method bool createDir($dirname, $config = [])
 * @method array listFiles($directory = '', $recursive = false)
 * @method array listPaths($directory = '', $recursive = false)
 * @method array getWithMetadata($path, array $metadata)
 * @method string|false getMimetype($path)
 * @method string|false getTimestamp($path)
 * @method string|false getVisibility($path)
 * @method int|false getSize($path);
 * @method bool setVisibility($path, $visibility)
 * @method array|false getMetadata($path)
 * @method Handler get($path, Handler $handler = null)
 * @method Filesystem flushCache()
 * @method void assertPresent($path)
 * @method void assertAbsent($path)
 * @method Filesystem addPlugin(PluginInterface $plugin)
 */
class MountManager
{
    use PluggableTrait;

    /**
     * @var FilesystemInterface[]
     */
    protected $filesystems = [];

    /**
     * Constructor.
     *
     * @param FilesystemInterface[] $filesystems [:prefix => Filesystem,]
     *
     * @throws InvalidArgumentException
     */
    public function __construct(array $filesystems = [])
    {
        $this->mountFilesystems($filesystems);
    }

    /**
     * Mount filesystems.
     *
     * @param FilesystemInterface[] $filesystems [:prefix => Filesystem,]
     *
     * @throws InvalidArgumentException
     *
     * @return $this
     */
    public function mountFilesystems(array $filesystems)
    {
        foreach ($filesystems as $prefix => $filesystem) {
            $this->mountFilesystem($prefix, $filesystem);
        }

        return $this;
    }

    /**
     * Mount filesystems.
     *
     * @param string              $prefix
     * @param FilesystemInterface $filesystem
     *
     * @throws InvalidArgumentException
     *
     * @return $this
     */
    public function mountFilesystem($prefix, FilesystemInterface $filesystem)
    {
        if ( ! is_string($prefix)) {
            throw new InvalidArgumentException(__METHOD__ . ' expects argument #1 to be a string.');
        }

        $this->filesystems[$prefix] = $filesystem;

        return $this;
    }

    /**
     * Get the filesystem with the corresponding prefix.
     *
     * @param string $prefix
     *
     * @throws FilesystemNotFoundException
     *
     * @return FilesystemInterface
     */
    public function getFilesystem($prefix)
    {
        if ( ! isset($this->filesystems[$prefix])) {
            throw new FilesystemNotFoundException('No filesystem mounted with prefix ' . $prefix);
        }

        return $this->filesystems[$prefix];
    }

    /**
     * Retrieve the prefix from an arguments array.
     *
     * @param array $arguments
     *
     * @throws InvalidArgumentException
     *
     * @return array [:prefix, :arguments]
     */
    public function filterPrefix(array $arguments)
    {
        if (empty($arguments)) {
            throw new InvalidArgumentException('At least one argument needed');
        }

        $path = array_shift($arguments);

        if ( ! is_string($path)) {
            throw new InvalidArgumentException('First argument should be a string');
        }

        list($prefix, $path) = $this->getPrefixAndPath($path);
        array_unshift($arguments, $path);

        return [$prefix, $arguments];
    }

    /**
     * @param string $directory
     * @param bool   $recursive
     *
     * @throws InvalidArgumentException
     * @throws FilesystemNotFoundException
     *
     * @return array
     */
    public function listContents($directory = '', $recursive = false)
    {
        list($prefix, $directory) = $this->getPrefixAndPath($directory);
        $filesystem = $this->getFilesystem($prefix);
        $result = $filesystem->listContents($directory, $recursive);

        foreach ($result as &$file) {
            $file['filesystem'] = $prefix;
        }

        return $result;
    }

    /**
     * Call forwarder.
     *
     * @param string $method
     * @param array  $arguments
     *
     * @throws InvalidArgumentException
     * @throws FilesystemNotFoundException
     *
     * @return mixed
     */
    public function __call($method, $arguments)
    {
        list($prefix, $arguments) = $this->filterPrefix($arguments);

        return $this->invokePluginOnFilesystem($method, $arguments, $prefix);
    }

    /**
     * @param string $from
     * @param string $to
     * @param array  $config
     *
     * @throws InvalidArgumentException
     * @throws FilesystemNotFoundException
     *
     * @return bool
     */
    public function copy($from, $to, array $config = [])
    {
        list($prefixFrom, $from) = $this->getPrefixAndPath($from);

        $buffer = $this->getFilesystem($prefixFrom)->readStream($from);

        if ($buffer === false) {
            return false;
        }

        list($prefixTo, $to) = $this->getPrefixAndPath($to);

        $result = $this->getFilesystem($prefixTo)->writeStream($to, $buffer, $config);

        if (is_resource($buffer)) {
            fclose($buffer);
        }

        return $result;
    }

    /**
     * List with plugin adapter.
     *
     * @param array  $keys
     * @param string $directory
     * @param bool   $recursive
     *
     * @throws InvalidArgumentException
     * @throws FilesystemNotFoundException
     *
     * @return array
     */
    public function listWith(array $keys = [], $directory = '', $recursive = false)
    {
        list($prefix, $directory) = $this->getPrefixAndPath($directory);
        $arguments = [$keys, $directory, $recursive];

        return $this->invokePluginOnFilesystem('listWith', $arguments, $prefix);
    }

    /**
     * Move a file.
     *
     * @param string $from
     * @param string $to
     * @param array  $config
     *
     * @throws InvalidArgumentException
     * @throws FilesystemNotFoundException
     *
     * @return bool
     */
    public function move($from, $to, array $config = [])
    {
        list($prefixFrom, $pathFrom) = $this->getPrefixAndPath($from);
        list($prefixTo, $pathTo) = $this->getPrefixAndPath($to);

        if ($prefixFrom === $prefixTo) {
            $filesystem = $this->getFilesystem($prefixFrom);
            $renamed = $filesystem->rename($pathFrom, $pathTo);

            if ($renamed && isset($config['visibility'])) {
                return $filesystem->setVisibility($pathTo, $config['visibility']);
            }

            return $renamed;
        }

        $copied = $this->copy($from, $to, $config);

        if ($copied) {
            return $this->delete($from);
        }

        return false;
    }

    /**
     * Invoke a plugin on a filesystem mounted on a given prefix.
     *
     * @param string $method
     * @param array  $arguments
     * @param string $prefix
     *
     * @throws FilesystemNotFoundException
     *
     * @return mixed
     */
    public function invokePluginOnFilesystem($method, $arguments, $prefix)
    {
        $filesystem = $this->getFilesystem($prefix);

        try {
            return $this->invokePlugin($method, $arguments, $filesystem);
        } catch (PluginNotFoundException $e) {
            // Let it pass, it's ok, don't panic.
        }

        $callback = [$filesystem, $method];

        return call_user_func_array($callback, $arguments);
    }

    /**
     * @param string $path
     *
     * @throws InvalidArgumentException
     *
     * @return string[] [:prefix, :path]
     */
    protected function getPrefixAndPath($path)
    {
        if (strpos($path, '://') < 1) {
            throw new InvalidArgumentException('No prefix detected in path: ' . $path);
        }

        return explode('://', $path, 2);
    }
}
PK       ! *3    '  flysystem/src/FileNotFoundException.phpnu         <?php

namespace League\Flysystem;

use Exception as BaseException;

class FileNotFoundException extends Exception
{
    /**
     * @var string
     */
    protected $path;

    /**
     * Constructor.
     *
     * @param string     $path
     * @param int        $code
     * @param \Exception $previous
     */
    public function __construct($path, $code = 0, BaseException $previous = null)
    {
        $this->path = $path;

        parent::__construct('File not found at path: ' . $this->getPath(), $code, $previous);
    }

    /**
     * Get the path which was not found.
     *
     * @return string
     */
    public function getPath()
    {
        return $this->path;
    }
}
PK       ! p}R 
   
  "  flysystem/src/AdapterInterface.phpnu         <?php

namespace League\Flysystem;

interface AdapterInterface extends ReadInterface
{
    /**
     * @const  VISIBILITY_PUBLIC  public visibility
     */
    const VISIBILITY_PUBLIC = 'public';

    /**
     * @const  VISIBILITY_PRIVATE  private visibility
     */
    const VISIBILITY_PRIVATE = 'private';

    /**
     * Write a new file.
     *
     * @param string $path
     * @param string $contents
     * @param Config $config   Config object
     *
     * @return array|false false on failure file meta data on success
     */
    public function write($path, $contents, Config $config);

    /**
     * Write a new file using a stream.
     *
     * @param string   $path
     * @param resource $resource
     * @param Config   $config   Config object
     *
     * @return array|false false on failure file meta data on success
     */
    public function writeStream($path, $resource, Config $config);

    /**
     * Update a file.
     *
     * @param string $path
     * @param string $contents
     * @param Config $config   Config object
     *
     * @return array|false false on failure file meta data on success
     */
    public function update($path, $contents, Config $config);

    /**
     * Update a file using a stream.
     *
     * @param string   $path
     * @param resource $resource
     * @param Config   $config   Config object
     *
     * @return array|false false on failure file meta data on success
     */
    public function updateStream($path, $resource, Config $config);

    /**
     * Rename a file.
     *
     * @param string $path
     * @param string $newpath
     *
     * @return bool
     */
    public function rename($path, $newpath);

    /**
     * Copy a file.
     *
     * @param string $path
     * @param string $newpath
     *
     * @return bool
     */
    public function copy($path, $newpath);

    /**
     * Delete a file.
     *
     * @param string $path
     *
     * @return bool
     */
    public function delete($path);

    /**
     * Delete a directory.
     *
     * @param string $dirname
     *
     * @return bool
     */
    public function deleteDir($dirname);

    /**
     * Create a directory.
     *
     * @param string $dirname directory name
     * @param Config $config
     *
     * @return array|false
     */
    public function createDir($dirname, Config $config);

    /**
     * Set the visibility for a file.
     *
     * @param string $path
     * @param string $visibility
     *
     * @return array|false file meta data
     */
    public function setVisibility($path, $visibility);
}
PK       ! 	-X  X  !  flysystem/src/PluginInterface.phpnu         <?php

namespace League\Flysystem;

interface PluginInterface
{
    /**
     * Get the method name.
     *
     * @return string
     */
    public function getMethod();

    /**
     * Set the Filesystem object.
     *
     * @param FilesystemInterface $filesystem
     */
    public function setFilesystem(FilesystemInterface $filesystem);
}
PK       ! wdY  Y  )  flysystem/src/UnreadableFileException.phpnu         <?php

namespace League\Flysystem;

use SplFileInfo;

class UnreadableFileException extends Exception
{
    public static function forFileInfo(SplFileInfo $fileInfo)
    {
        return new static(
            sprintf(
                'Unreadable file encountered: %s',
                $fileInfo->getRealPath()
            )
        );
    }
}
PK       ! @      flysystem/docs/caching.mdnu         ---
layout: default
permalink: /caching/
title: Caching
---

# Caching

File system I/O is slow, so Flysystem uses cached file system meta-data to boost performance. When your application needs to scale you can also choose to use a (shared) persistent caching solution for this.
Or enable a per request caching (recommended).

## Installing the adapter cache decorator

~~~bash
composer require league/flysystem-cached-adapter
~~~

This package supplies an Adapter decorator which acts as a caching proxy.

The CachedAdapter (the decorator) caches anything but the file contents. This keeps the cache small enough to be beneficial and covers all the file system inspection operations.

## Memory Caching

The easiest way to boost the performance of Flysystem is to add Memory caching.
This type of caching will cache everything in the lifetime of the current process (cli-job or http-request).
Setting it up is easy:

~~~ php
use League\Flysystem\Filesystem;
use League\Flysystem\Adapter\Local as Adapter;
use League\Flysystem\Cached\CachedAdapter;
use League\Flysystem\Cached\Storage\Memory as CacheStore;

// Create the adapter
$localAdapter = new Local('/path/to/root');

// Create the cache store
$cacheStore = new CacheStore();

// Decorate the adapter
$adapter = new CachedAdapter($localAdapter, $cacheStore);

// And use that to create the file system
$filesystem = new Filesystem($adapter);
~~~

You can now use the file system as you would have before, but caching will be done for you on the fly.

## Persistent Caching

The following examples demonstrate how you can setup persistent meta-data caching:

## Predis Caching Setup

~~~ php
use League\Flysystem\Filesystem;
use League\Flysystem\Adapter\Local as Adapter;
use League\Flysystem\Cached\CachedAdapter;
use League\Flysystem\Cached\Storage\Predis as Cache;

$adapter = new CachedAdapter(new Adapter(__DIR__.'/path/to/root'), new Cache);
$filesystem = new Filesystem($adapter);

// Or supply a client
$client = new Predis\Client;
$adapter = new CachedAdapter(new Adapter(__DIR__.'/path/to/root'), new Cache($client));
$filesystem = new Filesystem($adapter);
~~~

## Memcached Caching Setup

~~~ php
use League\Flysystem\Filesystem;
use League\Flysystem\Adapter\Local as Adapter;
use League\Flysystem\Cached\CachedAdapter;
use League\Flysystem\Cached\Storage\Memcached as Cache;

$memcached = new Memcached;
$memcached->addServer('localhost', 11211);

$adapter = new CachedAdapter(
    new Adapter(__DIR__.'/path/to/root'),
    new Cache($memcached, 'storageKey', 300)
);
$filesystem = new Filesystem($adapter);
// Storage Key and expire time are optional
~~~

## Adapter Caching Setup

~~~ php
use Dropbox\Client;
use League\Flysystem\Filesystem;
use League\Flysystem\Adapter\Dropbox;
use League\Flysystem\Adapter\Local;
use League\Flysystem\Cached\CachedAdapter;
use League\Flysystem\Cached\Storage\Adapter;

$client = new Client('token', 'app');
$dropbox = new Dropbox($client, 'prefix');

$local = new Local('path');
$cache = new Adapter($local, 'file', 300);
$adapter = new CachedAdapter($dropbox, $cache);
$filesystem = new Filesystem($adapter);
~~~

## Stash Caching Setup

~~~ php
use Stash\Pool;
use League\Flysystem\Adapter\Local as Adapter;
use League\Flysystem\Cached\CachedAdapter;
use League\Flysystem\Cached\Storage\Stash as Cache;

$pool = new Pool();
// you can optionally pass a driver (recommended, default: in-memory driver)

$cache = new Cache($pool, 'storageKey', 300);
// Storage Key and expire time are optional

$adapter = new CachedAdapter(new Adapter(__DIR__.'/path/to/root'), $cache);
$filesystem = new Filesystem($adapter);
~~~

For list of drivers and configuration options check their [documentation](http://www.stashphp.com/Drivers.html).
PK       ! 53w  w    flysystem/docs/installation.mdnu         ---
layout: default
permalink: /installation/
title: Installation
---

# Installation

Through Composer, obviously:

~~~ bash
composer require league/flysystem
~~~

You can also use Flysystem without using Composer by registering an autoloader function:

~~~ php
spl_autoload_register(function($class) {
    $prefix = 'League\\Flysystem\\';

    if ( ! substr($class, 0, 17) === $prefix) {
        return;
    }

    $class = substr($class, strlen($prefix));
    $location = __DIR__ . 'path/to/flysystem/src/' . str_replace('\\', '/', $class) . '.php';

    if (is_file($location)) {
        require_once($location);
    }
});
~~~
PK       ! {OG    "  flysystem/docs/upgrade-to-1.0.0.mdnu         ---
layout: default
permalink: /upgrade-to-1.0.0/
title: Upgrade to 1.0.0
---

# Upgrade to 1.0.0

While version 1.0.0 is largely backwards compatible from earlier versions in every 
day usage, some parts require a different boostrapping.

## Relocated Adapters

In order to have better dependency management, and to remove some of the
version contstraints, some of the adapters have been moved out of the main
repository. These adapters are:

* [AwsS3: AWS SDK V2 Adapter](/adapter/aws-s3-v2/)
* [AwsS3V3: AWS SDK V3 Adapter](/adapter/aws-s3-v3/)
* [Dropbox](/adapter/dropbox/)
* [Rackspace](/adapter/rackspace/)
* [GridFS](/adapter/gridfs/)
* [Sftp](/adapter/sftp/)
* [WebDAV](/adapter/webdav/)
* [ZipArchive](/adapter/zip-archive/)

##  Caching

Caching has been removed from the main Filesystem class and is now implemented
as an adapter decorator.

### Version 0.x

~~~ php
$filesystem = new Filesystem($adapter, $cacheAdapter);
~~~

### Version 1.0.0

Install the required adapter decorator:

~~~ bash
composer require league/flysystem-cached-adapter
~~~

And convert the bootstrapping to:

~~~ php
use League\Flysystem\Adapter\Local;
use League\Flysystem\Cached\CachedAdapter;

$decoratedAdapter = new CachedAdapter($adapter, $cacheAdapter);
$filesystem = new Filesystem($decoratedAdapter);
~~~

## Helper Methods

In order to clean up the Filsystem class, some helper functions have been moved to plugins.

* ListWith
* ListPaths
* ListFiles
* GetWithMetadata
* EmptyDir (new)


PK       ! l      flysystem/docs/integrations.mdnu         ---
layout: default
permalink: /integrations/
title: Integrations
---

# Integrations

Want to get started quickly? Check out some of these bridging packages:

* [Laravel integration](https://github.com/GrahamCampbell/Laravel-Flysystem)
* [Symfony integration](https://github.com/1up-lab/OneupFlysystemBundle)
* [Zend Framework integration](https://github.com/bushbaby/BsbFlysystem)
* [CakePHP integration](https://github.com/WyriHaximus/FlyPie)
* [Silex integration](https://github.com/WyriHaximus/SliFly)
* [Yii 2 integration](https://github.com/creocoder/yii2-flysystem)
* [Backup manager](https://github.com/heybigname/backup-manager)

### Other integrations

Laravel 5 will ship with Flysystem as the underlying engine for local and remote file handling.
PK       ! ݢ      flysystem/docs/plugins.mdnu         ---
layout: default
permalink: /plugins/
title: Plugins
---

# Plugins
Need a feature which is not included in Flysystem's bag of tricks? Write a plugin!

~~~ php
use League\Flysystem\FilesystemInterface;
use League\Flysystem\PluginInterface;

class MaximusAwesomeness implements PluginInterface
{
    protected $filesystem;

    public function setFilesystem(FilesystemInterface $filesystem)
    {
        $this->filesystem = $filesystem;
    }

    public function getMethod()
    {
        return 'getDown';
    }

    public function handle($path = null)
    {
        $contents = $this->filesystem->read($path);

        return sha1($contents);
    }
}
~~~

Now we're ready to use the plugin

~~~ php
use League\Flysystem\Filesystem;
use League\Flysystem\Adapter;

$filesystem = new Filesystem(new Adapter\Local(__DIR__.'/path/to/files/'));
$filesystem->addPlugin(new MaximusAwesomeness);
$sha1 = $filesystem->getDown('path/to/file');
~~~
PK       ! g        flysystem/docs/CNAMEnu         flysystem.thephpleague.comPK       ! Ι      flysystem/docs/index.mdnu         ---
layout: default
permalink: /
title: Introduction
---

# Introduction

<span style="float: left; margin: 0 10px 0 0;">
[![SensioLabsInsight](//insight.sensiolabs.com/projects/9820f1af-2fd0-4ab6-b42a-03e0c821e0af/big.png)](//insight.sensiolabs.com/projects/9820f1af-2fd0-4ab6-b42a-03e0c821e0af)
</span>

[![Author](//img.shields.io/badge/author-@frankdejonge-blue.svg?style=flat-square)](//twitter.com/frankdejonge)
[![Source Code](//img.shields.io/badge/source-thephpleague/flysystem-blue.svg?style=flat-square)](//github.com/thephpleague/flysystem)
[![Latest Version](//img.shields.io/github/tag/thephpleague/flysystem.svg?style=flat-square)](//github.com/thephpleague/flysystem/releases)
[![Software License](//img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](//github.com/thephpleague/flysystem/blob/master/LICENSE)
[![Build Status](//img.shields.io/travis/thephpleague/flysystem/master.svg?style=flat-square)](//travis-ci.org/thephpleague/flysystem)
[![Coverage Status](//img.shields.io/scrutinizer/coverage/g/thephpleague/flysystem.svg?style=flat-square)](//scrutinizer-ci.com/g/thephpleague/flysystem/code-structure)
[![Quality Score](//img.shields.io/scrutinizer/g/thephpleague/flysystem.svg?style=flat-square)](//scrutinizer-ci.com/g/thephpleague/flysystem)
[![Total Downloads](//img.shields.io/packagist/dt/league/flysystem.svg?style=flat-square)](//packagist.org/packages/league/flysystem)
![php 5.5+](//img.shields.io/badge/php-min%205.5-red.svg?style=flat-square)

Flysystem is a filesystem abstraction which allows you to easily swap out a local filesystem for a remote one. Technical debt is reduced as is the chance of vendor lock-in.

## Goals

* Have a generic API for handling common tasks across multiple file storage engines.
* Have consistent output which you can rely on.
* Integrate well with other packages/frameworks.
* Be cacheable.
* Emulate directories in systems that support none, like AwsS3.
* Support third party plugins.
* Make it easy to test your filesystem interactions.
* Support streams for big file handling

## Questions?

Flysystem was created by Frank de Jonge, follow him on Twitter for updates: [@frankdejonge](//twitter.com/frankdejonge).

Please submit issues on [Github](//github.com/thephpleague/flysystem).
PK       ! T      flysystem/docs/api.mdnu         ---
layout: default
permalink: /api/
title: API
---

# API

## General Usage

__Write Files__

~~~ php
$filesystem->write('path/to/file.txt', 'contents');
~~~

__Update Files__

~~~ php
$filesystem->update('path/to/file.txt', 'new contents');
~~~

__Write or Update Files__

~~~ php
$filesystem->put('path/to/file.txt', 'contents');
~~~

__Read Files__

~~~ php
$contents = $filesystem->read('path/to/file.txt');
~~~

__Check if a file exists__

~~~ php
$exists = $filesystem->has('path/to/file.txt');
~~~

__NOTE__: This only has consistent behaviour for files, not directories. Directories
are less important in Flysystem, they're created implicitly and often ignored because
not every adapter (filesystem type) supports directories.

__Delete Files__

~~~ php
$filesystem->delete('path/to/file.txt');
~~~

__Read and Delete__

~~~ php
$contents = $filesystem->readAndDelete('path/to/file.txt');
~~~

__Rename Files__

~~~ php
$filesystem->rename('filename.txt', 'newname.txt');
~~~

__Copy Files__

~~~ php
$filesystem->copy('filename.txt', 'duplicate.txt');
~~~

__Get Mimetypes__

~~~ php
$mimetype = $filesystem->getMimetype('path/to/file.txt');
~~~

__Get Timestamps__

~~~ php
$timestamp = $filesystem->getTimestamp('path/to/file.txt');
~~~

__Get File Sizes__

~~~ php
$size = $filesystem->getSize('path/to/file.txt');
~~~

__Create Directories__

~~~ php
$filesystem->createDir('path/to/nested/directory');
~~~
Directories are also made implicitly when writing to a deeper path

~~~ php
$filesystem->write('path/to/file.txt', 'contents');
~~~

__Delete Directories__

~~~ php
$filesystem->deleteDir('path/to/directory');
~~~
The above method will delete directories recursively

__NOTE__: All paths used by Flysystem API are relative to the adapter root directory.

__Manage Visibility__

Visibility is the abstraction of file permissions across multiple platforms. Visibility can be either public or private.

~~~ php
use League\Flysystem\AdapterInterface;

$filesystem->write('db.backup', $backup, [
    'visibility' => AdapterInterface::VISIBILITY_PRIVATE
]);

// or simply

$filesystem->write('db.backup', $backup, ['visibility' => 'private']);
~~~

You can also change and check visibility of existing files

~~~ php
if ($filesystem->getVisibility('secret.txt') === 'private') {
    $filesystem->setVisibility('secret.txt', 'public');
}
~~~

## Global visibility setting

You can set the visibility as a default, which prevents you from setting it all over the place.

~~~ php
$filesystem = new League\Flysystem\Filesystem($adapter, [
    'visibility' => AdapterInterface::VISIBILITY_PRIVATE
]);
~~~

__List Contents__

~~~ php
$contents = $filemanager->listContents();
~~~

The result of a contents listing is a collection of arrays containing all the metadata the file manager knows at that time. By default you'll receive path info and file type. Additional info could be supplied by default depending on the adapter used.

Example:

~~~ php
foreach ($contents as $object) {
    echo $object['basename'].' is located at'.$object['path'].' and is a '.$object['type'];
}
~~~

By default Flysystem lists the top directory non-recursively. You can supply a directory name and recursive boolean to get more precise results

~~~ php
$contents = $filesystem->listContents('some/dir', true);
~~~

__List paths__

~~~ php
$filesystem->addPlugin(new ListPaths());

$paths = $filesystem->listPaths();

foreach ($paths as $path) {
    echo $path;
}
~~~

__List with ensured presence of specific metadata__

~~~ php
$listing = $filesystem->listWith(['mimetype', 'size', 'timestamp'], 'optional/path/to/dir', true);

foreach ($listing as $object) {
    echo $object['path'].' has mimetype: '.$object['mimetype'];
}
~~~

__Get file into with explicit metadata__

~~~ php
$info = $filesystem->getWithMetadata('path/to/file.txt', ['timestamp', 'mimetype']);
echo $info['mimetype'];
echo $info['timestamp'];
~~~

__NOTE__: This requires the `League\Flysystem\Plugin\GetWithMetadata` plugin.

## Using streams for reads and writes

<p class="message-notice">
Some SDK's close streams after consuming them, therefore, before calling fclose on the resource, check if it's still valid using <code>is_resource</code>.
</p>

~~~ php
$stream = fopen('/path/to/database.backup', 'r+');
$filesystem->writeStream('backups/'.strftime('%G-%m-%d').'.backup', $stream);

// Using write you can also directly set the visibility
$filesystem->writeStream('backups/'.strftime('%G-%m-%d').'.backup', $stream, [
    'visibility' => AdapterInterface::VISIBILITY_PRIVATE
]);

if (is_resource($stream)) {
    fclose($stream);
}

// Or update a file with stream contents
$filesystem->updateStream('backups/'.strftime('%G-%m-%d').'.backup', $stream);

// Retrieve a read-stream
$stream = $filesystem->readStream('something/is/here.ext');
$contents = stream_get_contents($stream);
fclose($stream);

// Create or overwrite using a stream.
$putStream = tmpfile();
fwrite($putStream, $contents);
rewind($putStream);
$filesystem->putStream('somewhere/here.txt', $putStream);

if (is_resource($putStream)) {
    fclose($putStream);
}
~~~
PK       ! g        flysystem/docs/.gitignorenu         _site
PK       ! PI 6  6    flysystem/docs/core-concepts.mdnu         ---
layout: default
permalink: /core-concepts/
title: Core Concepts
---

# Core Concepts

In order to better understand how and why Flysystem works
the way it does, several concepts require some explanation.

## Overview

* [Adapters](#adapters)
* [Relative Paths](#relative-paths)
* [Files first](#files-first)

## Adapters

The main entry point for the file system API is the
FilesystemInterface. When working with file systems, this is
the class you'll want to be talking to.

Flysystem works the way it does because of its use of the 
adapter pattern. The inconsistencies of the different file 
systems are eliminated in these adapters.

While adapters have a public interface (publicly accessible
methods), they should be considered __internal__.

## Relative Paths

Portability is a very important concept within Flysystem. In order
to roll out this aspect to the fullest, all paths in Flysystem are
relative. File system root paths, whether remote or local, are viewed
as endpoints. Because of this, file systems are movable independently.
This allows parts of the application file handling to move to other
storage types, while the majority is in a centralized location.

Like the storage type, root paths are an implementation detail. When
root paths are defined as configuration, the stability of your code
improves.

## Files First

Flysystem has a files first approach. Storage systems like AWS S3
are linear file systems, this means the path to a file is used as an
identifier, rather than a representation of all the directories it's
nested in.

This means directories are second class citizens. Because of this,
directories will be automatically created on file systems that require
them when writing files. Not only does this make handling writes a lot
easier, it also ensures a consistent behaviors across all file system
types.
PK       ! ee+        flysystem/docs/_data/images.ymlnu         # Path to project specific favicon.ico, leave blank to use default
favicon:

# Path to project specific apple-touch-icon-precomposed.png, leave blank to use default
apple_touch:

# Path to project logo
logo:
PK       ! Q`/         flysystem/docs/_data/project.ymlnu         title: "Flysystem"
tagline: "Multiple Filesystems, One API"
description: "Filesystem abstraction package allowing you to use the same API for both local, remote and cloud filesystems."
google_analytics_tracking_id: "UA-46050814-3"
PK       ! CɃ      flysystem/docs/_data/menu.ymlnu         Getting Started:
    Introduction: '/'
    Installation: '/installation/'
    Integrations: '/integrations/'
    Core concepts: '/core-concepts/'
    The API: '/api/'
    Mount Manager: '/mount-manager/'
    Caching: '/caching/'
    Recipes: '/recipes/'
    Plugins: '/plugins/'
    Upgrade to 1.0.0: '/upgrade-to-1.0.0/'
    Creating an adapter: '/creating-an-adapter/'
    Performance: '/performance/'
Adapters:
    Local: '/adapter/local/'
    Azure: '/adapter/azure/'
    AWS S3 V2: '/adapter/aws-s3-v2/'
    AWS S3 V3: '/adapter/aws-s3-v3/'
    Copy.com: '/adapter/copy/'
    Dropbox: '/adapter/dropbox/'
    FTP: '/adapter/ftp/'
    GridFS: '/adapter/gridfs/'
    Memory: '/adapter/memory/'
    Null / Test: '/adapter/null-test/'
    Rackspace: '/adapter/rackspace/'
    ReplicateAdapter: '/adapter/replicate/'
    SFTP: '/adapter/sftp/'
    WebDAV: '/adapter/webdav/'
    PHPCR: '/adapter/phpcr/'
    ZipArchive: '/adapter/zip-archive/'
PK       ! Ze  e  %  flysystem/docs/creating-an-adapter.mdnu         ---
layout: default
permalink: /creating-an-adapter/
title: Creating an adapter
---

# Creating an adapter

If you want to address a file system, and there's no
adapter available, you'll need to create your own.

## What is an adapter

An adapter can be seen as a plug - it bridges the gap
between initially incompatible API's. The job of the adapter
is to translate requests into calls the file system
understands and re-format responses to comply with
the interface of the generic file system.

An adapter should __NEVER__ be used directly. It should
__ONLY__ be used to create a `League\Flysystem\FilesystemInterface`
implementation instance.

## The main interface to implement

An adapter is required to be an implementation of
`League\Flysystem\AdapterInterface`. This interface
dictates all the methods that need to be implemented.
The interface of an adapter is similar to the
`League\Flysystem\FilesystemInterface`, the method
names are the same, but the response is often different.

Responses from adapters are often arrays containing the
requested value. This is done because many calls to 
file systems return more values than initially requested
by the client. In order to be able to optimize file system
handling, all metadata is returned. For instance, when a
`listContents` call not only returns the paths, but also
timestamps or other related metadata, this information is
not lost. This information is returned through metadata, allowing
caching decorators to pick it up, and store for further use.

### Response values

key         | description              | type
----------- | ------------------------ | -----------
type        | `file` or `dir`          | `string`
path        | path to the file or dir  | `string`
contents    | file contents            | `string`
stream      | file contents            | `resource`
visibility  | `public` or `private`    | `string`
timestamp   | modified time            | `integer`

## Sharing the wealth

Have you created an adapter? Be sure to let us know!
Either create an issue on the GitHub repository, or
send a PR adding a link to the README. Contributions
are always very welcome.
PK       ! Lu  u    flysystem/docs/recipes.mdnu         ---
layout: default
permalink: /recipes/
title: Recipes
---

# Recipes

Flysystem Recipes describe common tasks and/or describe prefered ways to deal with
a problem. Please consider contributing a recipe. Contributions are very welcome!

## Handling uploads

### Plain PHP Upload

~~~ php
$stream = fopen($_FILES[$uploadname]['tmp_name'], 'r+');
$filesystem->writeStream('uploads/'.$_FILES[$uploadname]['name'], $stream);
fclose($stream);
~~~

### Symfony Upload

~~~ php
/** @var Symfony\Component\HttpFoundation\Request $request */
/** @var Symfony\Component\HttpFoundation\File\UploadedFile $file */
$file = $request->files->get($uploadname);

if ($file->isValid()) {
    $stream = fopen($file->getRealPath(), 'r+');
    $filesystem->writeStream('uploads/'.$file->getClientOriginalName(), $stream);
    fclose($stream);
}
~~~

### Laravel 5 - DI

~~~ php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;

class UploadController extends Controller {

    /**
     * Upload a file.
     *
     * @param  Request  $request
     * @return Response
     */
    public function store(Request $request, FilesystemInterface $filesystem)
    {
        $file = $request->file('upload');
        $stream = fopen($file->getRealPath(), 'r+');
        $filesystem->writeStream('uploads/'.$file->getClientOriginalName(), $stream);
        fclose($stream);
    }
}
~~~

### Laravel 4/5 - Static-Access Proxy

~~~ php
$file = Request::file($uploadname);

if ($file->isValid()) {
    $stream = fopen($file->getRealPath(), 'r+');
    $filesystem->writeStream('uploads/'.$file->getClientOriginalName(), $stream);
    fclose($stream);
}
~~~

### Yii 2 Upload

~~~ php
<?php

namespace app\controllers;

use yii\web\Controller;
use yii\web\UploadedFile;

class FileController extends Controller
{
    public function actionUpload()
    {
        $file = UploadedFile::getInstanceByName($uploadname);
        
        if ($file->error === UPLOAD_ERR_OK) {
            $stream = fopen($file->tempName, 'r+');
            $filesystem->writeStream('uploads/'.$file->name, $stream);
            fclose($stream);
        }
    }
}
~~~
PK       ! Иh      flysystem/docs/mount-manager.mdnu         ---
layout: default
permalink: /mount-manager/
title: Mount Manager
---

# Mount Manager

Flysystem comes with an wrapper class to easily work with multiple file system instances
from a single object. The `League\Flysystem\MountManager` is an easy to use container allowing
you to simplify more complex cross file system interactions.

Setting up a Mount Manager is easy:

~~~ php
$ftp = new League\Flysystem\Filesystem($ftpAdapter);
$s3 = new League\Flysystem\Filesystem($s3Adapter);
$local = new League\Flysystem\Filesystem($localAdapter);

// Add them in the constructor
$manager = new League\Flysystem\MountManager([
    'ftp' => $ftp,
    's3' => $s3,
]);

// Or mount them later
$manager->mountFilesystem('local', $local);
~~~

Now we do all the file operations we'd normally do on a `Flysystem\Filesystem` instance.

~~~ php
// Read from FTP
$contents = $manager->read('ftp://some/file.txt');

// And write to local
$manager->write('local://put/it/here.txt', $contents);
~~~

This makes it easy to code up simple sync strategies.

~~~ php
$contents = $manager->listContents('local://uploads', true);

foreach ($contents as $entry) {
    $update = false;

    if ( ! $manager->has('storage://'.$entry['path'])) {
        $update = true;
    } elseif ($manager->getTimestamp('local://'.$entry['path']) > $manager->getTimestamp('storage://'.$entry['path'])) {
        $update = true;
    }

    if ($update) {
        $manager->put('storage://'.$entry['path'], $manager->read('local://'.$entry['path']));
    }
}
~~~

## Specialized calls

### Copy

The copy method provided by the Mount Manager takes the origin of the file into account.
When it detects the source and destination are located on a different file systems it'll
use a streamed upload instead, transparently.

~~~ php
$mountManager->copy('local://some/file.ext', 'backup://storage/location.ext');
~~~

### Move

The `move` call is the multi-file system counterpart to `rename`. Where rename must be used on
the same file system, the `move` call provides the same conceptual behavior, but then on two
different file systems.

~~~ php
$mountManager->move('local://some/upload.jpeg', 'cdn://users/1/profile-picture.jpeg');
~~~
PK       ! {    #  flysystem/docs/adapter/replicate.mdnu         ---
layout: default
permalink: /adapter/replicate/
title: Replicate Adapter
---

# Replicate Adapter

## Installation

~~~ bash
composer require league/flysystem-replicate-adapter
~~~

## Usage

The `ReplicateAdapter` facilitates smooth transitions between adapters, allowing an application to stay functional and migrate its files from one adapter to another. The adapter takes two other adapters, a source and a replica. Every change is delegated to both adapters, while all the read operations are passed onto the source only.

~~~ php
$source = new League\Flysystem\AwsS3V3\AwsS3Adapter(...);
$replica = new League\Flysystem\Adapter\Local(...);
$adapter = new League\Flysystem\Replicate\ReplicateAdapter($source, $replica);
~~~
PK       ! ;    !  flysystem/docs/adapter/dropbox.mdnu         ---
layout: default
permalink: /adapter/dropbox/
title: Dropbox Adapter
---

# Dropbox Adapter

## Installation

~~~ bash
composer require spatie/flysystem-dropbox
~~~

## Usage

A token can be generated in the [App Console](https://www.dropbox.com/developers/apps) for any Dropbox API app. You'll find more info at [the Dropbox Developer Blog](https://blogs.dropbox.com/developers/2014/05/generate-an-access-token-for-your-own-account/).

~~~ php
use League\Flysystem\Filesystem;
use Spatie\Dropbox\Client;
use Spatie\FlysystemDropbox\DropboxAdapter;

$client = new Client($authorizationToken);

$adapter = new DropboxAdapter($client);

$filesystem = new Filesystem($adapter);
~~~
PK       ! N;u  u     flysystem/docs/adapter/webdav.mdnu         ---
layout: default
permalink: /adapter/webdav/
title: WebDAV Adapter
---

# WebDAV Adapter

## Installation

~~~ bash
composer require league/flysystem-webdav
~~~

## Usage

~~~ php
$client = new Sabre\DAV\Client($settings);
$adapter = new League\Flysystem\WebDAV\WebDAVAdapter($client, 'optional/path/prefix');
$flysystem = new League\Flysystem\Filesystem($adapter);
~~~
PK       ! H      flysystem/docs/adapter/copy.mdnu         ---
layout: default
permalink: /adapter/copy/
title: Copy.com Adapter
---

# Copy.com Adapter

## Installation

~~~ bash
composer require league/flysystem-copy
~~~

## Usage

~~~ php
use Barracuda\Copy\API;
use League\Flysystem\Filesystem;
use League\Flysystem\Copy\CopyAdapter;

$client = new API($consumerKey, $consumerSecret, $accessToken, $tokenSecret);
$filesystem = new Filesystem(new CopyAdapter($client, 'optional/path/prefix'));
~~~
PK       ! Ɏ    #  flysystem/docs/adapter/rackspace.mdnu         ---
layout: default
permalink: /adapter/rackspace/
title: Rackspace Adapter
---

# Rackspace Adapter

## Installation

~~~ bash
composer require league/flysystem-rackspace
~~~

## Usage

~~~ php
use OpenCloud\OpenStack;
use OpenCloud\Rackspace;
use League\Flysystem\Filesystem;
use League\Flysystem\Rackspace\RackspaceAdapter;

$client = new OpenStack(Rackspace::UK_IDENTITY_ENDPOINT, [
    'username' => ':username',
    'password' => ':password',
]);

$store = $client->objectStoreService('cloudFiles', 'LON');
$container = $store->getContainer('flysystem');

$filesystem = new Filesystem(new RackspaceAdapter($container, 'optional/path/prefix'));
~~~
PK       ! 7z0  0  #  flysystem/docs/adapter/null-test.mdnu         ---
layout: default
permalink: /adapter/null-test/
title: Null Adapter
---

# Null Adapter

## Installation

Comes with the main Flysystem package.

## Usage

Acts like `/dev/null`

~~~ php
$adapter = new League\Flysystem\Adapter\NullAdapter;
$filesystem = new League\Flysystem\Filesystem($adapter);
~~~
PK       ! C<      flysystem/docs/adapter/sftp.mdnu         ---
layout: default
permalink: /adapter/sftp/
title: SFTP Adapter
---

# SFTP Adapter

## Installation

~~~ bash
composer require league/flysystem-sftp
~~~

## Usage

~~~ php
use League\Flysystem\Filesystem;
use League\Flysystem\Sftp\SftpAdapter;

$filesystem = new Filesystem(new SftpAdapter([
    'host' => 'example.com',
    'port' => 21,
    'username' => 'username',
    'password' => 'password',
    'privateKey' => 'path/to/or/contents/of/privatekey',
    'root' => '/path/to/root',
    'timeout' => 10,
]));
~~~
PK       ! k 4y       flysystem/docs/adapter/gridfs.mdnu         ---
layout: default
permalink: /adapter/gridfs/
title: GridFS Adapter
---

# GridFS Adapter

## Installation

~~~ bash
composer require league/flysystem-gridfs
~~~

## Usage

~~~ php
use League\Flysystem\GridFS\GridFSAdapter;
use League\Flysystem\Filesystem;

$mongoClient = new MongoClient();
$gridFs = $mongoClient->selectDB('db_name')->getGridFS();

$adapter = new GridFSAdapter($gridFs);
$filesystem = new Filesystem($adapter);
~~~
PK       !       flysystem/docs/adapter/ftp.mdnu         ---
layout: default
permalink: /adapter/ftp/
title: FTP Adapter
---

# FTP Adapter

## Installation

Comes with the main Flysystem package.

## Usage

~~~ php
use League\Flysystem\Filesystem;
use League\Flysystem\Adapter\Ftp as Adapter;

$filesystem = new Filesystem(new Adapter([
    'host' => 'ftp.example.com',
    'username' => 'username',
    'password' => 'password',

    /** optional config settings */
    'port' => 21,
    'root' => '/path/to/root',
    'passive' => true,
    'ssl' => true,
    'timeout' => 30,
]));
~~~
PK       ! S  S    flysystem/docs/adapter/azure.mdnu         ---
layout: default
permalink: /adapter/azure/
title: Azure Blob Storage
---

# Azure Blob Storage

## Installation

~~~ bash
composer require league/flysystem-azure
~~~

## Usage

~~~ php
use WindowsAzure\Common\ServicesBuilder;
use League\Flysystem\Filesystem;
use League\Flysystem\Azure\AzureAdapter;

$endpoint = sprintf(
    'DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s',
    'account-name',
    'api-key'
);

$blobRestProxy = ServicesBuilder::getInstance()->createBlobService($endpoint);

$filesystem = new Filesystem(new AzureAdapter($blobRestProxy, 'my-container'));
~~~
PK       ! (VG  G  #  flysystem/docs/adapter/aws-s3-v3.mdnu         ---
layout: default
permalink: /adapter/aws-s3-v3/
title: Aws S3 Adapter V3
---

# Aws S3 Adapter - SDK V3

## Installation

~~~ bash
composer require league/flysystem-aws-s3-v3
~~~

## Usage

~~~ php
use Aws\S3\S3Client;
use League\Flysystem\AwsS3v3\AwsS3Adapter;
use League\Flysystem\Filesystem;

$client = S3Client::factory([
    'credentials' => [
        'key'    => 'your-key',
        'secret' => 'your-secret',
    ],
    'region' => 'your-region',
    'version' => 'latest|version',
]);

$adapter = new AwsS3Adapter($client, 'your-bucket-name', 'optional/path/prefix');
~~~
PK       ! ?)?/  /    flysystem/docs/adapter/local.mdnu         ---
layout: default
permalink: /adapter/local/
title: Local Adapter
---

# Local Adapter

## Installation

Comes with the main Flysystem package.

## Usage

~~~ php
use League\Flysystem\Filesystem;
use League\Flysystem\Adapter\Local;

$adapter = new Local(__DIR__.'/path/to/root');
$filesystem = new Filesystem($adapter);
~~~

## Locks

By default this adapter uses a lock during writes
and updates. This behaviour can be altered using the
second constructor argument.

~~~ php
$adapter = new Local(__DIR__.'/path/to/too', 0);
~~~

## Links [added in 1.0.8]

The Local adapter doesn't support links, this violates
the root path constraint which is enforced throughout
Flysystem. By default, when links are encountered an
exception is thrown. This behaviour can be altered
using the third constructor argument.

~~~ php
// Skip links
$adapter = new Local(__DIR__.'/path/to/too', LOCK_EX, Local::SKIP_LINKS);

// Throw exceptions (default)
$adapter = new Local(__DIR__.'/path/to/too', LOCK_EX, Local::DISALLOW_LINKS);
~~~

## File and directory permission settings [added in 1.0.14]

~~~ php
$adapter = new Local(__DIR__.'/path/to/too', LOCK_EX, Local::DISALLOW_LINKS, [
    'file' => [
        'public' => 0744,
        'private' => 0700,
    ],
    'dir' => [
        'public' => 0755,
        'private' => 0700,
    ]
]);
~~~
PK       ! r    #  flysystem/docs/adapter/aws-s3-v2.mdnu         ---
layout: default
permalink: /adapter/aws-s3-v2/
title: Aws S3 Adapter V2
---

# Aws S3 Adapter - SDK V2

## Installation

~~~ bash
composer require league/flysystem-aws-s3-v2
~~~

## Usage

~~~ php
use Aws\S3\S3Client;
use League\Flysystem\AwsS3v2\AwsS3Adapter;
use League\Flysystem\Filesystem;

$client = S3Client::factory([
    'key'    => '[your key]',
    'secret' => '[your secret]',
    'region' => '[aws-region]',
]);

$adapter = new AwsS3Adapter($client, 'bucket-name', 'optional/path/prefix');

$filesystem = new Filesystem($adapter);
~~~

To enable [reduced redunancy storage](http://aws.amazon.com/s3/details/#RRS) set up your adapter like so:

~~~ php
$adapter = new AwsS3Adapter($client, 'bucket-name', 'optional/path/prefix', [
    'StorageClass'  =>  'REDUCED_REDUNDANCY',
]);
~~~

### Compatible storage protocols

If you're using a storage service which implements the S3 protocols, you can set the `base_url` configuration option when constructing the client.

~~~ php
$client = S3Client::factory([
    'base_url' => 'http://some.other.endpoint',
    // ... other settings
]);
~~~

Known compliant storage providers are:

* [Google Cloud Storage](https://cloud.google.com/storage/docs/migrating#migration-simple)
* Know more? Please submit a PR!
PK       ! iq0    %  flysystem/docs/adapter/zip-archive.mdnu         ---
layout: default
permalink: /adapter/zip-archive/
title: ZipArchive Adapter
---

# ZipArchive Adapter

## Installation

~~~ bash
composer require league/flysystem-ziparchive
~~~

## Usage

~~~ php
use League\Flysystem\Filesystem;
use League\Flysystem\ZipArchive\ZipArchiveAdapter;

$filesystem = new Filesystem(new ZipArchiveAdapter(__DIR__.'/path/to/archive.zip'));
~~~

### Force Save

When creating a new zip file it will only be saved at the end of the PHP request because the ZipArchive library relies on an internal `__destruct` method to be called. You can force the saving of the zip file before the end of the request by calling the `close` method on the archive through the adapter.

~~~ php
$filesystem->getAdapter()->getArchive()->close();
~~~
PK       !        flysystem/docs/adapter/memory.mdnu         ---
layout: default
permalink: /adapter/memory/
title: Memory Adapter
---

# Memory Adapter

This adapter keeps the filesystem completely in memory. This is useful when you need a filesystem, but don't want it persisted.

## Installation

~~~ bash
composer require league/flysystem-memory
~~~

## Usage

~~~ php
use League\Flysystem\Filesystem;
use League\Flysystem\Memory\MemoryAdapter;

$filesystem = new Filesystem(new MemoryAdapter());
~~~
PK       ! _Ã      flysystem/docs/adapter/phpcr.mdnu         ---
layout: default
permalink: /adapter/phpcr/
title: PHPCR Adapter
---

# PHPCR Adapter

This adapter works with any [PHPCR](http://phpcr.github.io) implementation.
Choose the one that fits your needs and add it to your project, or composer
will  complain that you miss `phpcr/phpcr-implementation`. See
[this article](http://symfony.com/doc/master/cmf/cookbook/database/choosing_phpcr_implementation.html)
for more on choosing your implementation.

## Installation

Assuming you go with jackalope-doctrine-dbal, do:

~~~bash
composer require jackalope/jackalope-doctrine-dbal league/flysystem-phpcr
~~~

## Usage

Bootstrap your PHPCR implementation. If you chose jackalope-doctrine-dbal with sqlite, 
this will look like this for example:

~~~php
use League\Flysystem\Filesystem;
use League\Flysystem\Phpcr\PhpcrAdapter;
use Jackalope\RepositoryFactoryDoctrineDBAL;
use Doctrine\DBAL\Driver\Connection;
use Doctrine\DBAL\DriverManager;

$connection = DriverManager::getConnection([
    'driver' => 'pdo_sqlite',
    'path'   => '/path/to/sqlite.db',
]);
$factory = new RepositoryFactoryDoctrineDBAL();
$repository = $factory->getRepository([
    'jackalope.doctrine_dbal_connection' => $connection,
]);
$session = $repository->login(new SimpleCredentials('', ''));

// this part looks the same regardless of your phpcr implementation.
$root = '/flysystem_tests';
$filesystem = new Filesystem(new PhpcrAdapter($session, $root));
~~~
PK       ! S      flysystem/docs/performance.mdnu         ---
layout: default
permalink: /performance/
title: API
---

# Performance

Flysystem aims to be as reliable as possible. In some cases this means doing extra
checks to make sure the outcome will be as expected. For some adapter this means Flysystem
will make extra calls to assert whether or not a file exists. This improves the reliability
but also impacts performance. You can opt out of this behaviour.

~~~ php
new League\Flysystem\Config;
new League\Flysystem\Filesystem;

$local = Filesystem($localAdapter, new Config([
    'disable_asserts' => true,
]));
~~~

This will disable the asserts which happen before the following calls: write, writeStream, update,
updateStream, copy (2x), and delete.

This functionality is available since `1.0.26`.
PK       ! Xv  v  $  flysystem/docs/_layouts/default.htmlnu         <!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    {% if page.url == '/' %}
        <title>{{ site.data.project.title }} - {{ site.data.project.tagline }}</title>
    {% else %}
        <title>{{ page.title }} - {{ site.data.project.title }}</title>
    {% endif %}
    {% if site.data.project.description %}
        <meta name="description" content="{{ site.data.project.description }}">
    {% endif %}
    {% if site.data.images.favicon %}
        <link rel="icon" type="image/x-icon" href="{{ site.data.images.favicon }}" />
    {% else %}
        <link rel="icon" type="image/x-icon" href="//theme.thephpleague.com/img/favicon.ico" />
    {% endif %}
    {% if site.data.images.apple_touch %}
        <link rel="apple-touch-icon-precomposed" href="{{ site.data.images.apple_touch }}">
    {% else %}
        <link rel="apple-touch-icon-precomposed" href="//theme.thephpleague.com/img/apple-touch-icon-precomposed.png">
    {% endif %}
    <link rel="stylesheet" href="//theme.thephpleague.com/css/all.css">
    <base href="//flysystem.thephpleague.com{{ page.permalink }}">
</head>
<body>

<section class="all_packages">
    <a href="//thephpleague.com/">
        <img src="//theme.thephpleague.com/img/loep_logo.png" width="195" height="200" alt="The League of Extraordinary Packages">
    </a>
    <h2>Our Packages:</h2>
    <ul>
        <!-- Loaded via JavaScript -->
    </ul>
</section>

<header>
    <a class="logo" href="/">
        {% if site.data.images.logo %}
            <span class="icon">
                <img src="{{ site.data.images.logo }}" width="50" height="40" alt="{{ site.data.project.title }} - {{ site.data.project.tagline }}">
            </span>
        {% endif %}
        <span class="name">{{ site.data.project.title }}</span>
        <span class="tagline">{{ site.data.project.tagline }}</span>
    </a>
    <a href="//thephpleague.com/" class="league">
        Presented by The League of Extraordinary Packages
    </a>
</header>

<input type="checkbox" id="menu">
<label for="menu" onclick>
    <div class="closed">&#9776; Menu</div>
    <div class="open">&#9776; Hide Menu</div>
</label>

<main>
    <menu>
        {% for section in site.data.menu %}
            <h2>{{ section[0] }}</h2>
            <ul>
                {% for link in section[1] %}
                    <li {% if page.url == link[1] %}class="selected"{% endif %}>
                        <a href="{{ link[1] }}">{{ link[0] }}</a>
                    </li>
                {% endfor %}
            </ul>
        {% endfor %}
    </menu>
    <article>
        {{ content }}
    </article>
</main>

<footer>
    <span>&copy; Copyright <a href="//thephpleague.com">The League of Extraordinary Packages</a>.</span>
    <span>Site design by <a href="//reinink.ca">Jonathan Reinink</a>.</span>
</footer>

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="//theme.thephpleague.com/js/scripts.js"></script>
<script src="//theme.thephpleague.com/js/prism.js"></script>

{% if site.data.project.google_analytics_tracking_id %}
    <script>
        (function(b,o,i,l,e,r){b.GoogleAnalyticsObject=l;b[l]||(b[l]=
        function(){(b[l].q=b[l].q||[]).push(arguments)});b[l].l=+new Date;
        e=o.createElement(i);r=o.getElementsByTagName(i)[0];
        e.src='//www.google-analytics.com/analytics.js';
        r.parentNode.insertBefore(e,r)}(window,document,'script','ga'));
        ga('create','{{  site.data.project.google_analytics_tracking_id }}');ga('send','pageview');
    </script>
{% endif %}

</body>
</html>
PK       ! ObA  A    flysystem/composer.jsonnu         {
    "name": "league/flysystem",
    "description": "Filesystem abstraction: Many filesystems, one API.",
    "keywords": [
        "filesystem", "filesystems", "files", "storage", "dropbox", "aws",
        "abstraction", "s3", "ftp", "sftp", "remote", "webdav",
        "file systems", "cloud", "cloud files", "rackspace", "copy.com"
    ],
    "license": "MIT",
    "authors": [
        {
            "name": "Frank de Jonge",
            "email": "info@frenky.net"
        }
    ],
    "require": {
        "php": ">=5.5.9"
    },
    "require-dev": {
        "ext-fileinfo": "*",
        "phpunit/phpunit": "~4.8",
        "mockery/mockery": "~0.9",
        "phpspec/phpspec": "^2.2"
    },
    "autoload": {
        "psr-4": {
            "League\\Flysystem\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "League\\Flysystem\\Stub\\": "stub/"
        }
    },
    "suggest": {
        "ext-fileinfo": "Required for MimeType",
        "league/flysystem-eventable-filesystem": "Allows you to use EventableFilesystem",
        "league/flysystem-rackspace": "Allows you to use Rackspace Cloud Files",
        "league/flysystem-azure": "Allows you to use Windows Azure Blob storage",
        "league/flysystem-webdav": "Allows you to use WebDAV storage",
        "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2",
        "league/flysystem-aws-s3-v3": "Allows you to use S3 storage with AWS SDK v3",
        "spatie/flysystem-dropbox": "Allows you to use Dropbox storage",
        "srmklive/flysystem-dropbox-v2": "Allows you to use Dropbox storage for PHP 5 applications",
        "league/flysystem-cached-adapter": "Flysystem adapter decorator for metadata caching",
        "league/flysystem-sftp": "Allows you to use SFTP server storage via phpseclib",
        "league/flysystem-ziparchive": "Allows you to use ZipArchive adapter"
    },
    "conflict": {
        "league/flysystem-sftp": "<1.0.6"
    },
    "config": {
        "bin-dir": "bin"
    },
    "extra": {
        "branch-alias": {
            "dev-master": "1.1-dev"
        }
    }
}
PK       ! x_'  '    flysystem/LICENSEnu         Copyright (c) 2013-2017 Frank de Jonge

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
PK       ! O; ;   oauth1-client/rfc5849.txtnu         





Internet Engineering Task Force (IETF)              E. Hammer-Lahav, Ed.
Request for Comments: 5849                                    April 2010
Category: Informational
ISSN: 2070-1721


                         The OAuth 1.0 Protocol

Abstract

   OAuth provides a method for clients to access server resources on
   behalf of a resource owner (such as a different client or an end-
   user).  It also provides a process for end-users to authorize third-
   party access to their server resources without sharing their
   credentials (typically, a username and password pair), using user-
   agent redirections.

Status of This Memo

   This document is not an Internet Standards Track specification; it is
   published for informational purposes.

   This document is a product of the Internet Engineering Task Force
   (IETF).  It represents the consensus of the IETF community.  It has
   received public review and has been approved for publication by the
   Internet Engineering Steering Group (IESG).  Not all documents
   approved by the IESG are a candidate for any level of Internet
   Standard; see Section 2 of RFC 5741.

   Information about the current status of this document, any errata,
   and how to provide feedback on it may be obtained at
   http://www.rfc-editor.org/info/rfc5849.

Copyright Notice

   Copyright (c) 2010 IETF Trust and the persons identified as the
   document authors.  All rights reserved.

   This document is subject to BCP 78 and the IETF Trust's Legal
   Provisions Relating to IETF Documents
   (http://trustee.ietf.org/license-info) in effect on the date of
   publication of this document.  Please review these documents
   carefully, as they describe your rights and restrictions with respect
   to this document.  Code Components extracted from this document must
   include Simplified BSD License text as described in Section 4.e of
   the Trust Legal Provisions and are provided without warranty as
   described in the Simplified BSD License.




Hammer-Lahav                  Informational                     [Page 1]

RFC 5849                        OAuth 1.0                     April 2010


Table of Contents

   1. Introduction ....................................................3
      1.1. Terminology ................................................4
      1.2. Example ....................................................5
      1.3. Notational Conventions .....................................7
   2. Redirection-Based Authorization .................................8
      2.1. Temporary Credentials ......................................9
      2.2. Resource Owner Authorization ..............................10
      2.3. Token Credentials .........................................12
   3. Authenticated Requests .........................................14
      3.1. Making Requests ...........................................14
      3.2. Verifying Requests ........................................16
      3.3. Nonce and Timestamp .......................................17
      3.4. Signature .................................................18
           3.4.1. Signature Base String ..............................18
           3.4.2. HMAC-SHA1 ..........................................25
           3.4.3. RSA-SHA1 ...........................................25
           3.4.4. PLAINTEXT ..........................................26
      3.5. Parameter Transmission ....................................26
           3.5.1. Authorization Header ...............................27
           3.5.2. Form-Encoded Body ..................................28
           3.5.3. Request URI Query ..................................28
      3.6. Percent Encoding ..........................................29
   4. Security Considerations ........................................29
      4.1. RSA-SHA1 Signature Method .................................29
      4.2. Confidentiality of Requests ...............................30
      4.3. Spoofing by Counterfeit Servers ...........................30
      4.4. Proxying and Caching of Authenticated Content .............30
      4.5. Plaintext Storage of Credentials ..........................30
      4.6. Secrecy of the Client Credentials .........................31
      4.7. Phishing Attacks ..........................................31
      4.8. Scoping of Access Requests ................................31
      4.9. Entropy of Secrets ........................................32
      4.10. Denial-of-Service / Resource-Exhaustion Attacks ..........32
      4.11. SHA-1 Cryptographic Attacks ..............................33
      4.12. Signature Base String Limitations ........................33
      4.13. Cross-Site Request Forgery (CSRF) ........................33
      4.14. User Interface Redress ...................................34
      4.15. Automatic Processing of Repeat Authorizations ............34
   5. Acknowledgments ................................................35
   Appendix A.  Differences from the Community Edition ...............36
   6. References .....................................................37
      6.1. Normative References ......................................37
      6.2. Informative References ....................................38






Hammer-Lahav                  Informational                     [Page 2]

RFC 5849                        OAuth 1.0                     April 2010


1.  Introduction

   The OAuth protocol was originally created by a small community of web
   developers from a variety of websites and other Internet services who
   wanted to solve the common problem of enabling delegated access to
   protected resources.  The resulting OAuth protocol was stabilized at
   version 1.0 in October 2007, and revised in June 2009 (Revision A) as
   published at <http://oauth.net/core/1.0a>.

   This specification provides an informational documentation of OAuth
   Core 1.0 Revision A, addresses several errata reported since that
   time, and makes numerous editorial clarifications.  While this
   specification is not an item of the IETF's OAuth Working Group, which
   at the time of writing is working on an OAuth version that can be
   appropriate for publication on the standards track, it has been
   transferred to the IETF for change control by authors of the original
   work.

   In the traditional client-server authentication model, the client
   uses its credentials to access its resources hosted by the server.
   With the increasing use of distributed web services and cloud
   computing, third-party applications require access to these server-
   hosted resources.

   OAuth introduces a third role to the traditional client-server
   authentication model: the resource owner.  In the OAuth model, the
   client (which is not the resource owner, but is acting on its behalf)
   requests access to resources controlled by the resource owner, but
   hosted by the server.  In addition, OAuth allows the server to verify
   not only the resource owner authorization, but also the identity of
   the client making the request.

   OAuth provides a method for clients to access server resources on
   behalf of a resource owner (such as a different client or an end-
   user).  It also provides a process for end-users to authorize third-
   party access to their server resources without sharing their
   credentials (typically, a username and password pair), using user-
   agent redirections.

   For example, a web user (resource owner) can grant a printing service
   (client) access to her private photos stored at a photo sharing
   service (server), without sharing her username and password with the
   printing service.  Instead, she authenticates directly with the photo
   sharing service which issues the printing service delegation-specific
   credentials.






Hammer-Lahav                  Informational                     [Page 3]

RFC 5849                        OAuth 1.0                     April 2010


   In order for the client to access resources, it first has to obtain
   permission from the resource owner.  This permission is expressed in
   the form of a token and matching shared-secret.  The purpose of the
   token is to make it unnecessary for the resource owner to share its
   credentials with the client.  Unlike the resource owner credentials,
   tokens can be issued with a restricted scope and limited lifetime,
   and revoked independently.

   This specification consists of two parts.  The first part defines a
   redirection-based user-agent process for end-users to authorize
   client access to their resources, by authenticating directly with the
   server and provisioning tokens to the client for use with the
   authentication method.  The second part defines a method for making
   authenticated HTTP [RFC2616] requests using two sets of credentials,
   one identifying the client making the request, and a second
   identifying the resource owner on whose behalf the request is being
   made.

   The use of OAuth with any transport protocol other than [RFC2616] is
   undefined.

1.1.  Terminology

   client
         An HTTP client (per [RFC2616]) capable of making OAuth-
         authenticated requests (Section 3).

   server
         An HTTP server (per [RFC2616]) capable of accepting OAuth-
         authenticated requests (Section 3).

   protected resource
         An access-restricted resource that can be obtained from the
         server using an OAuth-authenticated request (Section 3).

   resource owner
         An entity capable of accessing and controlling protected
         resources by using credentials to authenticate with the server.

   credentials
         Credentials are a pair of a unique identifier and a matching
         shared secret.  OAuth defines three classes of credentials:
         client, temporary, and token, used to identify and authenticate
         the client making the request, the authorization request, and
         the access grant, respectively.






Hammer-Lahav                  Informational                     [Page 4]

RFC 5849                        OAuth 1.0                     April 2010


   token
         A unique identifier issued by the server and used by the client
         to associate authenticated requests with the resource owner
         whose authorization is requested or has been obtained by the
         client.  Tokens have a matching shared-secret that is used by
         the client to establish its ownership of the token, and its
         authority to represent the resource owner.

   The original community specification used a somewhat different
   terminology that maps to this specifications as follows (original
   community terms provided on left):

   Consumer:  client

   Service Provider:  server

   User:  resource owner

   Consumer Key and Secret:  client credentials

   Request Token and Secret:  temporary credentials

   Access Token and Secret:  token credentials

1.2.  Example

   Jane (resource owner) has recently uploaded some private vacation
   photos (protected resources) to her photo sharing site
   'photos.example.net' (server).  She would like to use the
   'printer.example.com' website (client) to print one of these photos.
   Typically, Jane signs into 'photos.example.net' using her username
   and password.

   However, Jane does not wish to share her username and password with
   the 'printer.example.com' website, which needs to access the photo in
   order to print it.  In order to provide its users with better
   service, 'printer.example.com' has signed up for a set of
   'photos.example.net' client credentials ahead of time:

   Client Identifier
         dpf43f3p2l4k3l03

   Client Shared-Secret:
         kd94hf93k423kf44

   The 'printer.example.com' website has also configured its application
   to use the protocol endpoints listed in the 'photos.example.net' API
   documentation, which use the "HMAC-SHA1" signature method:



Hammer-Lahav                  Informational                     [Page 5]

RFC 5849                        OAuth 1.0                     April 2010


   Temporary Credential Request
         https://photos.example.net/initiate

   Resource Owner Authorization URI:
         https://photos.example.net/authorize

   Token Request URI:
         https://photos.example.net/token

   Before 'printer.example.com' can ask Jane to grant it access to the
   photos, it must first establish a set of temporary credentials with
   'photos.example.net' to identify the delegation request.  To do so,
   the client sends the following HTTPS [RFC2818] request to the server:

     POST /initiate HTTP/1.1
     Host: photos.example.net
     Authorization: OAuth realm="Photos",
        oauth_consumer_key="dpf43f3p2l4k3l03",
        oauth_signature_method="HMAC-SHA1",
        oauth_timestamp="137131200",
        oauth_nonce="wIjqoS",
        oauth_callback="http%3A%2F%2Fprinter.example.com%2Fready",
        oauth_signature="74KNZJeDHnMBp0EMJ9ZHt%2FXKycU%3D"

   The server validates the request and replies with a set of temporary
   credentials in the body of the HTTP response (line breaks are for
   display purposes only):

     HTTP/1.1 200 OK
     Content-Type: application/x-www-form-urlencoded

     oauth_token=hh5s93j4hdidpola&oauth_token_secret=hdhd0244k9j7ao03&
     oauth_callback_confirmed=true

   The client redirects Jane's user-agent to the server's Resource Owner
   Authorization endpoint to obtain Jane's approval for accessing her
   private photos:

     https://photos.example.net/authorize?oauth_token=hh5s93j4hdidpola

   The server requests Jane to sign in using her username and password
   and if successful, asks her to approve granting 'printer.example.com'
   access to her private photos.  Jane approves the request and her
   user-agent is redirected to the callback URI provided by the client
   in the previous request (line breaks are for display purposes only):

     http://printer.example.com/ready?
     oauth_token=hh5s93j4hdidpola&oauth_verifier=hfdp7dh39dks9884



Hammer-Lahav                  Informational                     [Page 6]

RFC 5849                        OAuth 1.0                     April 2010


   The callback request informs the client that Jane completed the
   authorization process.  The client then requests a set of token
   credentials using its temporary credentials (over a secure Transport
   Layer Security (TLS) channel):

     POST /token HTTP/1.1
     Host: photos.example.net
     Authorization: OAuth realm="Photos",
        oauth_consumer_key="dpf43f3p2l4k3l03",
        oauth_token="hh5s93j4hdidpola",
        oauth_signature_method="HMAC-SHA1",
        oauth_timestamp="137131201",
        oauth_nonce="walatlh",
        oauth_verifier="hfdp7dh39dks9884",
        oauth_signature="gKgrFCywp7rO0OXSjdot%2FIHF7IU%3D"

   The server validates the request and replies with a set of token
   credentials in the body of the HTTP response:

     HTTP/1.1 200 OK
     Content-Type: application/x-www-form-urlencoded

     oauth_token=nnch734d00sl2jdk&oauth_token_secret=pfkkdhi9sl3r4s00

   With a set of token credentials, the client is now ready to request
   the private photo:

     GET /photos?file=vacation.jpg&size=original HTTP/1.1
     Host: photos.example.net
     Authorization: OAuth realm="Photos",
        oauth_consumer_key="dpf43f3p2l4k3l03",
        oauth_token="nnch734d00sl2jdk",
        oauth_signature_method="HMAC-SHA1",
        oauth_timestamp="137131202",
        oauth_nonce="chapoH",
        oauth_signature="MdpQcU8iPSUjWoN%2FUDMsK2sui9I%3D"

   The 'photos.example.net' server validates the request and responds
   with the requested photo. 'printer.example.com' is able to continue
   accessing Jane's private photos using the same set of token
   credentials for the duration of Jane's authorization, or until Jane
   revokes access.

1.3.  Notational Conventions

   The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
   "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
   document are to be interpreted as described in [RFC2119].



Hammer-Lahav                  Informational                     [Page 7]

RFC 5849                        OAuth 1.0                     April 2010


2.  Redirection-Based Authorization

   OAuth uses tokens to represent the authorization granted to the
   client by the resource owner.  Typically, token credentials are
   issued by the server at the resource owner's request, after
   authenticating the resource owner's identity (usually using a
   username and password).

   There are many ways in which a server can facilitate the provisioning
   of token credentials.  This section defines one such way, using HTTP
   redirections and the resource owner's user-agent.  This redirection-
   based authorization method includes three steps:

   1.  The client obtains a set of temporary credentials from the server
       (in the form of an identifier and shared-secret).  The temporary
       credentials are used to identify the access request throughout
       the authorization process.

   2.  The resource owner authorizes the server to grant the client's
       access request (identified by the temporary credentials).

   3.  The client uses the temporary credentials to request a set of
       token credentials from the server, which will enable it to access
       the resource owner's protected resources.

   The server MUST revoke the temporary credentials after being used
   once to obtain the token credentials.  It is RECOMMENDED that the
   temporary credentials have a limited lifetime.  Servers SHOULD enable
   resource owners to revoke token credentials after they have been
   issued to clients.

   In order for the client to perform these steps, the server needs to
   advertise the URIs of the following three endpoints:

   Temporary Credential Request
         The endpoint used by the client to obtain a set of temporary
         credentials as described in Section 2.1.

   Resource Owner Authorization
         The endpoint to which the resource owner is redirected to grant
         authorization as described in Section 2.2.

   Token Request
         The endpoint used by the client to request a set of token
         credentials using the set of temporary credentials as described
         in Section 2.3.





Hammer-Lahav                  Informational                     [Page 8]

RFC 5849                        OAuth 1.0                     April 2010


   The three URIs advertised by the server MAY include a query component
   as defined by [RFC3986], Section 3, but if present, the query MUST
   NOT contain any parameters beginning with the "oauth_" prefix, to
   avoid conflicts with the protocol parameters added to the URIs when
   used.

   The methods in which the server advertises and documents its three
   endpoints are beyond the scope of this specification.  Clients should
   avoid making assumptions about the size of tokens and other server-
   generated values, which are left undefined by this specification.  In
   addition, protocol parameters MAY include values that require
   encoding when transmitted.  Clients and servers should not make
   assumptions about the possible range of their values.

2.1.  Temporary Credentials

   The client obtains a set of temporary credentials from the server by
   making an authenticated (Section 3) HTTP "POST" request to the
   Temporary Credential Request endpoint (unless the server advertises
   another HTTP request method for the client to use).  The client
   constructs a request URI by adding the following REQUIRED parameter
   to the request (in addition to the other protocol parameters, using
   the same parameter transmission method):

   oauth_callback:  An absolute URI back to which the server will
                    redirect the resource owner when the Resource Owner
                    Authorization step (Section 2.2) is completed.  If
                    the client is unable to receive callbacks or a
                    callback URI has been established via other means,
                    the parameter value MUST be set to "oob" (case
                    sensitive), to indicate an out-of-band
                    configuration.

   Servers MAY specify additional parameters.

   When making the request, the client authenticates using only the
   client credentials.  The client MAY omit the empty "oauth_token"
   protocol parameter from the request and MUST use the empty string as
   the token secret value.

   Since the request results in the transmission of plain text
   credentials in the HTTP response, the server MUST require the use of
   a transport-layer mechanisms such as TLS or Secure Socket Layer (SSL)
   (or a secure channel with equivalent protections).







Hammer-Lahav                  Informational                     [Page 9]

RFC 5849                        OAuth 1.0                     April 2010


   For example, the client makes the following HTTPS request:

     POST /request_temp_credentials HTTP/1.1
     Host: server.example.com
     Authorization: OAuth realm="Example",
        oauth_consumer_key="jd83jd92dhsh93js",
        oauth_signature_method="PLAINTEXT",
        oauth_callback="http%3A%2F%2Fclient.example.net%2Fcb%3Fx%3D1",
        oauth_signature="ja893SD9%26"

   The server MUST verify (Section 3.2) the request and if valid,
   respond back to the client with a set of temporary credentials (in
   the form of an identifier and shared-secret).  The temporary
   credentials are included in the HTTP response body using the
   "application/x-www-form-urlencoded" content type as defined by
   [W3C.REC-html40-19980424] with a 200 status code (OK).

   The response contains the following REQUIRED parameters:

   oauth_token
         The temporary credentials identifier.

   oauth_token_secret
         The temporary credentials shared-secret.

   oauth_callback_confirmed
         MUST be present and set to "true".  The parameter is used to
         differentiate from previous versions of the protocol.

   Note that even though the parameter names include the term 'token',
   these credentials are not token credentials, but are used in the next
   two steps in a similar manner to token credentials.

   For example (line breaks are for display purposes only):

     HTTP/1.1 200 OK
     Content-Type: application/x-www-form-urlencoded

     oauth_token=hdk48Djdsa&oauth_token_secret=xyz4992k83j47x0b&
     oauth_callback_confirmed=true

2.2.  Resource Owner Authorization

   Before the client requests a set of token credentials from the
   server, it MUST send the user to the server to authorize the request.
   The client constructs a request URI by adding the following REQUIRED
   query parameter to the Resource Owner Authorization endpoint URI:




Hammer-Lahav                  Informational                    [Page 10]

RFC 5849                        OAuth 1.0                     April 2010


   oauth_token
         The temporary credentials identifier obtained in Section 2.1 in
         the "oauth_token" parameter.  Servers MAY declare this
         parameter as OPTIONAL, in which case they MUST provide a way
         for the resource owner to indicate the identifier through other
         means.

   Servers MAY specify additional parameters.

   The client directs the resource owner to the constructed URI using an
   HTTP redirection response, or by other means available to it via the
   resource owner's user-agent.  The request MUST use the HTTP "GET"
   method.

   For example, the client redirects the resource owner's user-agent to
   make the following HTTPS request:

     GET /authorize_access?oauth_token=hdk48Djdsa HTTP/1.1
     Host: server.example.com

   The way in which the server handles the authorization request,
   including whether it uses a secure channel such as TLS/SSL is beyond
   the scope of this specification.  However, the server MUST first
   verify the identity of the resource owner.

   When asking the resource owner to authorize the requested access, the
   server SHOULD present to the resource owner information about the
   client requesting access based on the association of the temporary
   credentials with the client identity.  When displaying any such
   information, the server SHOULD indicate if the information has been
   verified.

   After receiving an authorization decision from the resource owner,
   the server redirects the resource owner to the callback URI if one
   was provided in the "oauth_callback" parameter or by other means.

   To make sure that the resource owner granting access is the same
   resource owner returning back to the client to complete the process,
   the server MUST generate a verification code: an unguessable value
   passed to the client via the resource owner and REQUIRED to complete
   the process.  The server constructs the request URI by adding the
   following REQUIRED parameters to the callback URI query component:

   oauth_token
         The temporary credentials identifier received from the client.






Hammer-Lahav                  Informational                    [Page 11]

RFC 5849                        OAuth 1.0                     April 2010


   oauth_verifier
         The verification code.

   If the callback URI already includes a query component, the server
   MUST append the OAuth parameters to the end of the existing query.

   For example, the server redirects the resource owner's user-agent to
   make the following HTTP request:

     GET /cb?x=1&oauth_token=hdk48Djdsa&oauth_verifier=473f82d3 HTTP/1.1
     Host: client.example.net

   If the client did not provide a callback URI, the server SHOULD
   display the value of the verification code, and instruct the resource
   owner to manually inform the client that authorization is completed.
   If the server knows a client to be running on a limited device, it
   SHOULD ensure that the verifier value is suitable for manual entry.

2.3.  Token Credentials

   The client obtains a set of token credentials from the server by
   making an authenticated (Section 3) HTTP "POST" request to the Token
   Request endpoint (unless the server advertises another HTTP request
   method for the client to use).  The client constructs a request URI
   by adding the following REQUIRED parameter to the request (in
   addition to the other protocol parameters, using the same parameter
   transmission method):

   oauth_verifier
         The verification code received from the server in the previous
         step.

   When making the request, the client authenticates using the client
   credentials as well as the temporary credentials.  The temporary
   credentials are used as a substitute for token credentials in the
   authenticated request and transmitted using the "oauth_token"
   parameter.

   Since the request results in the transmission of plain text
   credentials in the HTTP response, the server MUST require the use of
   a transport-layer mechanism such as TLS or SSL (or a secure channel
   with equivalent protections).









Hammer-Lahav                  Informational                    [Page 12]

RFC 5849                        OAuth 1.0                     April 2010


   For example, the client makes the following HTTPS request:

     POST /request_token HTTP/1.1
     Host: server.example.com
     Authorization: OAuth realm="Example",
        oauth_consumer_key="jd83jd92dhsh93js",
        oauth_token="hdk48Djdsa",
        oauth_signature_method="PLAINTEXT",
        oauth_verifier="473f82d3",
        oauth_signature="ja893SD9%26xyz4992k83j47x0b"

   The server MUST verify (Section 3.2) the validity of the request,
   ensure that the resource owner has authorized the provisioning of
   token credentials to the client, and ensure that the temporary
   credentials have not expired or been used before.  The server MUST
   also verify the verification code received from the client.  If the
   request is valid and authorized, the token credentials are included
   in the HTTP response body using the
   "application/x-www-form-urlencoded" content type as defined by
   [W3C.REC-html40-19980424] with a 200 status code (OK).

   The response contains the following REQUIRED parameters:

   oauth_token
         The token identifier.

   oauth_token_secret
         The token shared-secret.

   For example:

     HTTP/1.1 200 OK
     Content-Type: application/x-www-form-urlencoded

     oauth_token=j49ddk933skd9dks&oauth_token_secret=ll399dj47dskfjdk

   The server must retain the scope, duration, and other attributes
   approved by the resource owner, and enforce these restrictions when
   receiving a client request made with the token credentials issued.

   Once the client receives and stores the token credentials, it can
   proceed to access protected resources on behalf of the resource owner
   by making authenticated requests (Section 3) using the client
   credentials together with the token credentials received.







Hammer-Lahav                  Informational                    [Page 13]

RFC 5849                        OAuth 1.0                     April 2010


3.  Authenticated Requests

   The HTTP authentication methods defined by [RFC2617] enable clients
   to make authenticated HTTP requests.  Clients using these methods
   gain access to protected resources by using their credentials
   (typically, a username and password pair), which allow the server to
   verify their authenticity.  Using these methods for delegation
   requires the client to assume the role of the resource owner.

   OAuth provides a method designed to include two sets of credentials
   with each request, one to identify the client, and another to
   identify the resource owner.  Before a client can make authenticated
   requests on behalf of the resource owner, it must obtain a token
   authorized by the resource owner.  Section 2 provides one such method
   through which the client can obtain a token authorized by the
   resource owner.

   The client credentials take the form of a unique identifier and an
   associated shared-secret or RSA key pair.  Prior to making
   authenticated requests, the client establishes a set of credentials
   with the server.  The process and requirements for provisioning these
   are outside the scope of this specification.  Implementers are urged
   to consider the security ramifications of using client credentials,
   some of which are described in Section 4.6.

   Making authenticated requests requires prior knowledge of the
   server's configuration.  OAuth includes multiple methods for
   transmitting protocol parameters with requests (Section 3.5), as well
   as multiple methods for the client to prove its rightful ownership of
   the credentials used (Section 3.4).  The way in which clients
   discover the required configuration is outside the scope of this
   specification.

3.1.  Making Requests

   An authenticated request includes several protocol parameters.  Each
   parameter name begins with the "oauth_" prefix, and the parameter
   names and values are case sensitive.  Clients make authenticated
   requests by calculating the values of a set of protocol parameters
   and adding them to the HTTP request as follows:

   1.  The client assigns value to each of these REQUIRED (unless
       specified otherwise) protocol parameters:








Hammer-Lahav                  Informational                    [Page 14]

RFC 5849                        OAuth 1.0                     April 2010


       oauth_consumer_key
         The identifier portion of the client credentials (equivalent to
         a username).  The parameter name reflects a deprecated term
         (Consumer Key) used in previous revisions of the specification,
         and has been retained to maintain backward compatibility.

       oauth_token
         The token value used to associate the request with the resource
         owner.  If the request is not associated with a resource owner
         (no token available), clients MAY omit the parameter.

       oauth_signature_method
         The name of the signature method used by the client to sign the
         request, as defined in Section 3.4.

       oauth_timestamp
         The timestamp value as defined in Section 3.3.  The parameter
         MAY be omitted when using the "PLAINTEXT" signature method.

       oauth_nonce
         The nonce value as defined in Section 3.3.  The parameter MAY
         be omitted when using the "PLAINTEXT" signature method.

       oauth_version
         OPTIONAL.  If present, MUST be set to "1.0".  Provides the
         version of the authentication process as defined in this
         specification.

   2.  The protocol parameters are added to the request using one of the
       transmission methods listed in Section 3.5.  Each parameter MUST
       NOT appear more than once per request.

   3.  The client calculates and assigns the value of the
       "oauth_signature" parameter as described in Section 3.4 and adds
       the parameter to the request using the same method as in the
       previous step.

   4.  The client sends the authenticated HTTP request to the server.

   For example, to make the following HTTP request authenticated (the
   "c2&a3=2+q" string in the following examples is used to illustrate
   the impact of a form-encoded entity-body):

     POST /request?b5=%3D%253D&a3=a&c%40=&a2=r%20b HTTP/1.1
     Host: example.com
     Content-Type: application/x-www-form-urlencoded

     c2&a3=2+q



Hammer-Lahav                  Informational                    [Page 15]

RFC 5849                        OAuth 1.0                     April 2010


   The client assigns values to the following protocol parameters using
   its client credentials, token credentials, the current timestamp, a
   uniquely generated nonce, and indicates that it will use the
   "HMAC-SHA1" signature method:

     oauth_consumer_key:     9djdj82h48djs9d2
     oauth_token:            kkk9d7dh3k39sjv7
     oauth_signature_method: HMAC-SHA1
     oauth_timestamp:        137131201
     oauth_nonce:            7d8f3e4a

   The client adds the protocol parameters to the request using the
   OAuth HTTP "Authorization" header field:

     Authorization: OAuth realm="Example",
                    oauth_consumer_key="9djdj82h48djs9d2",
                    oauth_token="kkk9d7dh3k39sjv7",
                    oauth_signature_method="HMAC-SHA1",
                    oauth_timestamp="137131201",
                    oauth_nonce="7d8f3e4a"

   Then, it calculates the value of the "oauth_signature" parameter
   (using client secret "j49sk3j29djd" and token secret "dh893hdasih9"),
   adds it to the request, and sends the HTTP request to the server:

     POST /request?b5=%3D%253D&a3=a&c%40=&a2=r%20b HTTP/1.1
     Host: example.com
     Content-Type: application/x-www-form-urlencoded
     Authorization: OAuth realm="Example",
                    oauth_consumer_key="9djdj82h48djs9d2",
                    oauth_token="kkk9d7dh3k39sjv7",
                    oauth_signature_method="HMAC-SHA1",
                    oauth_timestamp="137131201",
                    oauth_nonce="7d8f3e4a",
                    oauth_signature="bYT5CMsGcbgUdFHObYMEfcx6bsw%3D"

     c2&a3=2+q

3.2.  Verifying Requests

   Servers receiving an authenticated request MUST validate it by:

   o  Recalculating the request signature independently as described in
      Section 3.4 and comparing it to the value received from the client
      via the "oauth_signature" parameter.






Hammer-Lahav                  Informational                    [Page 16]

RFC 5849                        OAuth 1.0                     April 2010


   o  If using the "HMAC-SHA1" or "RSA-SHA1" signature methods, ensuring
      that the combination of nonce/timestamp/token (if present)
      received from the client has not been used before in a previous
      request (the server MAY reject requests with stale timestamps as
      described in Section 3.3).

   o  If a token is present, verifying the scope and status of the
      client authorization as represented by the token (the server MAY
      choose to restrict token usage to the client to which it was
      issued).

   o  If the "oauth_version" parameter is present, ensuring its value is
      "1.0".

   If the request fails verification, the server SHOULD respond with the
   appropriate HTTP response status code.  The server MAY include
   further details about why the request was rejected in the response
   body.

   The server SHOULD return a 400 (Bad Request) status code when
   receiving a request with unsupported parameters, an unsupported
   signature method, missing parameters, or duplicated protocol
   parameters.  The server SHOULD return a 401 (Unauthorized) status
   code when receiving a request with invalid client credentials, an
   invalid or expired token, an invalid signature, or an invalid or used
   nonce.

3.3.  Nonce and Timestamp

   The timestamp value MUST be a positive integer.  Unless otherwise
   specified by the server's documentation, the timestamp is expressed
   in the number of seconds since January 1, 1970 00:00:00 GMT.

   A nonce is a random string, uniquely generated by the client to allow
   the server to verify that a request has never been made before and
   helps prevent replay attacks when requests are made over a non-secure
   channel.  The nonce value MUST be unique across all requests with the
   same timestamp, client credentials, and token combinations.

   To avoid the need to retain an infinite number of nonce values for
   future checks, servers MAY choose to restrict the time period after
   which a request with an old timestamp is rejected.  Note that this
   restriction implies a level of synchronization between the client's
   and server's clocks.  Servers applying such a restriction MAY provide
   a way for the client to sync with the server's clock; alternatively,
   both systems could synchronize with a trusted time service.  Details
   of clock synchronization strategies are beyond the scope of this
   specification.



Hammer-Lahav                  Informational                    [Page 17]

RFC 5849                        OAuth 1.0                     April 2010


3.4.  Signature

   OAuth-authenticated requests can have two sets of credentials: those
   passed via the "oauth_consumer_key" parameter and those in the
   "oauth_token" parameter.  In order for the server to verify the
   authenticity of the request and prevent unauthorized access, the
   client needs to prove that it is the rightful owner of the
   credentials.  This is accomplished using the shared-secret (or RSA
   key) part of each set of credentials.

   OAuth provides three methods for the client to prove its rightful
   ownership of the credentials: "HMAC-SHA1", "RSA-SHA1", and
   "PLAINTEXT".  These methods are generally referred to as signature
   methods, even though "PLAINTEXT" does not involve a signature.  In
   addition, "RSA-SHA1" utilizes an RSA key instead of the shared-
   secrets associated with the client credentials.

   OAuth does not mandate a particular signature method, as each
   implementation can have its own unique requirements.  Servers are
   free to implement and document their own custom methods.
   Recommending any particular method is beyond the scope of this
   specification.  Implementers should review the Security
   Considerations section (Section 4) before deciding on which method to
   support.

   The client declares which signature method is used via the
   "oauth_signature_method" parameter.  It then generates a signature
   (or a string of an equivalent value) and includes it in the
   "oauth_signature" parameter.  The server verifies the signature as
   specified for each method.

   The signature process does not change the request or its parameters,
   with the exception of the "oauth_signature" parameter.

3.4.1.  Signature Base String

   The signature base string is a consistent, reproducible concatenation
   of several of the HTTP request elements into a single string.  The
   string is used as an input to the "HMAC-SHA1" and "RSA-SHA1"
   signature methods.

   The signature base string includes the following components of the
   HTTP request:

   o  The HTTP request method (e.g., "GET", "POST", etc.).

   o  The authority as declared by the HTTP "Host" request header field.




Hammer-Lahav                  Informational                    [Page 18]

RFC 5849                        OAuth 1.0                     April 2010


   o  The path and query components of the request resource URI.

   o  The protocol parameters excluding the "oauth_signature".

   o  Parameters included in the request entity-body if they comply with
      the strict restrictions defined in Section 3.4.1.3.

   The signature base string does not cover the entire HTTP request.
   Most notably, it does not include the entity-body in most requests,
   nor does it include most HTTP entity-headers.  It is important to
   note that the server cannot verify the authenticity of the excluded
   request components without using additional protections such as SSL/
   TLS or other methods.

3.4.1.1.  String Construction

   The signature base string is constructed by concatenating together,
   in order, the following HTTP request elements:

   1.  The HTTP request method in uppercase.  For example: "HEAD",
       "GET", "POST", etc.  If the request uses a custom HTTP method, it
       MUST be encoded (Section 3.6).

   2.  An "&" character (ASCII code 38).

   3.  The base string URI from Section 3.4.1.2, after being encoded
       (Section 3.6).

   4.  An "&" character (ASCII code 38).

   5.  The request parameters as normalized in Section 3.4.1.3.2, after
       being encoded (Section 3.6).

   For example, the HTTP request:

     POST /request?b5=%3D%253D&a3=a&c%40=&a2=r%20b HTTP/1.1
     Host: example.com
     Content-Type: application/x-www-form-urlencoded
     Authorization: OAuth realm="Example",
                    oauth_consumer_key="9djdj82h48djs9d2",
                    oauth_token="kkk9d7dh3k39sjv7",
                    oauth_signature_method="HMAC-SHA1",
                    oauth_timestamp="137131201",
                    oauth_nonce="7d8f3e4a",
                    oauth_signature="bYT5CMsGcbgUdFHObYMEfcx6bsw%3D"

     c2&a3=2+q




Hammer-Lahav                  Informational                    [Page 19]

RFC 5849                        OAuth 1.0                     April 2010


   is represented by the following signature base string (line breaks
   are for display purposes only):

     POST&http%3A%2F%2Fexample.com%2Frequest&a2%3Dr%2520b%26a3%3D2%2520q
     %26a3%3Da%26b5%3D%253D%25253D%26c%2540%3D%26c2%3D%26oauth_consumer_
     key%3D9djdj82h48djs9d2%26oauth_nonce%3D7d8f3e4a%26oauth_signature_m
     ethod%3DHMAC-SHA1%26oauth_timestamp%3D137131201%26oauth_token%3Dkkk
     9d7dh3k39sjv7

3.4.1.2.  Base String URI

   The scheme, authority, and path of the request resource URI [RFC3986]
   are included by constructing an "http" or "https" URI representing
   the request resource (without the query or fragment) as follows:

   1.  The scheme and host MUST be in lowercase.

   2.  The host and port values MUST match the content of the HTTP
       request "Host" header field.

   3.  The port MUST be included if it is not the default port for the
       scheme, and MUST be excluded if it is the default.  Specifically,
       the port MUST be excluded when making an HTTP request [RFC2616]
       to port 80 or when making an HTTPS request [RFC2818] to port 443.
       All other non-default port numbers MUST be included.

   For example, the HTTP request:

     GET /r%20v/X?id=123 HTTP/1.1
     Host: EXAMPLE.COM:80

   is represented by the base string URI: "http://example.com/r%20v/X".

   In another example, the HTTPS request:

     GET /?q=1 HTTP/1.1
     Host: www.example.net:8080

   is represented by the base string URI:
   "https://www.example.net:8080/".

3.4.1.3.  Request Parameters

   In order to guarantee a consistent and reproducible representation of
   the request parameters, the parameters are collected and decoded to
   their original decoded form.  They are then sorted and encoded in a
   particular manner that is often different from their original
   encoding scheme, and concatenated into a single string.



Hammer-Lahav                  Informational                    [Page 20]

RFC 5849                        OAuth 1.0                     April 2010


3.4.1.3.1.  Parameter Sources

   The parameters from the following sources are collected into a single
   list of name/value pairs:

   o  The query component of the HTTP request URI as defined by
      [RFC3986], Section 3.4.  The query component is parsed into a list
      of name/value pairs by treating it as an
      "application/x-www-form-urlencoded" string, separating the names
      and values and decoding them as defined by
      [W3C.REC-html40-19980424], Section 17.13.4.

   o  The OAuth HTTP "Authorization" header field (Section 3.5.1) if
      present.  The header's content is parsed into a list of name/value
      pairs excluding the "realm" parameter if present.  The parameter
      values are decoded as defined by Section 3.5.1.

   o  The HTTP request entity-body, but only if all of the following
      conditions are met:

      *  The entity-body is single-part.

      *  The entity-body follows the encoding requirements of the
         "application/x-www-form-urlencoded" content-type as defined by
         [W3C.REC-html40-19980424].

      *  The HTTP request entity-header includes the "Content-Type"
         header field set to "application/x-www-form-urlencoded".

      The entity-body is parsed into a list of decoded name/value pairs
      as described in [W3C.REC-html40-19980424], Section 17.13.4.

   The "oauth_signature" parameter MUST be excluded from the signature
   base string if present.  Parameters not explicitly included in the
   request MUST be excluded from the signature base string (e.g., the
   "oauth_version" parameter when omitted).















Hammer-Lahav                  Informational                    [Page 21]

RFC 5849                        OAuth 1.0                     April 2010


   For example, the HTTP request:

       POST /request?b5=%3D%253D&a3=a&c%40=&a2=r%20b HTTP/1.1
       Host: example.com
       Content-Type: application/x-www-form-urlencoded
       Authorization: OAuth realm="Example",
                      oauth_consumer_key="9djdj82h48djs9d2",
                      oauth_token="kkk9d7dh3k39sjv7",
                      oauth_signature_method="HMAC-SHA1",
                      oauth_timestamp="137131201",
                      oauth_nonce="7d8f3e4a",
                      oauth_signature="djosJKDKJSD8743243%2Fjdk33klY%3D"

       c2&a3=2+q

   contains the following (fully decoded) parameters used in the
   signature base sting:

               +------------------------+------------------+
               |          Name          |       Value      |
               +------------------------+------------------+
               |           b5           |       =%3D       |
               |           a3           |         a        |
               |           c@           |                  |
               |           a2           |        r b       |
               |   oauth_consumer_key   | 9djdj82h48djs9d2 |
               |       oauth_token      | kkk9d7dh3k39sjv7 |
               | oauth_signature_method |     HMAC-SHA1    |
               |     oauth_timestamp    |     137131201    |
               |       oauth_nonce      |     7d8f3e4a     |
               |           c2           |                  |
               |           a3           |        2 q       |
               +------------------------+------------------+

   Note that the value of "b5" is "=%3D" and not "==".  Both "c@" and
   "c2" have empty values.  While the encoding rules specified in this
   specification for the purpose of constructing the signature base
   string exclude the use of a "+" character (ASCII code 43) to
   represent an encoded space character (ASCII code 32), this practice
   is widely used in "application/x-www-form-urlencoded" encoded values,
   and MUST be properly decoded, as demonstrated by one of the "a3"
   parameter instances (the "a3" parameter is used twice in this
   request).








Hammer-Lahav                  Informational                    [Page 22]

RFC 5849                        OAuth 1.0                     April 2010


3.4.1.3.2.  Parameters Normalization

   The parameters collected in Section 3.4.1.3 are normalized into a
   single string as follows:

   1.  First, the name and value of each parameter are encoded
       (Section 3.6).

   2.  The parameters are sorted by name, using ascending byte value
       ordering.  If two or more parameters share the same name, they
       are sorted by their value.

   3.  The name of each parameter is concatenated to its corresponding
       value using an "=" character (ASCII code 61) as a separator, even
       if the value is empty.

   4.  The sorted name/value pairs are concatenated together into a
       single string by using an "&" character (ASCII code 38) as
       separator.

   For example, the list of parameters from the previous section would
   be normalized as follows:

                                 Encoded:

               +------------------------+------------------+
               |          Name          |       Value      |
               +------------------------+------------------+
               |           b5           |     %3D%253D     |
               |           a3           |         a        |
               |          c%40          |                  |
               |           a2           |       r%20b      |
               |   oauth_consumer_key   | 9djdj82h48djs9d2 |
               |       oauth_token      | kkk9d7dh3k39sjv7 |
               | oauth_signature_method |     HMAC-SHA1    |
               |     oauth_timestamp    |     137131201    |
               |       oauth_nonce      |     7d8f3e4a     |
               |           c2           |                  |
               |           a3           |       2%20q      |
               +------------------------+------------------+











Hammer-Lahav                  Informational                    [Page 23]

RFC 5849                        OAuth 1.0                     April 2010


                                  Sorted:

               +------------------------+------------------+
               |          Name          |       Value      |
               +------------------------+------------------+
               |           a2           |       r%20b      |
               |           a3           |       2%20q      |
               |           a3           |         a        |
               |           b5           |     %3D%253D     |
               |          c%40          |                  |
               |           c2           |                  |
               |   oauth_consumer_key   | 9djdj82h48djs9d2 |
               |       oauth_nonce      |     7d8f3e4a     |
               | oauth_signature_method |     HMAC-SHA1    |
               |     oauth_timestamp    |     137131201    |
               |       oauth_token      | kkk9d7dh3k39sjv7 |
               +------------------------+------------------+

                            Concatenated Pairs:

                  +-------------------------------------+
                  |              Name=Value             |
                  +-------------------------------------+
                  |               a2=r%20b              |
                  |               a3=2%20q              |
                  |                 a3=a                |
                  |             b5=%3D%253D             |
                  |                c%40=                |
                  |                 c2=                 |
                  | oauth_consumer_key=9djdj82h48djs9d2 |
                  |         oauth_nonce=7d8f3e4a        |
                  |   oauth_signature_method=HMAC-SHA1  |
                  |      oauth_timestamp=137131201      |
                  |     oauth_token=kkk9d7dh3k39sjv7    |
                  +-------------------------------------+

   and concatenated together into a single string (line breaks are for
   display purposes only):

     a2=r%20b&a3=2%20q&a3=a&b5=%3D%253D&c%40=&c2=&oauth_consumer_key=9dj
     dj82h48djs9d2&oauth_nonce=7d8f3e4a&oauth_signature_method=HMAC-SHA1
     &oauth_timestamp=137131201&oauth_token=kkk9d7dh3k39sjv7









Hammer-Lahav                  Informational                    [Page 24]

RFC 5849                        OAuth 1.0                     April 2010


3.4.2.  HMAC-SHA1

   The "HMAC-SHA1" signature method uses the HMAC-SHA1 signature
   algorithm as defined in [RFC2104]:

     digest = HMAC-SHA1 (key, text)

   The HMAC-SHA1 function variables are used in following way:

   text    is set to the value of the signature base string from
           Section 3.4.1.1.

   key     is set to the concatenated values of:

           1.  The client shared-secret, after being encoded
               (Section 3.6).

           2.  An "&" character (ASCII code 38), which MUST be included
               even when either secret is empty.

           3.  The token shared-secret, after being encoded
               (Section 3.6).

   digest  is used to set the value of the "oauth_signature" protocol
           parameter, after the result octet string is base64-encoded
           per [RFC2045], Section 6.8.

3.4.3.  RSA-SHA1

   The "RSA-SHA1" signature method uses the RSASSA-PKCS1-v1_5 signature
   algorithm as defined in [RFC3447], Section 8.2 (also known as
   PKCS#1), using SHA-1 as the hash function for EMSA-PKCS1-v1_5.  To
   use this method, the client MUST have established client credentials
   with the server that included its RSA public key (in a manner that is
   beyond the scope of this specification).

   The signature base string is signed using the client's RSA private
   key per [RFC3447], Section 8.2.1:

     S = RSASSA-PKCS1-V1_5-SIGN (K, M)

   Where:

   K     is set to the client's RSA private key,

   M     is set to the value of the signature base string from
         Section 3.4.1.1, and




Hammer-Lahav                  Informational                    [Page 25]

RFC 5849                        OAuth 1.0                     April 2010


   S     is the result signature used to set the value of the
         "oauth_signature" protocol parameter, after the result octet
         string is base64-encoded per [RFC2045] section 6.8.

   The server verifies the signature per [RFC3447] section 8.2.2:

     RSASSA-PKCS1-V1_5-VERIFY ((n, e), M, S)

   Where:

   (n, e) is set to the client's RSA public key,

   M      is set to the value of the signature base string from
          Section 3.4.1.1, and

   S      is set to the octet string value of the "oauth_signature"
          protocol parameter received from the client.

3.4.4.  PLAINTEXT

   The "PLAINTEXT" method does not employ a signature algorithm.  It
   MUST be used with a transport-layer mechanism such as TLS or SSL (or
   sent over a secure channel with equivalent protections).  It does not
   utilize the signature base string or the "oauth_timestamp" and
   "oauth_nonce" parameters.

   The "oauth_signature" protocol parameter is set to the concatenated
   value of:

   1.  The client shared-secret, after being encoded (Section 3.6).

   2.  An "&" character (ASCII code 38), which MUST be included even
       when either secret is empty.

   3.  The token shared-secret, after being encoded (Section 3.6).

3.5.  Parameter Transmission

   When making an OAuth-authenticated request, protocol parameters as
   well as any other parameter using the "oauth_" prefix SHALL be
   included in the request using one and only one of the following
   locations, listed in order of decreasing preference:

   1.  The HTTP "Authorization" header field as described in
       Section 3.5.1.

   2.  The HTTP request entity-body as described in Section 3.5.2.




Hammer-Lahav                  Informational                    [Page 26]

RFC 5849                        OAuth 1.0                     April 2010


   3.  The HTTP request URI query as described in Section 3.5.3.

   In addition to these three methods, future extensions MAY define
   other methods for including protocol parameters in the request.

3.5.1.  Authorization Header

   Protocol parameters can be transmitted using the HTTP "Authorization"
   header field as defined by [RFC2617] with the auth-scheme name set to
   "OAuth" (case insensitive).

   For example:

     Authorization: OAuth realm="Example",
        oauth_consumer_key="0685bd9184jfhq22",
        oauth_token="ad180jjd733klru7",
        oauth_signature_method="HMAC-SHA1",
        oauth_signature="wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D",
        oauth_timestamp="137131200",
        oauth_nonce="4572616e48616d6d65724c61686176",
        oauth_version="1.0"

   Protocol parameters SHALL be included in the "Authorization" header
   field as follows:

   1.  Parameter names and values are encoded per Parameter Encoding
       (Section 3.6).

   2.  Each parameter's name is immediately followed by an "=" character
       (ASCII code 61), a """ character (ASCII code 34), the parameter
       value (MAY be empty), and another """ character (ASCII code 34).

   3.  Parameters are separated by a "," character (ASCII code 44) and
       OPTIONAL linear whitespace per [RFC2617].

   4.  The OPTIONAL "realm" parameter MAY be added and interpreted per
       [RFC2617] section 1.2.

   Servers MAY indicate their support for the "OAuth" auth-scheme by
   returning the HTTP "WWW-Authenticate" response header field upon
   client requests for protected resources.  As per [RFC2617], such a
   response MAY include additional HTTP "WWW-Authenticate" header
   fields:

   For example:

     WWW-Authenticate: OAuth realm="http://server.example.com/"




Hammer-Lahav                  Informational                    [Page 27]

RFC 5849                        OAuth 1.0                     April 2010


   The realm parameter defines a protection realm per [RFC2617], Section
   1.2.

3.5.2.  Form-Encoded Body

   Protocol parameters can be transmitted in the HTTP request entity-
   body, but only if the following REQUIRED conditions are met:

   o  The entity-body is single-part.

   o  The entity-body follows the encoding requirements of the
      "application/x-www-form-urlencoded" content-type as defined by
      [W3C.REC-html40-19980424].

   o  The HTTP request entity-header includes the "Content-Type" header
      field set to "application/x-www-form-urlencoded".

   For example (line breaks are for display purposes only):

     oauth_consumer_key=0685bd9184jfhq22&oauth_token=ad180jjd733klr
     u7&oauth_signature_method=HMAC-SHA1&oauth_signature=wOJIO9A2W5
     mFwDgiDvZbTSMK%2FPY%3D&oauth_timestamp=137131200&oauth_nonce=4
     572616e48616d6d65724c61686176&oauth_version=1.0

   The entity-body MAY include other request-specific parameters, in
   which case, the protocol parameters SHOULD be appended following the
   request-specific parameters, properly separated by an "&" character
   (ASCII code 38).

3.5.3.  Request URI Query

   Protocol parameters can be transmitted by being added to the HTTP
   request URI as a query parameter as defined by [RFC3986], Section 3.

   For example (line breaks are for display purposes only):

     GET /example/path?oauth_consumer_key=0685bd9184jfhq22&
     oauth_token=ad180jjd733klru7&oauth_signature_method=HM
     AC-SHA1&oauth_signature=wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%
     3D&oauth_timestamp=137131200&oauth_nonce=4572616e48616
     d6d65724c61686176&oauth_version=1.0 HTTP/1.1

   The request URI MAY include other request-specific query parameters,
   in which case, the protocol parameters SHOULD be appended following
   the request-specific parameters, properly separated by an "&"
   character (ASCII code 38).





Hammer-Lahav                  Informational                    [Page 28]

RFC 5849                        OAuth 1.0                     April 2010


3.6.  Percent Encoding

   Existing percent-encoding methods do not guarantee a consistent
   construction of the signature base string.  The following percent-
   encoding method is not defined to replace the existing encoding
   methods defined by [RFC3986] and [W3C.REC-html40-19980424].  It is
   used only in the construction of the signature base string and the
   "Authorization" header field.

   This specification defines the following method for percent-encoding
   strings:

   1.  Text values are first encoded as UTF-8 octets per [RFC3629] if
       they are not already.  This does not include binary values that
       are not intended for human consumption.

   2.  The values are then escaped using the [RFC3986] percent-encoding
       (%XX) mechanism as follows:

       *  Characters in the unreserved character set as defined by
          [RFC3986], Section 2.3 (ALPHA, DIGIT, "-", ".", "_", "~") MUST
          NOT be encoded.

       *  All other characters MUST be encoded.

       *  The two hexadecimal characters used to represent encoded
          characters MUST be uppercase.

   This method is different from the encoding scheme used by the
   "application/x-www-form-urlencoded" content-type (for example, it
   encodes space characters as "%20" and not using the "+" character).
   It MAY be different from the percent-encoding functions provided by
   web-development frameworks (e.g., encode different characters, use
   lowercase hexadecimal characters).

4.  Security Considerations

   As stated in [RFC2617], the greatest sources of risks are usually
   found not in the core protocol itself but in policies and procedures
   surrounding its use.  Implementers are strongly encouraged to assess
   how this protocol addresses their security requirements.

4.1.  RSA-SHA1 Signature Method

   Authenticated requests made with "RSA-SHA1" signatures do not use the
   token shared-secret, or any provisioned client shared-secret.  This
   means the request relies completely on the secrecy of the private key
   used by the client to sign requests.



Hammer-Lahav                  Informational                    [Page 29]

RFC 5849                        OAuth 1.0                     April 2010


4.2.  Confidentiality of Requests

   While this protocol provides a mechanism for verifying the integrity
   of requests, it provides no guarantee of request confidentiality.
   Unless further precautions are taken, eavesdroppers will have full
   access to request content.  Servers should carefully consider the
   kinds of data likely to be sent as part of such requests, and should
   employ transport-layer security mechanisms to protect sensitive
   resources.

4.3.  Spoofing by Counterfeit Servers

   This protocol makes no attempt to verify the authenticity of the
   server.  A hostile party could take advantage of this by intercepting
   the client's requests and returning misleading or otherwise incorrect
   responses.  Service providers should consider such attacks when
   developing services using this protocol, and should require
   transport-layer security for any requests where the authenticity of
   the server or of request responses is an issue.

4.4.  Proxying and Caching of Authenticated Content

   The HTTP Authorization scheme (Section 3.5.1) is optional.  However,
   [RFC2616] relies on the "Authorization" and "WWW-Authenticate" header
   fields to distinguish authenticated content so that it can be
   protected.  Proxies and caches, in particular, may fail to adequately
   protect requests not using these header fields.

   For example, private authenticated content may be stored in (and thus
   retrievable from) publicly accessible caches.  Servers not using the
   HTTP "Authorization" header field should take care to use other
   mechanisms, such as the "Cache-Control" header field, to ensure that
   authenticated content is protected.

4.5.  Plaintext Storage of Credentials

   The client shared-secret and token shared-secret function the same
   way passwords do in traditional authentication systems.  In order to
   compute the signatures used in methods other than "RSA-SHA1", the
   server must have access to these secrets in plaintext form.  This is
   in contrast, for example, to modern operating systems, which store
   only a one-way hash of user credentials.

   If an attacker were to gain access to these secrets -- or worse, to
   the server's database of all such secrets -- he or she would be able
   to perform any action on behalf of any resource owner.  Accordingly,
   it is critical that servers protect these secrets from unauthorized
   access.



Hammer-Lahav                  Informational                    [Page 30]

RFC 5849                        OAuth 1.0                     April 2010


4.6.  Secrecy of the Client Credentials

   In many cases, the client application will be under the control of
   potentially untrusted parties.  For example, if the client is a
   desktop application with freely available source code or an
   executable binary, an attacker may be able to download a copy for
   analysis.  In such cases, attackers will be able to recover the
   client credentials.

   Accordingly, servers should not use the client credentials alone to
   verify the identity of the client.  Where possible, other factors
   such as IP address should be used as well.

4.7.  Phishing Attacks

   Wide deployment of this and similar protocols may cause resource
   owners to become inured to the practice of being redirected to
   websites where they are asked to enter their passwords.  If resource
   owners are not careful to verify the authenticity of these websites
   before entering their credentials, it will be possible for attackers
   to exploit this practice to steal resource owners' passwords.

   Servers should attempt to educate resource owners about the risks
   phishing attacks pose, and should provide mechanisms that make it
   easy for resource owners to confirm the authenticity of their sites.
   Client developers should consider the security implications of how
   they interact with a user-agent (e.g., separate window, embedded),
   and the ability of the end-user to verify the authenticity of the
   server website.

4.8.  Scoping of Access Requests

   By itself, this protocol does not provide any method for scoping the
   access rights granted to a client.  However, most applications do
   require greater granularity of access rights.  For example, servers
   may wish to make it possible to grant access to some protected
   resources but not others, or to grant only limited access (such as
   read-only access) to those protected resources.

   When implementing this protocol, servers should consider the types of
   access resource owners may wish to grant clients, and should provide
   mechanisms to do so.  Servers should also take care to ensure that
   resource owners understand the access they are granting, as well as
   any risks that may be involved.







Hammer-Lahav                  Informational                    [Page 31]

RFC 5849                        OAuth 1.0                     April 2010


4.9.  Entropy of Secrets

   Unless a transport-layer security protocol is used, eavesdroppers
   will have full access to authenticated requests and signatures, and
   will thus be able to mount offline brute-force attacks to recover the
   credentials used.  Servers should be careful to assign shared-secrets
   that are long enough, and random enough, to resist such attacks for
   at least the length of time that the shared-secrets are valid.

   For example, if shared-secrets are valid for two weeks, servers
   should ensure that it is not possible to mount a brute force attack
   that recovers the shared-secret in less than two weeks.  Of course,
   servers are urged to err on the side of caution, and use the longest
   secrets reasonable.

   It is equally important that the pseudo-random number generator
   (PRNG) used to generate these secrets be of sufficiently high
   quality.  Many PRNG implementations generate number sequences that
   may appear to be random, but that nevertheless exhibit patterns or
   other weaknesses that make cryptanalysis or brute force attacks
   easier.  Implementers should be careful to use cryptographically
   secure PRNGs to avoid these problems.

4.10.  Denial-of-Service / Resource-Exhaustion Attacks

   This specification includes a number of features that may make
   resource exhaustion attacks against servers possible.  For example,
   this protocol requires servers to track used nonces.  If an attacker
   is able to use many nonces quickly, the resources required to track
   them may exhaust available capacity.  And again, this protocol can
   require servers to perform potentially expensive computations in
   order to verify the signature on incoming requests.  An attacker may
   exploit this to perform a denial-of-service attack by sending a large
   number of invalid requests to the server.

   Resource Exhaustion attacks are by no means specific to this
   specification.  However, implementers should be careful to consider
   the additional avenues of attack that this protocol exposes, and
   design their implementations accordingly.  For example, entropy
   starvation typically results in either a complete denial of service
   while the system waits for new entropy or else in weak (easily
   guessable) secrets.  When implementing this protocol, servers should
   consider which of these presents a more serious risk for their
   application and design accordingly.







Hammer-Lahav                  Informational                    [Page 32]

RFC 5849                        OAuth 1.0                     April 2010


4.11.  SHA-1 Cryptographic Attacks

   SHA-1, the hash algorithm used in "HMAC-SHA1" and "RSA-SHA1"
   signature methods, has been shown to have a number of cryptographic
   weaknesses that significantly reduce its resistance to collision
   attacks.  While these weaknesses do not seem to affect the use of
   SHA-1 with the Hash-based Message Authentication Code (HMAC) and
   should not affect the "HMAC-SHA1" signature method, it may affect the
   use of the "RSA-SHA1" signature method.  NIST has announced that it
   will phase out use of SHA-1 in digital signatures by 2010
   [NIST_SHA-1Comments].

   Practically speaking, these weaknesses are difficult to exploit, and
   by themselves do not pose a significant risk to users of this
   protocol.  They may, however, make more efficient attacks possible,
   and servers should take this into account when considering whether
   SHA-1 provides an adequate level of security for their applications.

4.12.  Signature Base String Limitations

   The signature base string has been designed to support the signature
   methods defined in this specification.  Those designing additional
   signature methods, should evaluated the compatibility of the
   signature base string with their security requirements.

   Since the signature base string does not cover the entire HTTP
   request, such as most request entity-body, most entity-headers, and
   the order in which parameters are sent, servers should employ
   additional mechanisms to protect such elements.

4.13.  Cross-Site Request Forgery (CSRF)

   Cross-Site Request Forgery (CSRF) is a web-based attack whereby HTTP
   requests are transmitted from a user that the website trusts or has
   authenticated.  CSRF attacks on authorization approvals can allow an
   attacker to obtain authorization to protected resources without the
   consent of the User.  Servers SHOULD strongly consider best practices
   in CSRF prevention at all the protocol authorization endpoints.

   CSRF attacks on OAuth callback URIs hosted by clients are also
   possible.  Clients should prevent CSRF attacks on OAuth callback URIs
   by verifying that the resource owner at the client site intended to
   complete the OAuth negotiation with the server.  The methods for
   preventing such CSRF attacks are beyond the scope of this
   specification.






Hammer-Lahav                  Informational                    [Page 33]

RFC 5849                        OAuth 1.0                     April 2010


4.14.  User Interface Redress

   Servers should protect the authorization process against user
   interface (UI) redress attacks (also known as "clickjacking").  As of
   the time of this writing, no complete defenses against UI redress are
   available.  Servers can mitigate the risk of UI redress attacks using
   the following techniques:

   o  JavaScript frame busting.

   o  JavaScript frame busting, and requiring that browsers have
      JavaScript enabled on the authorization page.

   o  Browser-specific anti-framing techniques.

   o  Requiring password reentry before issuing OAuth tokens.

4.15.  Automatic Processing of Repeat Authorizations

   Servers may wish to automatically process authorization requests
   (Section 2.2) from clients that have been previously authorized by
   the resource owner.  When the resource owner is redirected to the
   server to grant access, the server detects that the resource owner
   has already granted access to that particular client.  Instead of
   prompting the resource owner for approval, the server automatically
   redirects the resource owner back to the client.

   If the client credentials are compromised, automatic processing
   creates additional security risks.  An attacker can use the stolen
   client credentials to redirect the resource owner to the server with
   an authorization request.  The server will then grant access to the
   resource owner's data without the resource owner's explicit approval,
   or even awareness of an attack.  If no automatic approval is
   implemented, an attacker must use social engineering to convince the
   resource owner to approve access.

   Servers can mitigate the risks associated with automatic processing
   by limiting the scope of token credentials obtained through automated
   approvals.  Tokens credentials obtained through explicit resource
   owner consent can remain unaffected.  Clients can mitigate the risks
   associated with automatic processing by protecting their client
   credentials.









Hammer-Lahav                  Informational                    [Page 34]

RFC 5849                        OAuth 1.0                     April 2010


5.  Acknowledgments

   This specification is directly based on the OAuth Core 1.0 Revision A
   community specification, which in turn was modeled after existing
   proprietary protocols and best practices that have been independently
   implemented by various companies.

   The community specification was edited by Eran Hammer-Lahav and
   authored by: Mark Atwood, Dirk Balfanz, Darren Bounds, Richard M.
   Conlan, Blaine Cook, Leah Culver, Breno de Medeiros, Brian Eaton,
   Kellan Elliott-McCrea, Larry Halff, Eran Hammer-Lahav, Ben Laurie,
   Chris Messina, John Panzer, Sam Quigley, David Recordon, Eran
   Sandler, Jonathan Sergent, Todd Sieling, Brian Slesinsky, and Andy
   Smith.

   The editor would like to thank the following individuals for their
   invaluable contribution to the publication of this edition of the
   protocol: Lisa Dusseault, Justin Hart, Avshalom Houri, Chris Messina,
   Mark Nottingham, Tim Polk, Peter Saint-Andre, Joseph Smarr, and Paul
   Walker.































Hammer-Lahav                  Informational                    [Page 35]

RFC 5849                        OAuth 1.0                     April 2010


Appendix A.  Differences from the Community Edition

   This specification includes the following changes made to the
   original community document [OAuthCore1.0_RevisionA] in order to
   correct mistakes and omissions identified since the document was
   originally published at <http://oauth.net>.

   o  Changed using TLS/SSL when sending or requesting plain text
      credentials from SHOULD to MUST.  This change affects any use of
      the "PLAINTEXT" signature method, as well as requesting temporary
      credentials (Section 2.1) and obtaining token credentials
      (Section 2.3).

   o  Adjusted nonce language to indicate it is unique per token/
      timestamp/client combination.

   o  Removed the requirement for timestamps to be equal to or greater
      than the timestamp used in the previous request.

   o  Changed the nonce and timestamp parameters to OPTIONAL when using
      the "PLAINTEXT" signature method.

   o  Extended signature base string coverage that includes
      "application/x-www-form-urlencoded" entity-body parameters when
      the HTTP method used is other than "POST" and URI query parameters
      when the HTTP method used is other than "GET".

   o  Incorporated corrections to the instructions in each signature
      method to encode the signature value before inserting it into the
      "oauth_signature" parameter, removing errors that would have
      caused double-encoded values.

   o  Allowed omitting the "oauth_token" parameter when empty.

   o  Permitted sending requests for temporary credentials with an empty
      "oauth_token" parameter.

   o  Removed the restrictions from defining additional "oauth_"
      parameters.












Hammer-Lahav                  Informational                    [Page 36]

RFC 5849                        OAuth 1.0                     April 2010


6.  References

6.1.  Normative References

   [RFC2045]  Freed, N. and N. Borenstein, "Multipurpose Internet Mail
              Extensions (MIME) Part One: Format of Internet Message
              Bodies", RFC 2045, November 1996.

   [RFC2104]  Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed-
              Hashing for Message Authentication", RFC 2104,
              February 1997.

   [RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
              Requirement Levels", BCP 14, RFC 2119, March 1997.

   [RFC2616]  Fielding, R., Gettys, J., Mogul, J., Frystyk, H.,
              Masinter, L., Leach, P., and T. Berners-Lee, "Hypertext
              Transfer Protocol -- HTTP/1.1", RFC 2616, June 1999.

   [RFC2617]  Franks, J., Hallam-Baker, P., Hostetler, J., Lawrence, S.,
              Leach, P., Luotonen, A., and L. Stewart, "HTTP
              Authentication: Basic and Digest Access Authentication",
              RFC 2617, June 1999.

   [RFC2818]  Rescorla, E., "HTTP Over TLS", RFC 2818, May 2000.

   [RFC3447]  Jonsson, J. and B. Kaliski, "Public-Key Cryptography
              Standards (PKCS) #1: RSA Cryptography Specifications
              Version 2.1", RFC 3447, February 2003.

   [RFC3629]  Yergeau, F., "UTF-8, a transformation format of ISO
              10646", STD 63, RFC 3629, November 2003.

   [RFC3986]  Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
              Resource Identifier (URI): Generic Syntax", STD 66,
              RFC 3986, January 2005.

   [W3C.REC-html40-19980424]
              Hors, A., Raggett, D., and I. Jacobs, "HTML 4.0
              Specification", World Wide Web Consortium
              Recommendation REC-html40-19980424, April 1998,
              <http://www.w3.org/TR/1998/REC-html40-19980424>.









Hammer-Lahav                  Informational                    [Page 37]

RFC 5849                        OAuth 1.0                     April 2010


6.2.  Informative References

   [NIST_SHA-1Comments]
              Burr, W., "NIST Comments on Cryptanalytic Attacks on
              SHA-1",
              <http://csrc.nist.gov/groups/ST/hash/statement.html>.

   [OAuthCore1.0_RevisionA]
              OAuth Community, "OAuth Core 1.0 Revision A",
              <http://oauth.net/core/1.0a>.

Author's Address

   Eran Hammer-Lahav (editor)

   EMail: eran@hueniverse.com
   URI:   http://hueniverse.com


































Hammer-Lahav                  Informational                    [Page 38]

PK       ! K      oauth1-client/CONTRIBUTING.mdnu         # Contributing

Contributions are **welcome** and will be fully **credited**.

We accept contributions via Pull Requests on [Github](https://github.com/thephpleague/oauth1-client).


## Pull Requests

- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer).

- **Add tests!** - Your patch won't be accepted if it doesn't have tests.

- **Document any change in behaviour** - Make sure the README and any other relevant documentation are kept up-to-date.

- **Consider our release cycle** - We try to follow semver. Randomly breaking public APIs is not an option.

- **Create topic branches** - Don't ask us to pull from your master branch.

- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.

- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before submitting.


## Running Tests

``` bash
$ phpunit
```


**Happy coding**!
PK       ! :Hԏ      oauth1-client/.scrutinizer.ymlnu         filter:
    excluded_paths: [tests/*]
checks:
    php:
        code_rating: true
        remove_extra_empty_lines: true
        remove_php_closing_tag: true
        remove_trailing_whitespace: true
        fix_use_statements:
            remove_unused: true
            preserve_multiple: false
            preserve_blanklines: true
            order_alphabetically: true
        fix_php_opening_tag: true
        fix_linefeed: true
        fix_line_ending: true
        fix_identation_4spaces: true
        fix_doc_comments: true
tools:
    external_code_coverage:
        timeout: 600
        runs: 4
    php_analyzer: true
    php_code_coverage: false
    php_code_sniffer:
        config:
            standard: PSR2
        filter:
            paths: ['src']
    php_loc:
        enabled: true
        excluded_dirs: [vendor, tests]
    php_cpd:
        enabled: true
        excluded_dirs: [vendor, tests]
PK       ! 6`      oauth1-client/.travis.ymlnu         language: php

php:
  - 5.3
  - 5.4
  - 5.5
  - 5.6
  - 7.0
  - hhvm

before_script:
  - travis_retry composer self-update
  - travis_retry composer install --no-interaction --prefer-source --dev
  - travis_retry phpenv rehash

script:
  - ./vendor/bin/phpcs --standard=psr2 src/
  - ./vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover

after_script:
  - if [ "$TRAVIS_PHP_VERSION" != "hhvm" ] && [ "$TRAVIS_PHP_VERSION" != "7.0" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi
  - if [ "$TRAVIS_PHP_VERSION" != "hhvm" ] && [ "$TRAVIS_PHP_VERSION" != "7.0" ]; then php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi
PK       ! ȹBf  f    oauth1-client/CONDUCT.mdnu         # Contributor Code of Conduct

As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.

We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality.

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery
* Personal attacks
* Trolling or insulting/derogatory comments
* Public or private harassment
* Publishing other's private information, such as physical or electronic addresses, without explicit permission
* Other unethical or unprofessional conduct.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team.

This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community in a direct capacity. Personal views, beliefs and values of individuals do not necessarily reflect those of the organisation or affiliated individuals and organisations.

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.

This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/)
PK       !  f-<  <  (  oauth1-client/tests/TrelloServerTest.phpnu         <?php namespace League\OAuth1\Client\Tests;

use League\OAuth1\Client\Server\Trello;
use League\OAuth1\Client\Credentials\ClientCredentials;
use Mockery as m;
use PHPUnit_Framework_TestCase;

class TrelloTest extends PHPUnit_Framework_TestCase
{
    /**
     * Close mockery.
     *
     * @return void
     */
    public function tearDown()
    {
        m::close();
    }

    public function testCreatingWithArray()
    {
        $server = new Trello($this->getMockClientCredentials());

        $credentials = $server->getClientCredentials();
        $this->assertInstanceOf('League\OAuth1\Client\Credentials\ClientCredentialsInterface', $credentials);
        $this->assertEquals($this->getApplicationKey(), $credentials->getIdentifier());
        $this->assertEquals('mysecret', $credentials->getSecret());
        $this->assertEquals('http://app.dev/', $credentials->getCallbackUri());
    }

    public function testCreatingWithObject()
    {
        $credentials = new ClientCredentials;
        $credentials->setIdentifier('myidentifier');
        $credentials->setSecret('mysecret');
        $credentials->setCallbackUri('http://app.dev/');

        $server = new Trello($credentials);

        $this->assertEquals($credentials, $server->getClientCredentials());
    }

    public function testGettingTemporaryCredentials()
    {
        $server = m::mock('League\OAuth1\Client\Server\Trello[createHttpClient]', array($this->getMockClientCredentials()));

        $server->shouldReceive('createHttpClient')->andReturn($client = m::mock('stdClass'));

        $me = $this;
        $client->shouldReceive('post')->with('https://trello.com/1/OAuthGetRequestToken', m::on(function($options) use ($me) {
            $headers = $options['headers'];

            $me->assertTrue(isset($headers['Authorization']));

            // OAuth protocol specifies a strict number of
            // headers should be sent, in the correct order.
            // We'll validate that here.
            $pattern = '/OAuth oauth_consumer_key=".*?", oauth_nonce="[a-zA-Z0-9]+", oauth_signature_method="HMAC-SHA1", oauth_timestamp="\d{10}", oauth_version="1.0", oauth_callback="'.preg_quote('http%3A%2F%2Fapp.dev%2F', '/').'", oauth_signature=".*?"/';

            $matches = preg_match($pattern, $headers['Authorization']);
            $me->assertEquals(1, $matches, 'Asserting that the authorization header contains the correct expression.');

            return true;
        }))->once()->andReturn($response = m::mock('stdClass'));
        $response->shouldReceive('getBody')->andReturn('oauth_token=temporarycredentialsidentifier&oauth_token_secret=temporarycredentialssecret&oauth_callback_confirmed=true');

        $credentials = $server->getTemporaryCredentials();
        $this->assertInstanceOf('League\OAuth1\Client\Credentials\TemporaryCredentials', $credentials);
        $this->assertEquals('temporarycredentialsidentifier', $credentials->getIdentifier());
        $this->assertEquals('temporarycredentialssecret', $credentials->getSecret());
    }

    public function testGettingDefaultAuthorizationUrl()
    {
        $server = new Trello($this->getMockClientCredentials());

        $expected = 'https://trello.com/1/OAuthAuthorizeToken?response_type=fragment&scope=read&expiration=1day&oauth_token=foo';

        $this->assertEquals($expected, $server->getAuthorizationUrl('foo'));

        $credentials = m::mock('League\OAuth1\Client\Credentials\TemporaryCredentials');
        $credentials->shouldReceive('getIdentifier')->andReturn('foo');
        $this->assertEquals($expected, $server->getAuthorizationUrl($credentials));
    }

    public function testGettingAuthorizationUrlWithExpirationAfterConstructingWithExpiration()
    {
        $credentials = $this->getMockClientCredentials();
        $expiration = $this->getApplicationExpiration(2);
        $credentials['expiration'] = $expiration;
        $server = new Trello($credentials);

        $expected = 'https://trello.com/1/OAuthAuthorizeToken?response_type=fragment&scope=read&expiration='.urlencode($expiration).'&oauth_token=foo';

        $this->assertEquals($expected, $server->getAuthorizationUrl('foo'));

        $credentials = m::mock('League\OAuth1\Client\Credentials\TemporaryCredentials');
        $credentials->shouldReceive('getIdentifier')->andReturn('foo');
        $this->assertEquals($expected, $server->getAuthorizationUrl($credentials));
    }

    public function testGettingAuthorizationUrlWithExpirationAfterSettingExpiration()
    {
        $expiration = $this->getApplicationExpiration(2);
        $server = new Trello($this->getMockClientCredentials());
        $server->setApplicationExpiration($expiration);

        $expected = 'https://trello.com/1/OAuthAuthorizeToken?response_type=fragment&scope=read&expiration='.urlencode($expiration).'&oauth_token=foo';

        $this->assertEquals($expected, $server->getAuthorizationUrl('foo'));

        $credentials = m::mock('League\OAuth1\Client\Credentials\TemporaryCredentials');
        $credentials->shouldReceive('getIdentifier')->andReturn('foo');
        $this->assertEquals($expected, $server->getAuthorizationUrl($credentials));
    }

    public function testGettingAuthorizationUrlWithNameAfterConstructingWithName()
    {
        $credentials = $this->getMockClientCredentials();
        $name = $this->getApplicationName();
        $credentials['name'] = $name;
        $server = new Trello($credentials);

        $expected = 'https://trello.com/1/OAuthAuthorizeToken?response_type=fragment&scope=read&expiration=1day&name='.urlencode($name).'&oauth_token=foo';

        $this->assertEquals($expected, $server->getAuthorizationUrl('foo'));

        $credentials = m::mock('League\OAuth1\Client\Credentials\TemporaryCredentials');
        $credentials->shouldReceive('getIdentifier')->andReturn('foo');
        $this->assertEquals($expected, $server->getAuthorizationUrl($credentials));
    }

    public function testGettingAuthorizationUrlWithNameAfterSettingName()
    {
        $name = $this->getApplicationName();
        $server = new Trello($this->getMockClientCredentials());
        $server->setApplicationName($name);

        $expected = 'https://trello.com/1/OAuthAuthorizeToken?response_type=fragment&scope=read&expiration=1day&name='.urlencode($name).'&oauth_token=foo';

        $this->assertEquals($expected, $server->getAuthorizationUrl('foo'));

        $credentials = m::mock('League\OAuth1\Client\Credentials\TemporaryCredentials');
        $credentials->shouldReceive('getIdentifier')->andReturn('foo');
        $this->assertEquals($expected, $server->getAuthorizationUrl($credentials));
    }

    public function testGettingAuthorizationUrlWithScopeAfterConstructingWithScope()
    {
        $credentials = $this->getMockClientCredentials();
        $scope = $this->getApplicationScope(false);
        $credentials['scope'] = $scope;
        $server = new Trello($credentials);

        $expected = 'https://trello.com/1/OAuthAuthorizeToken?response_type=fragment&scope='.urlencode($scope).'&expiration=1day&oauth_token=foo';

        $this->assertEquals($expected, $server->getAuthorizationUrl('foo'));

        $credentials = m::mock('League\OAuth1\Client\Credentials\TemporaryCredentials');
        $credentials->shouldReceive('getIdentifier')->andReturn('foo');
        $this->assertEquals($expected, $server->getAuthorizationUrl($credentials));
    }

    public function testGettingAuthorizationUrlWithScopeAfterSettingScope()
    {
        $scope = $this->getApplicationScope(false);
        $server = new Trello($this->getMockClientCredentials());
        $server->setApplicationScope($scope);

        $expected = 'https://trello.com/1/OAuthAuthorizeToken?response_type=fragment&scope='.urlencode($scope).'&expiration=1day&oauth_token=foo';

        $this->assertEquals($expected, $server->getAuthorizationUrl('foo'));

        $credentials = m::mock('League\OAuth1\Client\Credentials\TemporaryCredentials');
        $credentials->shouldReceive('getIdentifier')->andReturn('foo');
        $this->assertEquals($expected, $server->getAuthorizationUrl($credentials));
    }

    /**
     * @expectedException InvalidArgumentException
     */
    public function testGettingTokenCredentialsFailsWithManInTheMiddle()
    {
        $server = new Trello($this->getMockClientCredentials());

        $credentials = m::mock('League\OAuth1\Client\Credentials\TemporaryCredentials');
        $credentials->shouldReceive('getIdentifier')->andReturn('foo');

        $server->getTokenCredentials($credentials, 'bar', 'verifier');
    }

    public function testGettingTokenCredentials()
    {
        $server = m::mock('League\OAuth1\Client\Server\Trello[createHttpClient]', array($this->getMockClientCredentials()));

        $temporaryCredentials = m::mock('League\OAuth1\Client\Credentials\TemporaryCredentials');
        $temporaryCredentials->shouldReceive('getIdentifier')->andReturn('temporarycredentialsidentifier');
        $temporaryCredentials->shouldReceive('getSecret')->andReturn('temporarycredentialssecret');

        $server->shouldReceive('createHttpClient')->andReturn($client = m::mock('stdClass'));

        $me = $this;
        $client->shouldReceive('post')->with('https://trello.com/1/OAuthGetAccessToken', m::on(function($options) use ($me) {
            $headers = $options['headers'];
            $body = $options['form_params'];

            $me->assertTrue(isset($headers['Authorization']));

            // OAuth protocol specifies a strict number of
            // headers should be sent, in the correct order.
            // We'll validate that here.
            $pattern = '/OAuth oauth_consumer_key=".*?", oauth_nonce="[a-zA-Z0-9]+", oauth_signature_method="HMAC-SHA1", oauth_timestamp="\d{10}", oauth_version="1.0", oauth_token="temporarycredentialsidentifier", oauth_signature=".*?"/';

            $matches = preg_match($pattern, $headers['Authorization']);
            $me->assertEquals(1, $matches, 'Asserting that the authorization header contains the correct expression.');

            $me->assertSame($body, array('oauth_verifier' => 'myverifiercode'));

            return true;
        }))->once()->andReturn($response = m::mock('stdClass'));
        $response->shouldReceive('getBody')->andReturn('oauth_token=tokencredentialsidentifier&oauth_token_secret=tokencredentialssecret');

        $credentials = $server->getTokenCredentials($temporaryCredentials, 'temporarycredentialsidentifier', 'myverifiercode');
        $this->assertInstanceOf('League\OAuth1\Client\Credentials\TokenCredentials', $credentials);
        $this->assertEquals('tokencredentialsidentifier', $credentials->getIdentifier());
        $this->assertEquals('tokencredentialssecret', $credentials->getSecret());
    }

    public function testGettingUserDetails()
    {
        $server = m::mock('League\OAuth1\Client\Server\Trello[createHttpClient,protocolHeader]', array($this->getMockClientCredentials()));

        $temporaryCredentials = m::mock('League\OAuth1\Client\Credentials\TokenCredentials');
        $temporaryCredentials->shouldReceive('getIdentifier')->andReturn('tokencredentialsidentifier');
        $temporaryCredentials->shouldReceive('getSecret')->andReturn('tokencredentialssecret');

        $server->shouldReceive('createHttpClient')->andReturn($client = m::mock('stdClass'));

        $me = $this;
        $client->shouldReceive('get')->with('https://trello.com/1/members/me?key='.$this->getApplicationKey().'&token='.$this->getAccessToken(), m::on(function($options) use ($me) {
            $headers = $options['headers'];

            $me->assertTrue(isset($headers['Authorization']));

            // OAuth protocol specifies a strict number of
            // headers should be sent, in the correct order.
            // We'll validate that here.
            $pattern = '/OAuth oauth_consumer_key=".*?", oauth_nonce="[a-zA-Z0-9]+", oauth_signature_method="HMAC-SHA1", oauth_timestamp="\d{10}", oauth_version="1.0", oauth_token="tokencredentialsidentifier", oauth_signature=".*?"/';

            $matches = preg_match($pattern, $headers['Authorization']);
            $me->assertEquals(1, $matches, 'Asserting that the authorization header contains the correct expression.');

            return true;
        }))->once()->andReturn($response = m::mock('stdClass'));
        $response->shouldReceive('getBody')->once()->andReturn($this->getUserPayload());

        $user = $server
            ->setAccessToken($this->getAccessToken())
            ->getUserDetails($temporaryCredentials);
        $this->assertInstanceOf('League\OAuth1\Client\Server\User', $user);
        $this->assertEquals('Matilda Wormwood', $user->name);
        $this->assertEquals('545df696e29c0dddaed31967', $server->getUserUid($temporaryCredentials));
        $this->assertEquals(null, $server->getUserEmail($temporaryCredentials));
        $this->assertEquals('matildawormwood12', $server->getUserScreenName($temporaryCredentials));
    }

    protected function getMockClientCredentials()
    {
        return array(
            'identifier' => $this->getApplicationKey(),
            'secret' => 'mysecret',
            'callback_uri' => 'http://app.dev/',
        );
    }

    protected function getAccessToken()
    {
        return 'lmnopqrstuvwxyz';
    }

    protected function getApplicationKey()
    {
        return 'abcdefghijk';
    }

    protected function getApplicationExpiration($days = 0)
    {
        return is_numeric($days) && $days > 0 ? $days.'day'.($days == 1 ? '' : 's') : 'never';
    }

    protected function getApplicationName()
    {
        return 'fizz buzz';
    }

    protected function getApplicationScope($readonly = true)
    {
        return $readonly ? 'read' : 'read,write';
    }

    private function getUserPayload()
    {
        return '{
            "id": "545df696e29c0dddaed31967",
            "avatarHash": null,
            "bio": "I have magical powers",
            "bioData": null,
            "confirmed": true,
            "fullName": "Matilda Wormwood",
            "idPremOrgsAdmin": [],
            "initials": "MW",
            "memberType": "normal",
            "products": [],
            "status": "idle",
            "url": "https://trello.com/matildawormwood12",
            "username": "matildawormwood12",
            "avatarSource": "none",
            "email": null,
            "gravatarHash": "39aaaada0224f26f0bb8f1965326dcb7",
            "idBoards": [
                "545df696e29c0dddaed31968",
                "545e01d6c7b2dd962b5b46cb"
            ],
            "idOrganizations": [
                "54adfd79f9aea14f84009a85",
                "54adfde13b0e706947bc4789"
            ],
            "loginTypes": null,
            "oneTimeMessagesDismissed": [],
            "prefs": {
                "sendSummaries": true,
                "minutesBetweenSummaries": 1,
                "minutesBeforeDeadlineToNotify": 1440,
                "colorBlind": false,
                "timezoneInfo": {
                    "timezoneNext": "CDT",
                    "dateNext": "2015-03-08T08:00:00.000Z",
                    "offsetNext": 300,
                    "timezoneCurrent": "CST",
                    "offsetCurrent": 360
                }
            },
            "trophies": [],
            "uploadedAvatarHash": null,
            "premiumFeatures": [],
            "idBoardsPinned": null
        }';
    }
}
PK       ! {3  3  .  oauth1-client/tests/PlainTextSignatureTest.phpnu         <?php namespace League\OAuth1\Client\Tests;
/**
 * Part of the Sentry package.
 *
 * NOTICE OF LICENSE
 *
 * Licensed under the 3-clause BSD License.
 *
 * This source file is subject to the 3-clause BSD License that is
 * bundled with this package in the LICENSE file.  It is also available at
 * the following URL: http://www.opensource.org/licenses/BSD-3-Clause
 *
 * @package    Sentry
 * @version    2.0.0
 * @author     Cartalyst LLC
 * @license    BSD License (3-clause)
 * @copyright  (c) 2011 - 2013, Cartalyst LLC
 * @link       http://cartalyst.com
 */

use League\OAuth1\Client\Signature\PlainTextSignature;
use Mockery as m;
use PHPUnit_Framework_TestCase;

class PlainTextSignatureTest extends PHPUnit_Framework_TestCase
{
    /**
     * Close mockery.
     *
     * @return void
     */
    public function tearDown()
    {
        m::close();
    }

    public function testSigningRequest()
    {
        $signature = new PlainTextSignature($this->getMockClientCredentials());
        $this->assertEquals('clientsecret&', $signature->sign($uri = 'http://www.example.com/'));

        $signature->setCredentials($this->getMockCredentials());
        $this->assertEquals('clientsecret&tokensecret', $signature->sign($uri));
        $this->assertEquals('PLAINTEXT', $signature->method());
    }

    protected function getMockClientCredentials()
    {
        $clientCredentials = m::mock('League\OAuth1\Client\Credentials\ClientCredentialsInterface');
        $clientCredentials->shouldReceive('getSecret')->andReturn('clientsecret');
        return $clientCredentials;
    }

    protected function getMockCredentials()
    {
        $credentials = m::mock('League\OAuth1\Client\Credentials\CredentialsInterface');
        $credentials->shouldReceive('getSecret')->andReturn('tokensecret');
        return $credentials;
    }
}
PK       ! 
  
  -  oauth1-client/tests/ClientCredentialsTest.phpnu         <?php namespace League\OAuth1\Client\Tests;
/**
 * Part of the Sentry package.
 *
 * NOTICE OF LICENSE
 *
 * Licensed under the 3-clause BSD License.
 *
 * This source file is subject to the 3-clause BSD License that is
 * bundled with this package in the LICENSE file.  It is also available at
 * the following URL: http://www.opensource.org/licenses/BSD-3-Clause
 *
 * @package    Sentry
 * @version    2.0.0
 * @author     Cartalyst LLC
 * @license    BSD License (3-clause)
 * @copyright  (c) 2011 - 2013, Cartalyst LLC
 * @link       http://cartalyst.com
 */

use League\OAuth1\Client\Credentials\ClientCredentials;
use Mockery as m;
use PHPUnit_Framework_TestCase;

class ClientCredentialsTest extends PHPUnit_Framework_TestCase
{
    /**
     * Close mockery.
     *
     * @return void
     */
    public function tearDown()
    {
        m::close();
    }

    public function testManipulating()
    {
        $credentials = new ClientCredentials;
        $this->assertNull($credentials->getIdentifier());
        $credentials->setIdentifier('foo');
        $this->assertEquals('foo', $credentials->getIdentifier());
        $this->assertNull($credentials->getSecret());
        $credentials->setSecret('foo');
        $this->assertEquals('foo', $credentials->getSecret());
    }
}PK       ! ba4  a4  "  oauth1-client/tests/ServerTest.phpnu         <?php namespace League\OAuth1\Client\Tests;
/**
 * Part of the Sentry package.
 *
 * NOTICE OF LICENSE
 *
 * Licensed under the 3-clause BSD License.
 *
 * This source file is subject to the 3-clause BSD License that is
 * bundled with this package in the LICENSE file.  It is also available at
 * the following URL: http://www.opensource.org/licenses/BSD-3-Clause
 *
 * @package    Sentry
 * @version    2.0.0
 * @author     Cartalyst LLC
 * @license    BSD License (3-clause)
 * @copyright  (c) 2011 - 2013, Cartalyst LLC
 * @link       http://cartalyst.com
 */

use League\OAuth1\Client\Credentials\ClientCredentials;
use Mockery as m;
use PHPUnit_Framework_TestCase;

class ServerTest extends PHPUnit_Framework_TestCase
{
    /**
     * Setup resources and dependencies.
     *
     * @return void
     */
    public static function setUpBeforeClass()
    {
        require_once __DIR__.'/stubs/ServerStub.php';
    }

    /**
     * Close mockery.
     *
     * @return void
     */
    public function tearDown()
    {
        m::close();
    }

    public function testCreatingWithArray()
    {
        $server = new ServerStub($this->getMockClientCredentials());

        $credentials = $server->getClientCredentials();
        $this->assertInstanceOf('League\OAuth1\Client\Credentials\ClientCredentialsInterface', $credentials);
        $this->assertEquals('myidentifier', $credentials->getIdentifier());
        $this->assertEquals('mysecret', $credentials->getSecret());
        $this->assertEquals('http://app.dev/', $credentials->getCallbackUri());
    }

    public function testCreatingWithObject()
    {
        $credentials = new ClientCredentials;
        $credentials->setIdentifier('myidentifier');
        $credentials->setSecret('mysecret');
        $credentials->setCallbackUri('http://app.dev/');

        $server = new ServerStub($credentials);

        $this->assertEquals($credentials, $server->getClientCredentials());
    }

    /**
     * @expectedException InvalidArgumentException
     **/
    public function testCreatingWithInvalidInput()
    {
        $server = new ServerStub(uniqid());
    }

    public function testGettingTemporaryCredentials()
    {
        $server = m::mock('League\OAuth1\Client\Tests\ServerStub[createHttpClient]', array($this->getMockClientCredentials()));

        $server->shouldReceive('createHttpClient')->andReturn($client = m::mock('stdClass'));

        $me = $this;
        $client->shouldReceive('post')->with('http://www.example.com/temporary', m::on(function($options) use ($me) {
            $headers = $options['headers'];

            $me->assertTrue(isset($headers['Authorization']));

            // OAuth protocol specifies a strict number of
            // headers should be sent, in the correct order.
            // We'll validate that here.
            $pattern = '/OAuth oauth_consumer_key=".*?", oauth_nonce="[a-zA-Z0-9]+", oauth_signature_method="HMAC-SHA1", oauth_timestamp="\d{10}", oauth_version="1.0", oauth_callback="'.preg_quote('http%3A%2F%2Fapp.dev%2F', '/').'", oauth_signature=".*?"/';

            $matches = preg_match($pattern, $headers['Authorization']);
            $me->assertEquals(1, $matches, 'Asserting that the authorization header contains the correct expression.');

            return true;
        }))->once()->andReturn($response = m::mock('stdClass'));
        $response->shouldReceive('getBody')->andReturn('oauth_token=temporarycredentialsidentifier&oauth_token_secret=temporarycredentialssecret&oauth_callback_confirmed=true');

        $credentials = $server->getTemporaryCredentials();
        $this->assertInstanceOf('League\OAuth1\Client\Credentials\TemporaryCredentials', $credentials);
        $this->assertEquals('temporarycredentialsidentifier', $credentials->getIdentifier());
        $this->assertEquals('temporarycredentialssecret', $credentials->getSecret());
    }

    public function testGettingAuthorizationUrl()
    {
        $server = new ServerStub($this->getMockClientCredentials());

        $expected = 'http://www.example.com/authorize?oauth_token=foo';

        $this->assertEquals($expected, $server->getAuthorizationUrl('foo'));

        $credentials = m::mock('League\OAuth1\Client\Credentials\TemporaryCredentials');
        $credentials->shouldReceive('getIdentifier')->andReturn('foo');
        $this->assertEquals($expected, $server->getAuthorizationUrl($credentials));
    }

    /**
     * @expectedException InvalidArgumentException
     */
    public function testGettingTokenCredentialsFailsWithManInTheMiddle()
    {
        $server = new ServerStub($this->getMockClientCredentials());

        $credentials = m::mock('League\OAuth1\Client\Credentials\TemporaryCredentials');
        $credentials->shouldReceive('getIdentifier')->andReturn('foo');

        $server->getTokenCredentials($credentials, 'bar', 'verifier');
    }

    public function testGettingTokenCredentials()
    {
        $server = m::mock('League\OAuth1\Client\Tests\ServerStub[createHttpClient]', array($this->getMockClientCredentials()));

        $temporaryCredentials = m::mock('League\OAuth1\Client\Credentials\TemporaryCredentials');
        $temporaryCredentials->shouldReceive('getIdentifier')->andReturn('temporarycredentialsidentifier');
        $temporaryCredentials->shouldReceive('getSecret')->andReturn('temporarycredentialssecret');

        $server->shouldReceive('createHttpClient')->andReturn($client = m::mock('stdClass'));

        $me = $this;
        $client->shouldReceive('post')->with('http://www.example.com/token', m::on(function($options) use ($me) {
            $headers = $options['headers'];
            $body = $options['form_params'];

            $me->assertTrue(isset($headers['Authorization']));
            $me->assertFalse(isset($headers['User-Agent']));

            // OAuth protocol specifies a strict number of
            // headers should be sent, in the correct order.
            // We'll validate that here.
            $pattern = '/OAuth oauth_consumer_key=".*?", oauth_nonce="[a-zA-Z0-9]+", oauth_signature_method="HMAC-SHA1", oauth_timestamp="\d{10}", oauth_version="1.0", oauth_token="temporarycredentialsidentifier", oauth_signature=".*?"/';

            $matches = preg_match($pattern, $headers['Authorization']);
            $me->assertEquals(1, $matches, 'Asserting that the authorization header contains the correct expression.');

            $me->assertSame($body, array('oauth_verifier' => 'myverifiercode'));

            return true;
        }))->once()->andReturn($response = m::mock('stdClass'));
        $response->shouldReceive('getBody')->andReturn('oauth_token=tokencredentialsidentifier&oauth_token_secret=tokencredentialssecret');

        $credentials = $server->getTokenCredentials($temporaryCredentials, 'temporarycredentialsidentifier', 'myverifiercode');
        $this->assertInstanceOf('League\OAuth1\Client\Credentials\TokenCredentials', $credentials);
        $this->assertEquals('tokencredentialsidentifier', $credentials->getIdentifier());
        $this->assertEquals('tokencredentialssecret', $credentials->getSecret());
    }

    public function testGettingTokenCredentialsWithUserAgent()
    {
        $userAgent = 'FooBar';
        $server = m::mock('League\OAuth1\Client\Tests\ServerStub[createHttpClient]', array($this->getMockClientCredentials()));

        $temporaryCredentials = m::mock('League\OAuth1\Client\Credentials\TemporaryCredentials');
        $temporaryCredentials->shouldReceive('getIdentifier')->andReturn('temporarycredentialsidentifier');
        $temporaryCredentials->shouldReceive('getSecret')->andReturn('temporarycredentialssecret');

        $server->shouldReceive('createHttpClient')->andReturn($client = m::mock('stdClass'));

        $me = $this;
        $client->shouldReceive('post')->with('http://www.example.com/token', m::on(function($options) use ($me, $userAgent) {
            $headers = $options['headers'];
            $body = $options['form_params'];

            $me->assertTrue(isset($headers['Authorization']));
            $me->assertTrue(isset($headers['User-Agent']));
            $me->assertEquals($userAgent, $headers['User-Agent']);

            // OAuth protocol specifies a strict number of
            // headers should be sent, in the correct order.
            // We'll validate that here.
            $pattern = '/OAuth oauth_consumer_key=".*?", oauth_nonce="[a-zA-Z0-9]+", oauth_signature_method="HMAC-SHA1", oauth_timestamp="\d{10}", oauth_version="1.0", oauth_token="temporarycredentialsidentifier", oauth_signature=".*?"/';

            $matches = preg_match($pattern, $headers['Authorization']);
            $me->assertEquals(1, $matches, 'Asserting that the authorization header contains the correct expression.');

            $me->assertSame($body, array('oauth_verifier' => 'myverifiercode'));

            return true;
        }))->once()->andReturn($response = m::mock('stdClass'));
        $response->shouldReceive('getBody')->andReturn('oauth_token=tokencredentialsidentifier&oauth_token_secret=tokencredentialssecret');

        $credentials = $server->setUserAgent($userAgent)->getTokenCredentials($temporaryCredentials, 'temporarycredentialsidentifier', 'myverifiercode');
        $this->assertInstanceOf('League\OAuth1\Client\Credentials\TokenCredentials', $credentials);
        $this->assertEquals('tokencredentialsidentifier', $credentials->getIdentifier());
        $this->assertEquals('tokencredentialssecret', $credentials->getSecret());

    }

    public function testGettingUserDetails()
    {
        $server = m::mock('League\OAuth1\Client\Tests\ServerStub[createHttpClient,protocolHeader]', array($this->getMockClientCredentials()));

        $temporaryCredentials = m::mock('League\OAuth1\Client\Credentials\TokenCredentials');
        $temporaryCredentials->shouldReceive('getIdentifier')->andReturn('tokencredentialsidentifier');
        $temporaryCredentials->shouldReceive('getSecret')->andReturn('tokencredentialssecret');

        $server->shouldReceive('createHttpClient')->andReturn($client = m::mock('stdClass'));

        $me = $this;
        $client->shouldReceive('get')->with('http://www.example.com/user', m::on(function($options) use ($me) {
            $headers = $options['headers'];

            $me->assertTrue(isset($headers['Authorization']));

            // OAuth protocol specifies a strict number of
            // headers should be sent, in the correct order.
            // We'll validate that here.
            $pattern = '/OAuth oauth_consumer_key=".*?", oauth_nonce="[a-zA-Z0-9]+", oauth_signature_method="HMAC-SHA1", oauth_timestamp="\d{10}", oauth_version="1.0", oauth_token="tokencredentialsidentifier", oauth_signature=".*?"/';

            $matches = preg_match($pattern, $headers['Authorization']);
            $me->assertEquals(1, $matches, 'Asserting that the authorization header contains the correct expression.');

            return true;
        }))->once()->andReturn($response = m::mock('stdClass'));
        $response->shouldReceive('getBody')->once()->andReturn(json_encode(array('foo' => 'bar', 'id' => 123, 'contact_email' => 'baz@qux.com', 'username' => 'fred')));

        $user = $server->getUserDetails($temporaryCredentials);
        $this->assertInstanceOf('League\OAuth1\Client\Server\User', $user);
        $this->assertEquals('bar', $user->firstName);
        $this->assertEquals(123, $server->getUserUid($temporaryCredentials));
        $this->assertEquals('baz@qux.com', $server->getUserEmail($temporaryCredentials));
        $this->assertEquals('fred', $server->getUserScreenName($temporaryCredentials));
    }

    public function testGettingHeaders()
    {
        $server = new ServerStub($this->getMockClientCredentials());

        $tokenCredentials = m::mock('League\OAuth1\Client\Credentials\TokenCredentials');
        $tokenCredentials->shouldReceive('getIdentifier')->andReturn('mock_identifier');
        $tokenCredentials->shouldReceive('getSecret')->andReturn('mock_secret');

        // OAuth protocol specifies a strict number of
        // headers should be sent, in the correct order.
        // We'll validate that here.
        $pattern = '/OAuth oauth_consumer_key=".*?", oauth_nonce="[a-zA-Z0-9]+", oauth_signature_method="HMAC-SHA1", oauth_timestamp="\d{10}", oauth_version="1.0", oauth_token="mock_identifier", oauth_signature=".*?"/';

        // With a GET request
        $headers = $server->getHeaders($tokenCredentials, 'GET', 'http://example.com/');
        $this->assertTrue(isset($headers['Authorization']));

        $matches = preg_match($pattern, $headers['Authorization']);
        $this->assertEquals(1, $matches, 'Asserting that the authorization header contains the correct expression.');

        // With a POST request
        $headers = $server->getHeaders($tokenCredentials, 'POST', 'http://example.com/', array('body' => 'params'));
        $this->assertTrue(isset($headers['Authorization']));

        $matches = preg_match($pattern, $headers['Authorization']);
        $this->assertEquals(1, $matches, 'Asserting that the authorization header contains the correct expression.');
    }

    protected function getMockClientCredentials()
    {
        return array(
            'identifier' => 'myidentifier',
            'secret' => 'mysecret',
            'callback_uri' => 'http://app.dev/',
        );
    }
}
PK       ! y,    (  oauth1-client/tests/stubs/ServerStub.phpnu         <?php

namespace League\OAuth1\Client\Tests;

use League\OAuth1\Client\Credentials\TokenCredentials;
use League\OAuth1\Client\Server\Server;
use League\OAuth1\Client\Server\User;

class ServerStub extends Server
{
    /**
     * {@inheritDoc}
     */
    public function urlTemporaryCredentials()
    {
        return 'http://www.example.com/temporary';
    }

    /**
     * {@inheritDoc}
     */
    public function urlAuthorization()
    {
        return 'http://www.example.com/authorize';
    }

    /**
     * {@inheritDoc}
     */
    public function urlTokenCredentials()
    {
        return 'http://www.example.com/token';
    }

    /**
     * {@inheritDoc}
     */
    public function urlUserDetails()
    {
        return 'http://www.example.com/user';
    }

    /**
     * {@inheritDoc}
     */
    public function userDetails($data, TokenCredentials $tokenCredentials)
    {
        $user = new User;
        $user->firstName = $data['foo'];
        return $user;
    }

    /**
     * {@inheritDoc}
     */
    public function userUid($data, TokenCredentials $tokenCredentials)
    {
        return isset($data['id']) ? $data['id'] : null;
    }

    /**
     * {@inheritDoc}
     */
    public function userEmail($data, TokenCredentials $tokenCredentials)
    {
        return isset($data['contact_email']) ? $data['contact_email'] : null;
    }

    /**
     * {@inheritDoc}
     */
    public function userScreenName($data, TokenCredentials $tokenCredentials)
    {
        return isset($data['username']) ? $data['username'] : null;
    }
}
PK       !     -  oauth1-client/tests/HmacSha1SignatureTest.phpnu         <?php namespace League\OAuth1\Client\Tests;

/**
 * Part of the Sentry package.
 *
 * NOTICE OF LICENSE
 *
 * Licensed under the 3-clause BSD License.
 *
 * This source file is subject to the 3-clause BSD License that is
 * bundled with this package in the LICENSE file.  It is also available at
 * the following URL: http://www.opensource.org/licenses/BSD-3-Clause
 *
 * @package    Sentry
 * @version    2.0.0
 * @author     Cartalyst LLC
 * @license    BSD License (3-clause)
 * @copyright  (c) 2011 - 2013, Cartalyst LLC
 * @link       http://cartalyst.com
 */

use League\OAuth1\Client\Signature\HmacSha1Signature;
use Mockery as m;
use PHPUnit_Framework_TestCase;

class HmacSha1SignatureTest extends PHPUnit_Framework_TestCase
{
    /**
     * Close mockery.
     *
     * @return void
     */
    public function tearDown()
    {
        m::close();
    }

    public function testSigningRequest()
    {
        $signature = new HmacSha1Signature($this->getMockClientCredentials());

        $uri = 'http://www.example.com/?qux=corge';
        $parameters = array('foo' => 'bar', 'baz' => null);

        $this->assertEquals('A3Y7C1SUHXR1EBYIUlT3d6QT1cQ=', $signature->sign($uri, $parameters));
    }

    public function testQueryStringFromArray()
    {
        $array = array('a' => 'b');
        $res = $this->invokeQueryStringFromData($array);

        $this->assertSame(
            'a%3Db',
            $res
        );
    }

    public function testQueryStringFromIndexedArray()
    {
        $array = array('a', 'b');
        $res = $this->invokeQueryStringFromData($array);

        $this->assertSame(
            '0%3Da%261%3Db',
            $res
        );
    }

    public function testQueryStringFromMultiDimensionalArray()
    {
        $array = array(
            'a' => array(
                'b' => array(
                    'c' => 'd',
                ),
                'e' => array(
                    'f' => 'g',
                ),
            ),
            'h' => 'i',
            'empty' => '',
            'null' => null,
            'false' => false,
        );

        // Convert to query string.
        $res = $this->invokeQueryStringFromData($array);

        $this->assertSame(
            'a%5Bb%5D%5Bc%5D%3Dd%26a%5Be%5D%5Bf%5D%3Dg%26h%3Di%26empty%3D%26null%3D%26false%3D',
            $res
        );

        // Reverse engineer the string.
        $res = urldecode($res);

        $this->assertSame(
            'a[b][c]=d&a[e][f]=g&h=i&empty=&null=&false=',
            $res
        );

        // Finally, parse the string back to an array.
        parse_str($res, $original_array);

        // And ensure it matches the orignal array (approximately).
        $this->assertSame(
            array(
                'a' => array(
                    'b' => array(
                        'c' => 'd',
                    ),
                    'e' => array(
                        'f' => 'g',
                    ),
                ),
                'h' => 'i',
                'empty' => '',
                'null' => '', // null value gets lost in string translation
                'false' => '', // false value gets lost in string translation
            ),
            $original_array
        );
    }

    public function testSigningRequestWithMultiDimensionalParams()
    {
        $signature = new HmacSha1Signature($this->getMockClientCredentials());

        $uri = 'http://www.example.com/';
        $parameters = array(
            'a' => array(
                'b' => array(
                    'c' => 'd',
                ),
                'e' => array(
                    'f' => 'g',
                ),
            ),
            'h' => 'i',
            'empty' => '',
            'null' => null,
            'false' => false,
        );

        $this->assertEquals('ZUxiJKugeEplaZm9e4hshN0I70U=', $signature->sign($uri, $parameters));
    }

    protected function invokeQueryStringFromData(array $args)
    {
        $signature = new HmacSha1Signature(m::mock('League\OAuth1\Client\Credentials\ClientCredentialsInterface'));
        $refl = new \ReflectionObject($signature);
        $method = $refl->getMethod('queryStringFromData');
        $method->setAccessible(true);
        return $method->invokeArgs($signature, array($args));
    }

    protected function getMockClientCredentials()
    {
        $clientCredentials = m::mock('League\OAuth1\Client\Credentials\ClientCredentialsInterface');
        $clientCredentials->shouldReceive('getSecret')->andReturn('clientsecret');
        return $clientCredentials;
    }
}
PK       ! )  )  &  oauth1-client/tests/XingServerTest.phpnu         <?php namespace League\OAuth1\Client\Tests;

use League\OAuth1\Client\Server\Xing;
use League\OAuth1\Client\Credentials\ClientCredentials;
use Mockery as m;
use PHPUnit_Framework_TestCase;

class XingTest extends PHPUnit_Framework_TestCase
{
    /**
     * Close mockery.
     *
     * @return void
     */
    public function tearDown()
    {
        m::close();
    }

    public function testCreatingWithArray()
    {
        $server = new Xing($this->getMockClientCredentials());

        $credentials = $server->getClientCredentials();
        $this->assertInstanceOf('League\OAuth1\Client\Credentials\ClientCredentialsInterface', $credentials);
        $this->assertEquals($this->getApplicationKey(), $credentials->getIdentifier());
        $this->assertEquals('mysecret', $credentials->getSecret());
        $this->assertEquals('http://app.dev/', $credentials->getCallbackUri());
    }

    public function testCreatingWithObject()
    {
        $credentials = new ClientCredentials;
        $credentials->setIdentifier('myidentifier');
        $credentials->setSecret('mysecret');
        $credentials->setCallbackUri('http://app.dev/');

        $server = new Xing($credentials);

        $this->assertEquals($credentials, $server->getClientCredentials());
    }

    public function testGettingTemporaryCredentials()
    {
        $server = m::mock('League\OAuth1\Client\Server\Xing[createHttpClient]', array($this->getMockClientCredentials()));

        $server->shouldReceive('createHttpClient')->andReturn($client = m::mock('stdClass'));

        $me = $this;
        $client->shouldReceive('post')->with('https://api.xing.com/v1/request_token', m::on(function ($options) use ($me) {
            $headers = $options['headers'];
            $me->assertTrue(isset($headers['Authorization']));

            // OAuth protocol specifies a strict number of
            // headers should be sent, in the correct order.
            // We'll validate that here.
            $pattern = '/OAuth oauth_consumer_key=".*?", oauth_nonce="[a-zA-Z0-9]+", oauth_signature_method="HMAC-SHA1", oauth_timestamp="\d{10}", oauth_version="1.0", oauth_callback="'.preg_quote('http%3A%2F%2Fapp.dev%2F', '/').'", oauth_signature=".*?"/';

            $matches = preg_match($pattern, $headers['Authorization']);
            $me->assertEquals(1, $matches, 'Asserting that the authorization header contains the correct expression.');

            return true;
        }))->once()->andReturn($response = m::mock('stdClass'));
        $response->shouldReceive('getBody')->andReturn('oauth_token=temporarycredentialsidentifier&oauth_token_secret=temporarycredentialssecret&oauth_callback_confirmed=true');

        $credentials = $server->getTemporaryCredentials();
        $this->assertInstanceOf('League\OAuth1\Client\Credentials\TemporaryCredentials', $credentials);
        $this->assertEquals('temporarycredentialsidentifier', $credentials->getIdentifier());
        $this->assertEquals('temporarycredentialssecret', $credentials->getSecret());
    }

    public function testGettingDefaultAuthorizationUrl()
    {
        $server = new Xing($this->getMockClientCredentials());

        $expected = 'https://api.xing.com/v1/authorize?oauth_token=foo';

        $this->assertEquals($expected, $server->getAuthorizationUrl('foo'));

        $credentials = m::mock('League\OAuth1\Client\Credentials\TemporaryCredentials');
        $credentials->shouldReceive('getIdentifier')->andReturn('foo');
        $this->assertEquals($expected, $server->getAuthorizationUrl($credentials));
    }

    /**
     * @expectedException InvalidArgumentException
     */
    public function testGettingTokenCredentialsFailsWithManInTheMiddle()
    {
        $server = new Xing($this->getMockClientCredentials());

        $credentials = m::mock('League\OAuth1\Client\Credentials\TemporaryCredentials');
        $credentials->shouldReceive('getIdentifier')->andReturn('foo');

        $server->getTokenCredentials($credentials, 'bar', 'verifier');
    }

    public function testGettingTokenCredentials()
    {
        $server = m::mock('League\OAuth1\Client\Server\Xing[createHttpClient]', array($this->getMockClientCredentials()));

        $temporaryCredentials = m::mock('League\OAuth1\Client\Credentials\TemporaryCredentials');
        $temporaryCredentials->shouldReceive('getIdentifier')->andReturn('temporarycredentialsidentifier');
        $temporaryCredentials->shouldReceive('getSecret')->andReturn('temporarycredentialssecret');

        $server->shouldReceive('createHttpClient')->andReturn($client = m::mock('stdClass'));

        $me = $this;
        $client->shouldReceive('post')->with('https://api.xing.com/v1/access_token', m::on(function ($options) use ($me) {
            $headers = $options['headers'];
            $body = $options['form_params'];

            $me->assertTrue(isset($headers['Authorization']));

            // OAuth protocol specifies a strict number of
            // headers should be sent, in the correct order.
            // We'll validate that here.
            $pattern = '/OAuth oauth_consumer_key=".*?", oauth_nonce="[a-zA-Z0-9]+", oauth_signature_method="HMAC-SHA1", oauth_timestamp="\d{10}", oauth_version="1.0", oauth_token="temporarycredentialsidentifier", oauth_signature=".*?"/';

            $matches = preg_match($pattern, $headers['Authorization']);
            $me->assertEquals(1, $matches, 'Asserting that the authorization header contains the correct expression.');

            $me->assertSame($body, array('oauth_verifier' => 'myverifiercode'));

            return true;
        }))->once()->andReturn($response = m::mock('stdClass'));
        $response->shouldReceive('getBody')->andReturn('oauth_token=tokencredentialsidentifier&oauth_token_secret=tokencredentialssecret');

        $credentials = $server->getTokenCredentials($temporaryCredentials, 'temporarycredentialsidentifier', 'myverifiercode');
        $this->assertInstanceOf('League\OAuth1\Client\Credentials\TokenCredentials', $credentials);
        $this->assertEquals('tokencredentialsidentifier', $credentials->getIdentifier());
        $this->assertEquals('tokencredentialssecret', $credentials->getSecret());
    }

    public function testGettingUserDetails()
    {
        $server = m::mock('League\OAuth1\Client\Server\Xing[createHttpClient,protocolHeader]', array($this->getMockClientCredentials()));

        $temporaryCredentials = m::mock('League\OAuth1\Client\Credentials\TokenCredentials');
        $temporaryCredentials->shouldReceive('getIdentifier')->andReturn('tokencredentialsidentifier');
        $temporaryCredentials->shouldReceive('getSecret')->andReturn('tokencredentialssecret');

        $server->shouldReceive('createHttpClient')->andReturn($client = m::mock('stdClass'));

        $me = $this;
        $client->shouldReceive('get')->with('https://api.xing.com/v1/users/me', m::on(function ($options) use ($me) {
            $headers = $options['headers'];

            $me->assertTrue(isset($headers['Authorization']));

            // OAuth protocol specifies a strict number of
            // headers should be sent, in the correct order.
            // We'll validate that here.
            $pattern = '/OAuth oauth_consumer_key=".*?", oauth_nonce="[a-zA-Z0-9]+", oauth_signature_method="HMAC-SHA1", oauth_timestamp="\d{10}", oauth_version="1.0", oauth_token="tokencredentialsidentifier", oauth_signature=".*?"/';

            $matches = preg_match($pattern, $headers['Authorization']);
            $me->assertEquals(1, $matches, 'Asserting that the authorization header contains the correct expression.');

            return true;
        }))->once()->andReturn($response = m::mock('stdClass'));
        $response->shouldReceive('getBody')->once()->andReturn($this->getUserPayload());

        $user = $server->getUserDetails($temporaryCredentials);
        $this->assertInstanceOf('League\OAuth1\Client\Server\User', $user);
        $this->assertEquals('Roman Gelembjuk', $user->name);
        $this->assertEquals('17144430_0f9409', $server->getUserUid($temporaryCredentials));
        $this->assertEquals('XXXXXXXXXX@gmail.com', $server->getUserEmail($temporaryCredentials));
        $this->assertEquals('Roman Gelembjuk', $server->getUserScreenName($temporaryCredentials));
    }

    protected function getMockClientCredentials()
    {
        return array(
            'identifier' => $this->getApplicationKey(),
            'secret' => 'mysecret',
            'callback_uri' => 'http://app.dev/',
        );
    }

    protected function getApplicationKey()
    {
        return 'abcdefghijk';
    }

    protected function getApplicationExpiration($days = 0)
    {
        return is_numeric($days) && $days > 0 ? $days.'day'.($days == 1 ? '' : 's') : 'never';
    }

    protected function getApplicationName()
    {
        return 'fizz buzz';
    }

    private function getUserPayload()
    {
        return '{
		"users":[
			{
			"id":"17144430_0f9409",
			"active_email":"XXXXXXXXXX@gmail.com",
			"time_zone":
				{
				"utc_offset":3.0,
				"name":"Europe/Kiev"
				},
			"display_name":"Roman Gelembjuk",
			"first_name":"Roman",
			"last_name":"Gelembjuk",
			"gender":"m",
			"page_name":"Roman_Gelembjuk",
			"birth_date":
				{"year":null,"month":null,"day":null},
			"wants":null,
			"haves":null,
			"interests":null,
			"web_profiles":{},
			"badges":[],
			"photo_urls":
				{
				"large":"https://x1.xingassets.com/assets/frontend_minified/img/users/nobody_m.140x185.jpg",
				"maxi_thumb":"https://x1.xingassets.com/assets/frontend_minified/img/users/nobody_m.70x93.jpg",
				"medium_thumb":"https://x1.xingassets.com/assets/frontend_minified/img/users/nobody_m.57x75.jpg"
				},
			"permalink":"https://www.xing.com/profile/Roman_Gelembjuk",
			"languages":{"en":null},
			"employment_status":"EMPLOYEE",
			"organisation_member":null,
			"instant_messaging_accounts":{},
			"educational_background":
				{"degree":null,"primary_school":null,"schools":[],"qualifications":[]},
			"private_address":{
				"street":null,
				"zip_code":null,
				"city":null,
				"province":null,
				"country":null,
				"email":"XXXXXXXX@gmail.com",
				"fax":null,
				"phone":null,
				"mobile_phone":null}
			,"business_address":
				{
					"street":null,
					"zip_code":null,
					"city":"Ivano-Frankivsk",
					"province":null,
					"country":"UA",
					"email":null,
					"fax":null,"phone":null,"mobile_phone":null
				},
			"premium_services":[]
			}]}';
    }
}
PK       ! H    )  oauth1-client/resources/examples/xing.phpnu         <?php

require_once __DIR__.'/../../vendor/autoload.php';

// Create server
$server = new League\OAuth1\Client\Server\Xing(array(
    'identifier' => 'your-identifier',
    'secret' => 'your-secret',
    'callback_uri' => "http://your-callback-uri/",
));

// Start session
session_start();

// Step 4
if (isset($_GET['user'])) {

    // Check somebody hasn't manually entered this URL in,
    // by checking that we have the token credentials in
    // the session.
    if ( ! isset($_SESSION['token_credentials'])) {
        echo 'No token credentials.';
        exit(1);
    }

    // Retrieve our token credentials. From here, it's play time!
    $tokenCredentials = unserialize($_SESSION['token_credentials']);

    // // Below is an example of retrieving the identifier & secret
    // // (formally known as access token key & secret in earlier
    // // OAuth 1.0 specs).
    // $identifier = $tokenCredentials->getIdentifier();
    // $secret = $tokenCredentials->getSecret();

    // Some OAuth clients try to act as an API wrapper for
    // the server and it's API. We don't. This is what you
    // get - the ability to access basic information. If
    // you want to get fancy, you should be grabbing a
    // package for interacting with the APIs, by using
    // the identifier & secret that this package was
    // designed to retrieve for you. But, for fun,
    // here's basic user information.
    $user = $server->getUserDetails($tokenCredentials);
    var_dump($user);

// Step 3
} elseif (isset($_GET['oauth_token']) && isset($_GET['oauth_verifier'])) {

    // Retrieve the temporary credentials from step 2
    $temporaryCredentials = unserialize($_SESSION['temporary_credentials']);

    // Third and final part to OAuth 1.0 authentication is to retrieve token
    // credentials (formally known as access tokens in earlier OAuth 1.0
    // specs).
    $tokenCredentials = $server->getTokenCredentials($temporaryCredentials, $_GET['oauth_token'], $_GET['oauth_verifier']);

    // Now, we'll store the token credentials and discard the temporary
    // ones - they're irrelevant at this stage.
    unset($_SESSION['temporary_credentials']);
    $_SESSION['token_credentials'] = serialize($tokenCredentials);
    session_write_close();

    // Redirect to the user page
    header("Location: http://{$_SERVER['HTTP_HOST']}/?user=user");
    exit;

// Step 2.5 - denied request to authorize client
} elseif (isset($_GET['denied'])) {
    echo 'Hey! You denied the client access to your Xing account! If you did this by mistake, you should <a href="?go=go">try again</a>.';

// Step 2
} elseif (isset($_GET['go'])) {

    // First part of OAuth 1.0 authentication is retrieving temporary credentials.
    // These identify you as a client to the server.
    $temporaryCredentials = $server->getTemporaryCredentials();

    // Store the credentials in the session.
    $_SESSION['temporary_credentials'] = serialize($temporaryCredentials);
    session_write_close();

    // Second part of OAuth 1.0 authentication is to redirect the
    // resource owner to the login screen on the server.
    $server->authorize($temporaryCredentials);

// Step 1
} else {

    // Display link to start process
    echo '<a href="?go=go">Login</a>';
}
PK       ! XyB    ,  oauth1-client/resources/examples/twitter.phpnu         <?php

require_once __DIR__.'/../../vendor/autoload.php';

// Create server
$server = new League\OAuth1\Client\Server\Twitter(array(
    'identifier' => 'your-identifier',
    'secret' => 'your-secret',
    'callback_uri' => "http://your-callback-uri/",
));

// Start session
session_start();

// Step 4
if (isset($_GET['user'])) {

    // Check somebody hasn't manually entered this URL in,
    // by checking that we have the token credentials in
    // the session.
    if ( ! isset($_SESSION['token_credentials'])) {
        echo 'No token credentials.';
        exit(1);
    }

    // Retrieve our token credentials. From here, it's play time!
    $tokenCredentials = unserialize($_SESSION['token_credentials']);

    // // Below is an example of retrieving the identifier & secret
    // // (formally known as access token key & secret in earlier
    // // OAuth 1.0 specs).
    // $identifier = $tokenCredentials->getIdentifier();
    // $secret = $tokenCredentials->getSecret();

    // Some OAuth clients try to act as an API wrapper for
    // the server and it's API. We don't. This is what you
    // get - the ability to access basic information. If
    // you want to get fancy, you should be grabbing a
    // package for interacting with the APIs, by using
    // the identifier & secret that this package was
    // designed to retrieve for you. But, for fun,
    // here's basic user information.
    $user = $server->getUserDetails($tokenCredentials);
    var_dump($user);

// Step 3
} elseif (isset($_GET['oauth_token']) && isset($_GET['oauth_verifier'])) {

    // Retrieve the temporary credentials from step 2
    $temporaryCredentials = unserialize($_SESSION['temporary_credentials']);

    // Third and final part to OAuth 1.0 authentication is to retrieve token
    // credentials (formally known as access tokens in earlier OAuth 1.0
    // specs).
    $tokenCredentials = $server->getTokenCredentials($temporaryCredentials, $_GET['oauth_token'], $_GET['oauth_verifier']);

    // Now, we'll store the token credentials and discard the temporary
    // ones - they're irrelevant at this stage.
    unset($_SESSION['temporary_credentials']);
    $_SESSION['token_credentials'] = serialize($tokenCredentials);
    session_write_close();

    // Redirect to the user page
    header("Location: http://{$_SERVER['HTTP_HOST']}/?user=user");
    exit;

// Step 2.5 - denied request to authorize client
} elseif (isset($_GET['denied'])) {
    echo 'Hey! You denied the client access to your Twitter account! If you did this by mistake, you should <a href="?go=go">try again</a>.';

// Step 2
} elseif (isset($_GET['go'])) {

    // First part of OAuth 1.0 authentication is retrieving temporary credentials.
    // These identify you as a client to the server.
    $temporaryCredentials = $server->getTemporaryCredentials();

    // Store the credentials in the session.
    $_SESSION['temporary_credentials'] = serialize($temporaryCredentials);
    session_write_close();

    // Second part of OAuth 1.0 authentication is to redirect the
    // resource owner to the login screen on the server.
    $server->authorize($temporaryCredentials);

// Step 1
} else {

    // Display link to start process
    echo '<a href="?go=go">Login</a>';
}
PK       ! A    +  oauth1-client/resources/examples/tumblr.phpnu         <?php

require_once __DIR__.'/../../vendor/autoload.php';

// Create server
$server = new League\OAuth1\Client\Server\Tumblr(array(
    'identifier' => 'your-identifier',
    'secret' => 'your-secret',
    'callback_uri' => "http://your-callback-uri/",
));

// Start session
session_start();

// Step 4
if (isset($_GET['user'])) {

    // Check somebody hasn't manually entered this URL in,
    // by checking that we have the token credentials in
    // the session.
    if ( ! isset($_SESSION['token_credentials'])) {
        echo 'No token credentials.';
        exit(1);
    }

    // Retrieve our token credentials. From here, it's play time!
    $tokenCredentials = unserialize($_SESSION['token_credentials']);

    // // Below is an example of retrieving the identifier & secret
    // // (formally known as access token key & secret in earlier
    // // OAuth 1.0 specs).
    // $identifier = $tokenCredentials->getIdentifier();
    // $secret = $tokenCredentials->getSecret();

    // Some OAuth clients try to act as an API wrapper for
    // the server and it's API. We don't. This is what you
    // get - the ability to access basic information. If
    // you want to get fancy, you should be grabbing a
    // package for interacting with the APIs, by using
    // the identifier & secret that this package was
    // designed to retrieve for you. But, for fun,
    // here's basic user information.
    $user = $server->getUserDetails($tokenCredentials);
    var_dump($user);

// Step 3
} elseif (isset($_GET['oauth_token']) && isset($_GET['oauth_verifier'])) {

    // Retrieve the temporary credentials from step 2
    $temporaryCredentials = unserialize($_SESSION['temporary_credentials']);

    // Third and final part to OAuth 1.0 authentication is to retrieve token
    // credentials (formally known as access tokens in earlier OAuth 1.0
    // specs).
    $tokenCredentials = $server->getTokenCredentials($temporaryCredentials, $_GET['oauth_token'], $_GET['oauth_verifier']);

    // Now, we'll store the token credentials and discard the temporary
    // ones - they're irrelevant at this stage.
    unset($_SESSION['temporary_credentials']);
    $_SESSION['token_credentials'] = serialize($tokenCredentials);
    session_write_close();

    // Redirect to the user page
    header("Location: http://{$_SERVER['HTTP_HOST']}/?user=user");
    exit;

// Step 2
} elseif (isset($_GET['go'])) {

    // First part of OAuth 1.0 authentication is retrieving temporary credentials.
    // These identify you as a client to the server.
    $temporaryCredentials = $server->getTemporaryCredentials();

    // Store the credentials in the session.
    $_SESSION['temporary_credentials'] = serialize($temporaryCredentials);
    session_write_close();

    // Second part of OAuth 1.0 authentication is to redirect the
    // resource owner to the login screen on the server.
    $server->authorize($temporaryCredentials);

// Step 1
} else {

    // Display link to start process
    echo '<a href="?go=go">Login</a>';
}
PK       ! <zkJ  J    oauth1-client/LICENSEnu         The MIT License (MIT)

Copyright (c) 2013 Ben Corlett <bencorlett@me.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
PK       ! {)  )    oauth1-client/README.mdnu         # OAuth 1.0 Client

[![Latest Stable Version](https://img.shields.io/github/release/thephpleague/oauth1-client.svg?style=flat-square)](https://github.com/thephpleague/oauth1-client/releases)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)
[![Build Status](https://img.shields.io/travis/thephpleague/oauth1-client/master.svg?style=flat-square&1)](https://travis-ci.org/thephpleague/oauth1-client)
[![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/thephpleague/oauth1-client.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/oauth1-client/code-structure)
[![Quality Score](https://img.shields.io/scrutinizer/g/thephpleague/oauth1-client.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/oauth1-client)
[![Total Downloads](https://img.shields.io/packagist/dt/league/oauth1-client.svg?style=flat-square)](https://packagist.org/packages/thephpleague/oauth1-client)

OAuth 1 Client is an OAuth [RFC 5849 standards-compliant](http://tools.ietf.org/html/rfc5849) library for authenticating against OAuth 1 servers.

It has built in support for:

- Bitbucket
- Trello
- Tumblr
- Twitter
- Xing

Adding support for other providers is trivial. The library requires PHP 5.3+ and is PSR-2 compatible.

### Third-Party Providers

If you would like to support other providers, please make them available as a Composer package, then link to them
below.

These providers allow integration with other providers not supported by `oauth1-client`. They may require an older version
so please help them out with a pull request if you notice this.

- [Intuit](https://packagist.org/packages/wheniwork/oauth1-intuit)
- [500px](https://packagist.org/packages/mechant/oauth1-500px)
- [Etsy](https://packagist.org/packages/y0lk/oauth1-etsy)
- [Xero](https://packagist.org/packages/Invoiced/oauth1-xero)

#### Terminology (as per the RFC 5849 specification):

    client
        An HTTP client (per [RFC2616]) capable of making OAuth-
        authenticated requests (Section 3).

    server
        An HTTP server (per [RFC2616]) capable of accepting OAuth-
        authenticated requests (Section 3).

    protected resource
        An access-restricted resource that can be obtained from the
        server using an OAuth-authenticated request (Section 3).

    resource owner
        An entity capable of accessing and controlling protected
        resources by using credentials to authenticate with the server.

    credentials
        Credentials are a pair of a unique identifier and a matching
        shared secret.  OAuth defines three classes of credentials:
        client, temporary, and token, used to identify and authenticate
        the client making the request, the authorization request, and
        the access grant, respectively.

    token
        A unique identifier issued by the server and used by the client
        to associate authenticated requests with the resource owner
        whose authorization is requested or has been obtained by the
        client.  Tokens have a matching shared-secret that is used by
        the client to establish its ownership of the token, and its
        authority to represent the resource owner.

    The original community specification used a somewhat different
    terminology that maps to this specifications as follows (original
    community terms provided on left):

    Consumer:  client

    Service Provider:  server

    User:  resource owner

    Consumer Key and Secret:  client credentials

    Request Token and Secret:  temporary credentials

    Access Token and Secret:  token credentials


## Install

Via Composer

```shell
$ composer require league/oauth1-client
```


## Usage

### Bitbucket

```php
$server = new League\OAuth1\Client\Server\Bitbucket(array(
    'identifier' => 'your-identifier',
    'secret' => 'your-secret',
    'callback_uri' => "http://your-callback-uri/",
));
```

### Trello

```php
$server =  new League\OAuth1\Client\Server\Trello(array(
    'identifier' => 'your-identifier',
    'secret' => 'your-secret',
    'callback_uri' => 'http://your-callback-uri/',
    'name' => 'your-application-name', // optional, defaults to null
    'expiration' => 'your-application-expiration', // optional ('never', '1day', '2days'), defaults to '1day'
    'scope' => 'your-application-scope' // optional ('read', 'read,write'), defaults to 'read'
));
```

### Tumblr

```php
$server = new League\OAuth1\Client\Server\Tumblr(array(
    'identifier' => 'your-identifier',
    'secret' => 'your-secret',
    'callback_uri' => "http://your-callback-uri/",
));
```

### Twitter

```php
$server = new League\OAuth1\Client\Server\Twitter(array(
    'identifier' => 'your-identifier',
    'secret' => 'your-secret',
    'callback_uri' => "http://your-callback-uri/",
));
```

### Xing

```php
$server = new League\OAuth1\Client\Server\Xing(array(
    'identifier' => 'your-consumer-key',
    'secret' => 'your-consumer-secret',
    'callback_uri' => "http://your-callback-uri/",
));
```

### Showing a Login Button

To begin, it's advisable that you include a login button on your website. Most servers (Twitter, Tumblr etc) have resources available for making buttons that are familiar to users. Some servers actually require you use their buttons as part of their terms.

```html
<a href="authenticate.php">Login With Twitter</a>
```

### Retrieving Temporary Credentials

The first step to authenticating with OAuth 1 is to retrieve temporary credentials. These have been referred to as **request tokens** in earlier versions of OAuth 1.

To do this, we'll retrieve and store temporary credentials in the session, and redirect the user to the server:

```php
// Retrieve temporary credentials
$temporaryCredentials = $server->getTemporaryCredentials();

// Store credentials in the session, we'll need them later
$_SESSION['temporary_credentials'] = serialize($temporaryCredentials);
session_write_close();

// Second part of OAuth 1.0 authentication is to redirect the
// resource owner to the login screen on the server.
$server->authorize($temporaryCredentials);
```

The user will be redirected to the familiar login screen on the server, where they will login to their account and authorise your app to access their data.

### Retrieving Token Credentials

Once the user has authenticated (or denied) your application, they will be redirected to the `callback_uri` which you specified when creating the server.

> Note, some servers (such as Twitter) require that the callback URI you specify when authenticating matches what you registered with their app. This is to stop a potential third party impersonating you. This is actually part of the protocol however some servers choose to ignore this.
>
> Because of this, we actually require you specify a callback URI for all servers, regardless of whether the server requires it or not. This is good practice.

You'll need to handle when the user is redirected back. This will involve retrieving token credentials, which you may then use to make calls to the server on behalf of the user. These have been referred to as **access tokens** in earlier versions of OAuth 1.

```php
if (isset($_GET['oauth_token']) && isset($_GET['oauth_verifier'])) {
    // Retrieve the temporary credentials we saved before
    $temporaryCredentials = unserialize($_SESSION['temporary_credentials']);

    // We will now retrieve token credentials from the server
    $tokenCredentials = $server->getTokenCredentials($temporaryCredentials, $_GET['oauth_token'], $_GET['oauth_verifier']);
}
```

Now, you may choose to do what you need with the token credentials. You may store them in a database, in the session, or use them as one-off and then forget about them.

All credentials, (`client credentials`, `temporary credentials` and `token credentials`) all implement `League\OAuth1\Client\Credentials\CredentialsInterface` and have two sets of setters and getters exposed:

```php
var_dump($tokenCredentials->getIdentifier());
var_dump($tokenCredentials->getSecret());
```

In earlier versions of OAuth 1, the token credentials identifier and token credentials secret were referred to as **access token** and  **access token secret**. Don't be scared by the new terminology here - they are the same. This package is using the exact terminology in the RFC 5849 OAuth 1 standard.

> Twitter will send back an error message in the `denied` query string parameter, allowing you to provide feedback. Some servers do not send back an error message, but rather do not provide the successful `oauth_token` and `oauth_verifier` parameters.

### Accessing User Information

Now you have token credentials stored somewhere, you may use them to make calls against the server, as an authenticated user.

While this package is not intended to be a wrapper for every server's API, it does include basic methods that you may use to retrieve limited information. An example of where this may be useful is if you are using social logins, you only need limited information to confirm who the user is.

The four exposed methods are:

```php
// User is an instance of League\OAuth1\Client\Server\User
$user = $server->getUserDetails($tokenCredentials);

// UID is a string / integer unique representation of the user
$uid = $server->getUserUid($tokenCredentials);

// Email is either a string or null (as some providers do not supply this data)
$email = $server->getUserEmail($tokenCredentials);

// Screen name is also known as a username (Twitter handle etc)
$screenName = $server->getUserScreenName($tokenCredentials);
```

> `League\OAuth1\Client\Server\User` exposes a number of default public properties and also stores any additional data in an extra array - `$user->extra`. You may also iterate over a user's properties as if it was an array, `foreach ($user as $key => $value)`.

## Examples

Examples may be found under the [resources/examples](https://github.com/thephpleague/oauth1-client/tree/master/resources/examples) directory, which take the usage instructions here and go into a bit more depth. They are working examples that would only you substitute in your client credentials to have working.

## Testing

``` bash
$ phpunit
```


## Contributing

Please see [CONTRIBUTING](https://github.com/thephpleague/oauth1-client/blob/master/CONTRIBUTING.md) for details.


## Credits

- [Ben Corlett](https://github.com/bencorlett)
- [Steven Maguire](https://github.com/stevenmaguire)
- [All Contributors](https://github.com/thephpleague/oauth1-client/contributors)


## License

The MIT License (MIT). Please see [License File](https://github.com/thephpleague/oauth1-client/blob/master/LICENSE) for more information.
PK       ! O    0  oauth1-client/src/Client/Signature/Signature.phpnu         <?php

namespace League\OAuth1\Client\Signature;

use League\OAuth1\Client\Credentials\ClientCredentialsInterface;
use League\OAuth1\Client\Credentials\CredentialsInterface;

abstract class Signature implements SignatureInterface
{
    /**
     * The client credentials.
     *
     * @var ClientCredentialsInterface
     */
    protected $clientCredentials;

    /**
     * The (temporary or token) credentials.
     *
     * @var CredentialsInterface
     */
    protected $credentials;

    /**
     * {@inheritDoc}
     */
    public function __construct(ClientCredentialsInterface $clientCredentials)
    {
        $this->clientCredentials = $clientCredentials;
    }

    /**
     * {@inheritDoc}
     */
    public function setCredentials(CredentialsInterface $credentials)
    {
        $this->credentials = $credentials;
    }

    /**
     * Generate a signing key.
     *
     * @return string
     */
    protected function key()
    {
        $key = rawurlencode($this->clientCredentials->getSecret()).'&';

        if ($this->credentials !== null) {
            $key .= rawurlencode($this->credentials->getSecret());
        }

        return $key;
    }
}
PK       ! ]v    9  oauth1-client/src/Client/Signature/PlainTextSignature.phpnu         <?php

namespace League\OAuth1\Client\Signature;

class PlainTextSignature extends Signature implements SignatureInterface
{
    /**
     * {@inheritDoc}
     */
    public function method()
    {
        return 'PLAINTEXT';
    }

    /**
     * {@inheritDoc}
     */
    public function sign($uri, array $parameters = array(), $method = 'POST')
    {
        return $this->key();
    }
}
PK       ! lo    9  oauth1-client/src/Client/Signature/SignatureInterface.phpnu         <?php

namespace League\OAuth1\Client\Signature;

use League\OAuth1\Client\Credentials\ClientCredentialsInterface;
use League\OAuth1\Client\Credentials\CredentialsInterface;

interface SignatureInterface
{
    /**
     * Create a new signature instance.
     *
     * @param ClientCredentialsInterface $clientCredentials
     */
    public function __construct(ClientCredentialsInterface $clientCredentials);

    /**
     * Set the credentials used in the signature. These can be temporary
     * credentials when getting token credentials during the OAuth
     * authentication process, or token credentials when querying
     * the API.
     *
     * @param CredentialsInterface $credentials
     */
    public function setCredentials(CredentialsInterface $credentials);

    /**
     * Get the OAuth signature method.
     *
     * @return string
     */
    public function method();

    /**
     * Sign the given request for the client.
     *
     * @param string $uri
     * @param array  $parameters
     * @param string $method
     *
     * @return string
     */
    public function sign($uri, array $parameters = array(), $method = 'POST');
}
PK       ! B!    8  oauth1-client/src/Client/Signature/HmacSha1Signature.phpnu         <?php

namespace League\OAuth1\Client\Signature;

use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Uri;

class HmacSha1Signature extends Signature implements SignatureInterface
{
    /**
     * {@inheritDoc}
     */
    public function method()
    {
        return 'HMAC-SHA1';
    }

    /**
     * {@inheritDoc}
     */
    public function sign($uri, array $parameters = array(), $method = 'POST')
    {
        $url = $this->createUrl($uri);

        $baseString = $this->baseString($url, $method, $parameters);

        return base64_encode($this->hash($baseString));
    }

    /**
     * Create a Guzzle url for the given URI.
     *
     * @param string $uri
     *
     * @return Url
     */
    protected function createUrl($uri)
    {
        return Psr7\uri_for($uri);
    }

    /**
     * Generate a base string for a HMAC-SHA1 signature
     * based on the given a url, method, and any parameters.
     *
     * @param Url    $url
     * @param string $method
     * @param array  $parameters
     *
     * @return string
     */
    protected function baseString(Uri $url, $method = 'POST', array $parameters = array())
    {
        $baseString = rawurlencode($method).'&';

        $schemeHostPath = Uri::fromParts(array(
           'scheme' => $url->getScheme(),
           'host' => $url->getHost(),
           'path' => $url->getPath(),
        ));

        $baseString .= rawurlencode($schemeHostPath).'&';

        $data = array();
        parse_str($url->getQuery(), $query);
        $data = array_merge($query, $parameters);

        // normalize data key/values
        array_walk_recursive($data, function (&$key, &$value) {
            $key   = rawurlencode(rawurldecode($key));
            $value = rawurlencode(rawurldecode($value));
        });
        ksort($data);

        $baseString .= $this->queryStringFromData($data);

        return $baseString;
    }

    /**
     * Creates an array of rawurlencoded strings out of each array key/value pair
     * Handles multi-demensional arrays recursively.
     *
     * @param  array  $data        Array of parameters to convert.
     * @param  array  $queryParams Array to extend. False by default.
     * @param  string $prevKey     Optional Array key to append
     *
     * @return string              rawurlencoded string version of data
     */
    protected function queryStringFromData($data, $queryParams = false, $prevKey = '')
    {
        if ($initial = (false === $queryParams)) {
            $queryParams = array();
        }

        foreach ($data as $key => $value) {
            if ($prevKey) {
                $key = $prevKey.'['.$key.']'; // Handle multi-dimensional array
            }
            if (is_array($value)) {
                $queryParams = $this->queryStringFromData($value, $queryParams, $key);
            } else {
                $queryParams[] = rawurlencode($key.'='.$value); // join with equals sign
            }
        }

        if ($initial) {
            return implode('%26', $queryParams); // join with ampersand
        }

        return $queryParams;
    }

    /**
     * Hashes a string with the signature's key.
     *
     * @param string $string
     *
     * @return string
     */
    protected function hash($string)
    {
        return hash_hmac('sha1', $string, $this->key(), true);
    }
}
PK       ! X1      9  oauth1-client/src/Client/Credentials/TokenCredentials.phpnu         <?php

namespace League\OAuth1\Client\Credentials;

class TokenCredentials extends Credentials implements CredentialsInterface
{
}
PK       ! F&    C  oauth1-client/src/Client/Credentials/ClientCredentialsInterface.phpnu         <?php

namespace League\OAuth1\Client\Credentials;

interface ClientCredentialsInterface extends CredentialsInterface
{
    /**
     * Get the credentials callback URI.
     *
     * @return string
     */
    public function getCallbackUri();

    /**
     * Set the credentials callback URI.
     *
     * @return string
     */
    public function setCallbackUri($callbackUri);
}
PK       ! u   u   =  oauth1-client/src/Client/Credentials/CredentialsException.phpnu         <?php

namespace League\OAuth1\Client\Credentials;

use Exception;

class CredentialsException extends Exception
{
}
PK       ! SR  R  =  oauth1-client/src/Client/Credentials/CredentialsInterface.phpnu         <?php

namespace League\OAuth1\Client\Credentials;

interface CredentialsInterface
{
    /**
     * Get the credentials identifier.
     *
     * @return string
     */
    public function getIdentifier();

    /**
     * Set the credentials identifier.
     *
     * @param string $identifier
     */
    public function setIdentifier($identifier);

    /**
     * Get the credentials secret.
     *
     * @return string
     */
    public function getSecret();

    /**
     * Set the credentials secret.
     *
     * @param string $secret
     */
    public function setSecret($secret);
}
PK       ! ֟n(  (  4  oauth1-client/src/Client/Credentials/Credentials.phpnu         <?php

namespace League\OAuth1\Client\Credentials;

abstract class Credentials implements CredentialsInterface
{
    /**
     * The credentials identifier.
     *
     * @var string
     */
    protected $identifier;

    /**
     * The credentials secret.
     *
     * @var string
     */
    protected $secret;

    /**
     * {@inheritDoc}
     */
    public function getIdentifier()
    {
        return $this->identifier;
    }

    /**
     * {@inheritDoc}
     */
    public function setIdentifier($identifier)
    {
        $this->identifier = $identifier;
    }

    /**
     * {@inheritDoc}
     */
    public function getSecret()
    {
        return $this->secret;
    }

    /**
     * {@inheritDoc}
     */
    public function setSecret($secret)
    {
        $this->secret = $secret;
    }
}
PK       ! O      =  oauth1-client/src/Client/Credentials/TemporaryCredentials.phpnu         <?php

namespace League\OAuth1\Client\Credentials;

class TemporaryCredentials extends Credentials implements CredentialsInterface
{
}
PK       !     :  oauth1-client/src/Client/Credentials/ClientCredentials.phpnu         <?php

namespace League\OAuth1\Client\Credentials;

class ClientCredentials extends Credentials implements ClientCredentialsInterface
{
    /**
     * The credentials callback URI.
     *
     * @var string
     */
    protected $callbackUri;

    /**
     * {@inheritDoc}
     */
    public function getCallbackUri()
    {
        return $this->callbackUri;
    }

    /**
     * {@inheritDoc}
     */
    public function setCallbackUri($callbackUri)
    {
        $this->callbackUri = $callbackUri;
    }
}
PK       ! ڍO  O  *  oauth1-client/src/Client/Server/Server.phpnu         <?php

namespace League\OAuth1\Client\Server;

use GuzzleHttp\Client as GuzzleHttpClient;
use GuzzleHttp\Exception\BadResponseException;
use League\OAuth1\Client\Credentials\ClientCredentialsInterface;
use League\OAuth1\Client\Credentials\ClientCredentials;
use League\OAuth1\Client\Credentials\CredentialsInterface;
use League\OAuth1\Client\Credentials\CredentialsException;
use League\OAuth1\Client\Credentials\TemporaryCredentials;
use League\OAuth1\Client\Credentials\TokenCredentials;
use League\OAuth1\Client\Signature\HmacSha1Signature;
use League\OAuth1\Client\Signature\SignatureInterface;

abstract class Server
{
    /**
     * Client credentials.
     *
     * @var ClientCredentials
     */
    protected $clientCredentials;

    /**
     * Signature.
     *
     * @var SignatureInterface
     */
    protected $signature;

    /**
     * The response type for data returned from API calls.
     *
     * @var string
     */
    protected $responseType = 'json';

    /**
     * Cached user details response.
     *
     * @var unknown
     */
    protected $cachedUserDetailsResponse;

    /**
     * Optional user agent.
     *
     * @var string
     */
    protected $userAgent;

    /**
     * Create a new server instance.
     *
     * @param ClientCredentialsInterface|array $clientCredentials
     * @param SignatureInterface               $signature
     */
    public function __construct($clientCredentials, SignatureInterface $signature = null)
    {
        // Pass through an array or client credentials, we don't care
        if (is_array($clientCredentials)) {
            $clientCredentials = $this->createClientCredentials($clientCredentials);
        } elseif (!$clientCredentials instanceof ClientCredentialsInterface) {
            throw new \InvalidArgumentException('Client credentials must be an array or valid object.');
        }

        $this->clientCredentials = $clientCredentials;
        $this->signature = $signature ?: new HmacSha1Signature($clientCredentials);
    }

    /**
     * Gets temporary credentials by performing a request to
     * the server.
     *
     * @return TemporaryCredentials
     */
    public function getTemporaryCredentials()
    {
        $uri = $this->urlTemporaryCredentials();

        $client = $this->createHttpClient();

        $header = $this->temporaryCredentialsProtocolHeader($uri);
        $authorizationHeader = array('Authorization' => $header);
        $headers = $this->buildHttpClientHeaders($authorizationHeader);

        try {
            $response = $client->post($uri, [
                'headers' => $headers,
            ]);
        } catch (BadResponseException $e) {
            return $this->handleTemporaryCredentialsBadResponse($e);
        }

        return $this->createTemporaryCredentials((string) $response->getBody());
    }

    /**
     * Get the authorization URL by passing in the temporary credentials
     * identifier or an object instance.
     *
     * @param TemporaryCredentials|string $temporaryIdentifier
     *
     * @return string
     */
    public function getAuthorizationUrl($temporaryIdentifier)
    {
        // Somebody can pass through an instance of temporary
        // credentials and we'll extract the identifier from there.
        if ($temporaryIdentifier instanceof TemporaryCredentials) {
            $temporaryIdentifier = $temporaryIdentifier->getIdentifier();
        }

        $parameters = array('oauth_token' => $temporaryIdentifier);

        $url = $this->urlAuthorization();
        $queryString = http_build_query($parameters);

        return $this->buildUrl($url, $queryString);
    }

    /**
     * Redirect the client to the authorization URL.
     *
     * @param TemporaryCredentials|string $temporaryIdentifier
     */
    public function authorize($temporaryIdentifier)
    {
        $url = $this->getAuthorizationUrl($temporaryIdentifier);

        header('Location: '.$url);

        return;
    }

    /**
     * Retrieves token credentials by passing in the temporary credentials,
     * the temporary credentials identifier as passed back by the server
     * and finally the verifier code.
     *
     * @param TemporaryCredentials $temporaryCredentials
     * @param string               $temporaryIdentifier
     * @param string               $verifier
     *
     * @return TokenCredentials
     */
    public function getTokenCredentials(TemporaryCredentials $temporaryCredentials, $temporaryIdentifier, $verifier)
    {
        if ($temporaryIdentifier !== $temporaryCredentials->getIdentifier()) {
            throw new \InvalidArgumentException(
                'Temporary identifier passed back by server does not match that of stored temporary credentials.
                Potential man-in-the-middle.'
            );
        }

        $uri = $this->urlTokenCredentials();
        $bodyParameters = array('oauth_verifier' => $verifier);

        $client = $this->createHttpClient();

        $headers = $this->getHeaders($temporaryCredentials, 'POST', $uri, $bodyParameters);

        try {
            $response = $client->post($uri, [
                'headers' => $headers,
                'form_params' => $bodyParameters,
            ]);
        } catch (BadResponseException $e) {
            return $this->handleTokenCredentialsBadResponse($e);
        }

        return $this->createTokenCredentials((string) $response->getBody());
    }

    /**
     * Get user details by providing valid token credentials.
     *
     * @param TokenCredentials $tokenCredentials
     * @param bool             $force
     *
     * @return \League\OAuth1\Client\Server\User
     */
    public function getUserDetails(TokenCredentials $tokenCredentials, $force = false)
    {
        $data = $this->fetchUserDetails($tokenCredentials, $force);

        return $this->userDetails($data, $tokenCredentials);
    }

    /**
     * Get the user's unique identifier (primary key).
     *
     * @param TokenCredentials $tokenCredentials
     * @param bool             $force
     *
     * @return string|int
     */
    public function getUserUid(TokenCredentials $tokenCredentials, $force = false)
    {
        $data = $this->fetchUserDetails($tokenCredentials, $force);

        return $this->userUid($data, $tokenCredentials);
    }

    /**
     * Get the user's email, if available.
     *
     * @param TokenCredentials $tokenCredentials
     * @param bool             $force
     *
     * @return string|null
     */
    public function getUserEmail(TokenCredentials $tokenCredentials, $force = false)
    {
        $data = $this->fetchUserDetails($tokenCredentials, $force);

        return $this->userEmail($data, $tokenCredentials);
    }

    /**
     * Get the user's screen name (username), if available.
     *
     * @param TokenCredentials $tokenCredentials
     * @param bool             $force
     *
     * @return string
     */
    public function getUserScreenName(TokenCredentials $tokenCredentials, $force = false)
    {
        $data = $this->fetchUserDetails($tokenCredentials, $force);

        return $this->userScreenName($data, $tokenCredentials);
    }

    /**
     * Fetch user details from the remote service.
     *
     * @param TokenCredentials $tokenCredentials
     * @param bool             $force
     *
     * @return array HTTP client response
     */
    protected function fetchUserDetails(TokenCredentials $tokenCredentials, $force = true)
    {
        if (!$this->cachedUserDetailsResponse || $force) {
            $url = $this->urlUserDetails();

            $client = $this->createHttpClient();

            $headers = $this->getHeaders($tokenCredentials, 'GET', $url);

            try {
                $response = $client->get($url, [
                    'headers' => $headers,
                ]);
            } catch (BadResponseException $e) {
                $response = $e->getResponse();
                $body = $response->getBody();
                $statusCode = $response->getStatusCode();

                throw new \Exception(
                    "Received error [$body] with status code [$statusCode] when retrieving token credentials."
                );
            }
            switch ($this->responseType) {
                case 'json':
                    $this->cachedUserDetailsResponse = json_decode((string) $response->getBody(), true);
                    break;

                case 'xml':
                    $this->cachedUserDetailsResponse = simplexml_load_string((string) $response->getBody());
                    break;

                case 'string':
                    parse_str((string) $response->getBody(), $this->cachedUserDetailsResponse);
                    break;

                default:
                    throw new \InvalidArgumentException("Invalid response type [{$this->responseType}].");
            }
        }

        return $this->cachedUserDetailsResponse;
    }

    /**
     * Get the client credentials associated with the server.
     *
     * @return ClientCredentialsInterface
     */
    public function getClientCredentials()
    {
        return $this->clientCredentials;
    }

    /**
     * Get the signature associated with the server.
     *
     * @return SignatureInterface
     */
    public function getSignature()
    {
        return $this->signature;
    }

    /**
     * Creates a Guzzle HTTP client for the given URL.
     *
     * @return GuzzleHttpClient
     */
    public function createHttpClient()
    {
        return new GuzzleHttpClient();
    }

    /**
     * Set the user agent value.
     *
     * @param string $userAgent
     *
     * @return Server
     */
    public function setUserAgent($userAgent = null)
    {
        $this->userAgent = $userAgent;

        return $this;
    }

    /**
     * Get all headers required to created an authenticated request.
     *
     * @param CredentialsInterface $credentials
     * @param string               $method
     * @param string               $url
     * @param array                $bodyParameters
     *
     * @return array
     */
    public function getHeaders(CredentialsInterface $credentials, $method, $url, array $bodyParameters = array())
    {
        $header = $this->protocolHeader(strtoupper($method), $url, $credentials, $bodyParameters);
        $authorizationHeader = array('Authorization' => $header);
        $headers = $this->buildHttpClientHeaders($authorizationHeader);

        return $headers;
    }

    /**
     * Get Guzzle HTTP client default headers.
     *
     * @return array
     */
    protected function getHttpClientDefaultHeaders()
    {
        $defaultHeaders = array();
        if (!empty($this->userAgent)) {
            $defaultHeaders['User-Agent'] = $this->userAgent;
        }

        return $defaultHeaders;
    }

    /**
     * Build Guzzle HTTP client headers.
     *
     * @return array
     */
    protected function buildHttpClientHeaders($headers = array())
    {
        $defaultHeaders = $this->getHttpClientDefaultHeaders();

        return array_merge($headers, $defaultHeaders);
    }

    /**
     * Creates a client credentials instance from an array of credentials.
     *
     * @param array $clientCredentials
     *
     * @return ClientCredentials
     */
    protected function createClientCredentials(array $clientCredentials)
    {
        $keys = array('identifier', 'secret');

        foreach ($keys as $key) {
            if (!isset($clientCredentials[$key])) {
                throw new \InvalidArgumentException("Missing client credentials key [$key] from options.");
            }
        }

        $_clientCredentials = new ClientCredentials();
        $_clientCredentials->setIdentifier($clientCredentials['identifier']);
        $_clientCredentials->setSecret($clientCredentials['secret']);

        if (isset($clientCredentials['callback_uri'])) {
            $_clientCredentials->setCallbackUri($clientCredentials['callback_uri']);
        }

        return $_clientCredentials;
    }

    /**
     * Handle a bad response coming back when getting temporary credentials.
     *
     * @param BadResponseException $e
     *
     * @throws CredentialsException
     */
    protected function handleTemporaryCredentialsBadResponse(BadResponseException $e)
    {
        $response = $e->getResponse();
        $body = $response->getBody();
        $statusCode = $response->getStatusCode();

        throw new CredentialsException(
            "Received HTTP status code [$statusCode] with message \"$body\" when getting temporary credentials."
        );
    }

    /**
     * Creates temporary credentials from the body response.
     *
     * @param string $body
     *
     * @return TemporaryCredentials
     */
    protected function createTemporaryCredentials($body)
    {
        parse_str($body, $data);

        if (!$data || !is_array($data)) {
            throw new CredentialsException('Unable to parse temporary credentials response.');
        }

        if (!isset($data['oauth_callback_confirmed']) || $data['oauth_callback_confirmed'] != 'true') {
            throw new CredentialsException('Error in retrieving temporary credentials.');
        }

        $temporaryCredentials = new TemporaryCredentials();
        $temporaryCredentials->setIdentifier($data['oauth_token']);
        $temporaryCredentials->setSecret($data['oauth_token_secret']);

        return $temporaryCredentials;
    }

    /**
     * Handle a bad response coming back when getting token credentials.
     *
     * @param BadResponseException $e
     *
     * @throws CredentialsException
     */
    protected function handleTokenCredentialsBadResponse(BadResponseException $e)
    {
        $response = $e->getResponse();
        $body = $response->getBody();
        $statusCode = $response->getStatusCode();

        throw new CredentialsException(
            "Received HTTP status code [$statusCode] with message \"$body\" when getting token credentials."
        );
    }

    /**
     * Creates token credentials from the body response.
     *
     * @param string $body
     *
     * @return TokenCredentials
     */
    protected function createTokenCredentials($body)
    {
        parse_str($body, $data);

        if (!$data || !is_array($data)) {
            throw new CredentialsException('Unable to parse token credentials response.');
        }

        if (isset($data['error'])) {
            throw new CredentialsException("Error [{$data['error']}] in retrieving token credentials.");
        }

        $tokenCredentials = new TokenCredentials();
        $tokenCredentials->setIdentifier($data['oauth_token']);
        $tokenCredentials->setSecret($data['oauth_token_secret']);

        return $tokenCredentials;
    }

    /**
     * Get the base protocol parameters for an OAuth request.
     * Each request builds on these parameters.
     *
     * @return array
     *
     * @see    OAuth 1.0 RFC 5849 Section 3.1
     */
    protected function baseProtocolParameters()
    {
        $dateTime = new \DateTime();

        return array(
            'oauth_consumer_key' => $this->clientCredentials->getIdentifier(),
            'oauth_nonce' => $this->nonce(),
            'oauth_signature_method' => $this->signature->method(),
            'oauth_timestamp' => $dateTime->format('U'),
            'oauth_version' => '1.0',
        );
    }

    /**
     * Any additional required protocol parameters for an
     * OAuth request.
     *
     * @return array
     */
    protected function additionalProtocolParameters()
    {
        return array();
    }

    /**
     * Generate the OAuth protocol header for a temporary credentials
     * request, based on the URI.
     *
     * @param string $uri
     *
     * @return string
     */
    protected function temporaryCredentialsProtocolHeader($uri)
    {
        $parameters = array_merge($this->baseProtocolParameters(), array(
            'oauth_callback' => $this->clientCredentials->getCallbackUri(),
        ));

        $parameters['oauth_signature'] = $this->signature->sign($uri, $parameters, 'POST');

        return $this->normalizeProtocolParameters($parameters);
    }

    /**
     * Generate the OAuth protocol header for requests other than temporary
     * credentials, based on the URI, method, given credentials & body query
     * string.
     *
     * @param string               $method
     * @param string               $uri
     * @param CredentialsInterface $credentials
     * @param array                $bodyParameters
     *
     * @return string
     */
    protected function protocolHeader($method, $uri, CredentialsInterface $credentials, array $bodyParameters = array())
    {
        $parameters = array_merge(
            $this->baseProtocolParameters(),
            $this->additionalProtocolParameters(),
            array(
                'oauth_token' => $credentials->getIdentifier(),
            )
        );

        $this->signature->setCredentials($credentials);

        $parameters['oauth_signature'] = $this->signature->sign(
            $uri,
            array_merge($parameters, $bodyParameters),
            $method
        );

        return $this->normalizeProtocolParameters($parameters);
    }

    /**
     * Takes an array of protocol parameters and normalizes them
     * to be used as a HTTP header.
     *
     * @param array $parameters
     *
     * @return string
     */
    protected function normalizeProtocolParameters(array $parameters)
    {
        array_walk($parameters, function (&$value, $key) {
            $value = rawurlencode($key).'="'.rawurlencode($value).'"';
        });

        return 'OAuth '.implode(', ', $parameters);
    }

    /**
     * Generate a random string.
     *
     * @param int $length
     *
     * @return string
     *
     * @see    OAuth 1.0 RFC 5849 Section 3.3
     */
    protected function nonce($length = 32)
    {
        $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';

        return substr(str_shuffle(str_repeat($pool, 5)), 0, $length);
    }

    /**
     * Build a url by combining hostname and query string after checking for
     * exisiting '?' character in host.
     *
     * @param string $host
     * @param string $queryString
     *
     * @return string
     */
    protected function buildUrl($host, $queryString)
    {
        return $host.(strpos($host, '?') !== false ? '&' : '?').$queryString;
    }

    /**
     * Get the URL for retrieving temporary credentials.
     *
     * @return string
     */
    abstract public function urlTemporaryCredentials();

    /**
     * Get the URL for redirecting the resource owner to authorize the client.
     *
     * @return string
     */
    abstract public function urlAuthorization();

    /**
     * Get the URL retrieving token credentials.
     *
     * @return string
     */
    abstract public function urlTokenCredentials();

    /**
     * Get the URL for retrieving user details.
     *
     * @return string
     */
    abstract public function urlUserDetails();

    /**
     * Take the decoded data from the user details URL and convert
     * it to a User object.
     *
     * @param mixed            $data
     * @param TokenCredentials $tokenCredentials
     *
     * @return User
     */
    abstract public function userDetails($data, TokenCredentials $tokenCredentials);

    /**
     * Take the decoded data from the user details URL and extract
     * the user's UID.
     *
     * @param mixed            $data
     * @param TokenCredentials $tokenCredentials
     *
     * @return string|int
     */
    abstract public function userUid($data, TokenCredentials $tokenCredentials);

    /**
     * Take the decoded data from the user details URL and extract
     * the user's email.
     *
     * @param mixed            $data
     * @param TokenCredentials $tokenCredentials
     *
     * @return string
     */
    abstract public function userEmail($data, TokenCredentials $tokenCredentials);

    /**
     * Take the decoded data from the user details URL and extract
     * the user's screen name.
     *
     * @param mixed            $data
     * @param TokenCredentials $tokenCredentials
     *
     * @return string
     */
    abstract public function userScreenName($data, TokenCredentials $tokenCredentials);
}
PK       ! y	  	  (  oauth1-client/src/Client/Server/Xing.phpnu         <?php

namespace League\OAuth1\Client\Server;

use League\OAuth1\Client\Credentials\TokenCredentials;

class Xing extends Server
{
    const XING_API_ENDPOINT = 'https://api.xing.com';
    
    /**
    * {@inheritDoc}
    */
    public function urlTemporaryCredentials()
    {
        return self::XING_API_ENDPOINT . '/v1/request_token';
    }
    /**
    * {@inheritDoc}
    */
    public function urlAuthorization()
    {
        return self::XING_API_ENDPOINT . '/v1/authorize';
    }
    /**
    * {@inheritDoc}
    */
    public function urlTokenCredentials()
    {
        return self::XING_API_ENDPOINT . '/v1/access_token';
    }
    /**
    * {@inheritDoc}
    */
    public function urlUserDetails()
    {
        return self::XING_API_ENDPOINT . '/v1/users/me';
    }
    /**
    * {@inheritDoc}
    */
    public function userDetails($data, TokenCredentials $tokenCredentials)
    {
        if (!isset($data['users'][0])) {
            throw new \Exception('Not possible to get user info');
        }
        $data = $data['users'][0];
        
        $user = new User();
        $user->uid = $data['id'];
        $user->nickname = $data['display_name'];
        $user->name = $data['display_name'];
        $user->firstName = $data['first_name'];
        $user->lastName = $data['last_name'];
        $user->location = $data['private_address']['country'];
        
        if ($user->location == '') {
            $user->location = $data['business_address']['country'];
        }
        $user->description = $data['employment_status'];
        $user->imageUrl = $data['photo_urls']['maxi_thumb'];
        $user->email = $data['active_email'];
        
        $user->urls['permalink'] = $data['permalink'];
        
        return $user;
    }
    /**
    * {@inheritDoc}
    */
    public function userUid($data, TokenCredentials $tokenCredentials)
    {
        $data = $data['users'][0];
        return $data['id'];
    }
    /**
    * {@inheritDoc}
    */
    public function userEmail($data, TokenCredentials $tokenCredentials)
    {
        $data = $data['users'][0];
        return $data['active_email'];
    }
    /**
    * {@inheritDoc}
    */
    public function userScreenName($data, TokenCredentials $tokenCredentials)
    {
        $data = $data['users'][0];
        return $data['display_name'];
    }
}
PK       ! ʱ(  (  -  oauth1-client/src/Client/Server/Uservoice.phpnu         <?php

namespace League\OAuth1\Client\Server;

use InvalidArgumentException;
use League\OAuth1\Client\Credentials\TokenCredentials;
use League\OAuth1\Client\Signature\SignatureInterface;

class Uservoice extends Server
{
    /**
     * The base URL, used to generate the auth endpoints.
     *
     * @var string
     */
    protected $base;

    /**
     * {@inheritDoc}
     */
    public function __construct($clientCredentials, SignatureInterface $signature = null)
    {
        parent::__construct($clientCredentials, $signature);

        if (is_array($clientCredentials)) {
            $this->parseConfigurationArray($clientCredentials);
        }
    }

    /**
     * {@inheritDoc}
     */
    public function urlTemporaryCredentials()
    {
        return $this->base.'/oauth/request_token';
    }

    /**
     * {@inheritDoc}
     */
    public function urlAuthorization()
    {
        return $this->base.'/oauth/authorize';
    }

    /**
     * {@inheritDoc}
     */
    public function urlTokenCredentials()
    {
        return $this->base.'/oauth/access_token';
    }

    /**
     * {@inheritdoc}
     */
    public function urlUserDetails()
    {
        return $this->base.'/api/v1/users/current.json';
    }

    /**
     * {@inheritDoc}
     */
    public function userDetails($data, TokenCredentials $tokenCredentials)
    {
        $user = new User();

        $user->uid = $data['user']['id'];
        $user->name = $data['user']['name'];
        $user->imageUrl = $data['user']['avatar_url'];
        $user->email = $data['user']['email'];

        if ($data['user']['name']) {
            $parts = explode(' ', $data['user']['name']);

            if (count($parts) > 0) {
                $user->firstName = $parts[0];
            }

            if (count($parts) > 1) {
                $user->lastName = $parts[1];
            }
        }

        $user->urls[] = $data['user']['url'];

        return $user;
    }

    /**
     * {@inheritdoc}
     */
    public function userUid($data, TokenCredentials $tokenCredentials)
    {
        return $data['user']['id'];
    }

    /**
     * {@inheritdoc}
     */
    public function userEmail($data, TokenCredentials $tokenCredentials)
    {
        return $data['user']['email'];
    }

    /**
     * {@inheritdoc}
     */
    public function userScreenName($data, TokenCredentials $tokenCredentials)
    {
        return $data['user']['name'];
    }

    /**
     * Parse configuration array to set attributes.
     *
     * @param array $configuration
     *
     * @throws InvalidArgumentException
     */
    private function parseConfigurationArray(array $configuration = array())
    {
        if (isset($configuration['host'])) {
            throw new InvalidArgumentException('Missing host');
        }

        $this->base = trim($configuration['host'], '/');
    }
}
PK       ! X7p    +  oauth1-client/src/Client/Server/Magento.phpnu         <?php

namespace League\OAuth1\Client\Server;

use League\OAuth1\Client\Credentials\TemporaryCredentials;
use League\OAuth1\Client\Credentials\TokenCredentials;

/**
 * Magento OAuth 1.0a.
 *
 * This class reflects two Magento oddities:
 *  - Magento expects the oauth_verifier to be located in the header instead of
 *    the post body.
 *  - Magento expects the Accept to be located in the header
 *
 * Additionally, this is initialized with two additional parameters:
 *  - Boolean 'admin' to use the admin vs customer
 *  - String 'host' with the path to the magento host
 */
class Magento extends Server
{
    /**
     * Admin url.
     *
     * @var string
     */
    protected $adminUrl;

    /**
     * Base uri.
     *
     * @var string
     */
    protected $baseUri;

    /**
     * Server is admin.
     *
     * @var bool
     */
    protected $isAdmin = false;

    /**
     * oauth_verifier stored for use with.
     *
     * @var string
     */
    private $verifier;

    /**
     * {@inheritDoc}
     */
    public function __construct($clientCredentials, SignatureInterface $signature = null)
    {
        parent::__construct($clientCredentials, $signature);
        if (is_array($clientCredentials)) {
            $this->parseConfigurationArray($clientCredentials);
        }
    }

    /**
     * {@inheritDoc}
     */
    public function urlTemporaryCredentials()
    {
        return $this->baseUri.'/oauth/initiate';
    }

    /**
     * {@inheritDoc}
     */
    public function urlAuthorization()
    {
        return $this->isAdmin
            ? $this->adminUrl
            : $this->baseUri.'/oauth/authorize';
    }

    /**
     * {@inheritDoc}
     */
    public function urlTokenCredentials()
    {
        return $this->baseUri.'/oauth/token';
    }

    /**
     * {@inheritDoc}
     */
    public function urlUserDetails()
    {
        return $this->baseUri.'/api/rest/customers';
    }

    /**
     * {@inheritDoc}
     */
    public function userDetails($data, TokenCredentials $tokenCredentials)
    {
        if (!is_array($data) || !count($data)) {
            throw new \Exception('Not possible to get user info');
        }

        $id = key($data);
        $data = current($data);

        $user = new User();
        $user->uid = $id;

        $mapping = array(
            'email' => 'email',
            'firstName' => 'firstname',
            'lastName'  => 'lastname',
        );
        foreach ($mapping as $userKey => $dataKey) {
            if (!isset($data[$dataKey])) {
                continue;
            }
            $user->{$userKey} = $data[$dataKey];
        }

        $user->extra = array_diff_key($data, array_flip($mapping));

        return $user;
    }

    /**
     * {@inheritDoc}
     */
    public function userUid($data, TokenCredentials $tokenCredentials)
    {
        return key($data);
    }

    /**
     * {@inheritDoc}
     */
    public function userEmail($data, TokenCredentials $tokenCredentials)
    {
        $data = current($data);
        if (!isset($data['email'])) {
            return;
        }
        return $data['email'];
    }

    /**
     * {@inheritDoc}
     */
    public function userScreenName($data, TokenCredentials $tokenCredentials)
    {
        return;
    }

    /**
     * {@inheritDoc}
     */
    public function getTokenCredentials(TemporaryCredentials $temporaryCredentials, $temporaryIdentifier, $verifier)
    {
        $this->verifier = $verifier;

        return parent::getTokenCredentials($temporaryCredentials, $temporaryIdentifier, $verifier);
    }

    /**
     * {@inheritDoc}
     */
    protected function additionalProtocolParameters()
    {
        return array(
            'oauth_verifier' => $this->verifier,
        );
    }

    protected function getHttpClientDefaultHeaders()
    {
        $defaultHeaders = parent::getHttpClientDefaultHeaders();
        // Accept header is required, @see Mage_Api2_Model_Renderer::factory
        $defaultHeaders['Accept'] = 'application/json';

        return $defaultHeaders;
    }

    /**
     * Parse configuration array to set attributes.
     *
     * @param array $configuration
     * @throws \Exception
     */
    private function parseConfigurationArray(array $configuration = array())
    {
        if (!isset($configuration['host'])) {
            throw new \Exception('Missing Magento Host');
        }
        $url = parse_url($configuration['host']);
        $this->baseUri = sprintf('%s://%s', $url['scheme'], $url['host']);

        if (isset($url['port'])) {
            $this->baseUri .= ':'.$url['port'];
        }

        if (isset($url['path'])) {
            $this->baseUri .= '/'.trim($url['path'], '/');
        }
        $this->isAdmin = !empty($configuration['admin']);
        if (!empty($configuration['adminUrl'])) {
            $this->adminUrl = $configuration['adminUrl'].'/oauth_authorize';
        } else {
            $this->adminUrl = $this->baseUri.'/admin/oauth_authorize';
        }
    }
}
PK       !     +  oauth1-client/src/Client/Server/Twitter.phpnu         <?php

namespace League\OAuth1\Client\Server;

use League\OAuth1\Client\Credentials\TokenCredentials;

class Twitter extends Server
{
    /**
     * {@inheritDoc}
     */
    public function urlTemporaryCredentials()
    {
        return 'https://api.twitter.com/oauth/request_token';
    }

    /**
     * {@inheritDoc}
     */
    public function urlAuthorization()
    {
        return 'https://api.twitter.com/oauth/authenticate';
    }

    /**
     * {@inheritDoc}
     */
    public function urlTokenCredentials()
    {
        return 'https://api.twitter.com/oauth/access_token';
    }

    /**
     * {@inheritDoc}
     */
    public function urlUserDetails()
    {
        return 'https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true';
    }

    /**
     * {@inheritDoc}
     */
    public function userDetails($data, TokenCredentials $tokenCredentials)
    {
        $user = new User();

        $user->uid = $data['id_str'];
        $user->nickname = $data['screen_name'];
        $user->name = $data['name'];
        $user->location = $data['location'];
        $user->description = $data['description'];
        $user->imageUrl = $data['profile_image_url'];
        $user->email = null;
        if (isset($data['email'])) {
            $user->email = $data['email'];
        }

        $used = array('id', 'screen_name', 'name', 'location', 'description', 'profile_image_url', 'email');

        foreach ($data as $key => $value) {
            if (strpos($key, 'url') !== false) {
                if (!in_array($key, $used)) {
                    $used[] = $key;
                }

                $user->urls[$key] = $value;
            }
        }

        // Save all extra data
        $user->extra = array_diff_key($data, array_flip($used));

        return $user;
    }

    /**
     * {@inheritDoc}
     */
    public function userUid($data, TokenCredentials $tokenCredentials)
    {
        return $data['id'];
    }

    /**
     * {@inheritDoc}
     */
    public function userEmail($data, TokenCredentials $tokenCredentials)
    {
        return;
    }

    /**
     * {@inheritDoc}
     */
    public function userScreenName($data, TokenCredentials $tokenCredentials)
    {
        return $data['name'];
    }
}
PK       ! $Tb  b  -  oauth1-client/src/Client/Server/Bitbucket.phpnu         <?php

namespace League\OAuth1\Client\Server;

use League\OAuth1\Client\Credentials\TokenCredentials;

class Bitbucket extends Server
{
    /**
     * {@inheritDoc}
     */
    public function urlTemporaryCredentials()
    {
        return 'https://bitbucket.org/api/1.0/oauth/request_token';
    }

    /**
     * {@inheritDoc}
     */
    public function urlAuthorization()
    {
        return 'https://bitbucket.org/api/1.0/oauth/authenticate';
    }

    /**
     * {@inheritDoc}
     */
    public function urlTokenCredentials()
    {
        return 'https://bitbucket.org/api/1.0/oauth/access_token';
    }

    /**
     * {@inheritDoc}
     */
    public function urlUserDetails()
    {
        return 'https://bitbucket.org/api/1.0/user';
    }

    /**
     * {@inheritDoc}
     */
    public function userDetails($data, TokenCredentials $tokenCredentials)
    {
        $user = new User();

        $user->uid = $data['user']['username'];
        $user->nickname = $data['user']['username'];
        $user->name = $data['user']['display_name'];
        $user->firstName = $data['user']['first_name'];
        $user->lastName = $data['user']['last_name'];
        $user->imageUrl = $data['user']['avatar'];

        $used = array('username', 'display_name', 'avatar');

        foreach ($data as $key => $value) {
            if (strpos($key, 'url') !== false) {
                if (!in_array($key, $used)) {
                    $used[] = $key;
                }

                $user->urls[$key] = $value;
            }
        }

        // Save all extra data
        $user->extra = array_diff_key($data, array_flip($used));

        return $user;
    }

    /**
     * {@inheritDoc}
     */
    public function userUid($data, TokenCredentials $tokenCredentials)
    {
        return $data['user']['username'];
    }

    /**
     * {@inheritDoc}
     */
    public function userEmail($data, TokenCredentials $tokenCredentials)
    {
        return;
    }

    /**
     * {@inheritDoc}
     */
    public function userScreenName($data, TokenCredentials $tokenCredentials)
    {
        return $data['user']['display_name'];
    }
}
PK       ! j#'  '  *  oauth1-client/src/Client/Server/Trello.phpnu         <?php

namespace League\OAuth1\Client\Server;

use League\OAuth1\Client\Credentials\TokenCredentials;

class Trello extends Server
{
    /**
     * Access token.
     *
     * @var string
     */
    protected $accessToken;

    /**
     * Application expiration.
     *
     * @var string
     */
    protected $applicationExpiration;

    /**
     * Application key.
     *
     * @var string
     */
    protected $applicationKey;

    /**
     * Application name.
     *
     * @var string
     */
    protected $applicationName;

    /**
     * Application scope.
     *
     * @var string
     */
    protected $applicationScope;

    /**
     * {@inheritDoc}
     */
    public function __construct($clientCredentials, SignatureInterface $signature = null)
    {
        parent::__construct($clientCredentials, $signature);

        if (is_array($clientCredentials)) {
            $this->parseConfiguration($clientCredentials);
        }
    }

    /**
     * Set the access token.
     *
     * @param string $accessToken
     *
     * @return Trello
     */
    public function setAccessToken($accessToken)
    {
        $this->accessToken = $accessToken;

        return $this;
    }

    /**
     * Set the application expiration.
     *
     * @param string $applicationExpiration
     *
     * @return Trello
     */
    public function setApplicationExpiration($applicationExpiration)
    {
        $this->applicationExpiration = $applicationExpiration;

        return $this;
    }

    /**
     * Get application expiration.
     *
     * @return string
     */
    public function getApplicationExpiration()
    {
        return $this->applicationExpiration ?: '1day';
    }

    /**
     * Set the application name.
     *
     * @param string $applicationName
     *
     * @return Trello
     */
    public function setApplicationName($applicationName)
    {
        $this->applicationName = $applicationName;

        return $this;
    }

    /**
     * Get application name.
     *
     * @return string|null
     */
    public function getApplicationName()
    {
        return $this->applicationName ?: null;
    }

    /**
     * Set the application scope.
     *
     * @param string $applicationScope
     *
     * @return Trello
     */
    public function setApplicationScope($applicationScope)
    {
        $this->applicationScope = $applicationScope;

        return $this;
    }

    /**
     * Get application scope.
     *
     * @return string
     */
    public function getApplicationScope()
    {
        return $this->applicationScope ?: 'read';
    }

    /**
     * {@inheritDoc}
     */
    public function urlTemporaryCredentials()
    {
        return 'https://trello.com/1/OAuthGetRequestToken';
    }

    /**
     * {@inheritDoc}
     */
    public function urlAuthorization()
    {
        return 'https://trello.com/1/OAuthAuthorizeToken?'.
            $this->buildAuthorizationQueryParameters();
    }

    /**
     * {@inheritDoc}
     */
    public function urlTokenCredentials()
    {
        return 'https://trello.com/1/OAuthGetAccessToken';
    }

    /**
     * {@inheritDoc}
     */
    public function urlUserDetails()
    {
        return 'https://trello.com/1/members/me?key='.$this->applicationKey.'&token='.$this->accessToken;
    }

    /**
     * {@inheritDoc}
     */
    public function userDetails($data, TokenCredentials $tokenCredentials)
    {
        $user = new User();

        $user->nickname = $data['username'];
        $user->name = $data['fullName'];
        $user->imageUrl = null;

        $user->extra = (array) $data;

        return $user;
    }

    /**
     * {@inheritDoc}
     */
    public function userUid($data, TokenCredentials $tokenCredentials)
    {
        return $data['id'];
    }

    /**
     * {@inheritDoc}
     */
    public function userEmail($data, TokenCredentials $tokenCredentials)
    {
        return;
    }

    /**
     * {@inheritDoc}
     */
    public function userScreenName($data, TokenCredentials $tokenCredentials)
    {
        return $data['username'];
    }

    /**
     * Build authorization query parameters.
     *
     * @return string
     */
    private function buildAuthorizationQueryParameters()
    {
        $params = array(
            'response_type' => 'fragment',
            'scope' => $this->getApplicationScope(),
            'expiration' => $this->getApplicationExpiration(),
            'name' => $this->getApplicationName(),
        );

        return http_build_query($params);
    }

    /**
     * Parse configuration array to set attributes.
     *
     * @param array $configuration
     */
    private function parseConfiguration(array $configuration = array())
    {
        $configToPropertyMap = array(
            'identifier' => 'applicationKey',
            'expiration' => 'applicationExpiration',
            'name' => 'applicationName',
            'scope' => 'applicationScope',
        );

        foreach ($configToPropertyMap as $config => $property) {
            if (isset($configuration[$config])) {
                $this->$property = $configuration[$config];
            }
        }
    }
}
PK       ! ?    (  oauth1-client/src/Client/Server/User.phpnu         <?php

namespace League\OAuth1\Client\Server;

class User implements \IteratorAggregate
{
    /**
     * The user's unique ID.
     *
     * @var mixed
     */
    public $uid = null;

    /**
     * The user's nickname (screen name, username etc).
     *
     * @var mixed
     */
    public $nickname = null;

    /**
     * The user's name.
     *
     * @var mixed
     */
    public $name = null;

    /**
     * The user's first name.
     *
     * @var string
     */
    public $firstName = null;

    /**
     * The user's last name.
     *
     * @var string
     */
    public $lastName = null;

    /**
     * The user's email.
     *
     * @var string
     */
    public $email = null;

    /**
     * The user's location.
     *
     * @var string|array
     */
    public $location = null;

    /**
     * The user's description.
     *
     * @var string
     */
    public $description = null;

    /**
     * The user's image URL.
     *
     * @var string
     */
    public $imageUrl = null;

    /**
     * The users' URLs.
     *
     * @var string|array
     */
    public $urls = array();

    /**
     * Any extra data.
     *
     * @var array
     */
    public $extra = array();

    /**
     * Set a property on the user.
     *
     * @param string $key
     * @param mixed  $value
     */
    public function __set($key, $value)
    {
        if (isset($this->{$key})) {
            $this->{$key} = $value;
        }
    }

    /**
     * Get a property from the user.
     *
     * @param string $key
     *
     * @return mixed
     */
    public function __get($key)
    {
        if (isset($this->{$key})) {
            return $this->{$key};
        }
    }

    /**
     * {@inheritDoc}
     */
    public function getIterator()
    {
        return new \ArrayIterator($this);
    }
}
PK       ! J    *  oauth1-client/src/Client/Server/Tumblr.phpnu         <?php

namespace League\OAuth1\Client\Server;

use League\OAuth1\Client\Credentials\TokenCredentials;

class Tumblr extends Server
{
    /**
     * {@inheritDoc}
     */
    public function urlTemporaryCredentials()
    {
        return 'https://www.tumblr.com/oauth/request_token';
    }

    /**
     * {@inheritDoc}
     */
    public function urlAuthorization()
    {
        return 'https://www.tumblr.com/oauth/authorize';
    }

    /**
     * {@inheritDoc}
     */
    public function urlTokenCredentials()
    {
        return 'https://www.tumblr.com/oauth/access_token';
    }

    /**
     * {@inheritDoc}
     */
    public function urlUserDetails()
    {
        return 'https://api.tumblr.com/v2/user/info';
    }

    /**
     * {@inheritDoc}
     */
    public function userDetails($data, TokenCredentials $tokenCredentials)
    {
        // If the API has broke, return nothing
        if (!isset($data['response']['user']) || !is_array($data['response']['user'])) {
            return;
        }

        $data = $data['response']['user'];

        $user = new User();

        $user->nickname = $data['name'];

        // Save all extra data
        $used = array('name');
        $user->extra = array_diff_key($data, array_flip($used));

        return $user;
    }

    /**
     * {@inheritDoc}
     */
    public function userUid($data, TokenCredentials $tokenCredentials)
    {
        if (!isset($data['response']['user']) || !is_array($data['response']['user'])) {
            return;
        }

        $data = $data['response']['user'];

        return $data['name'];
    }

    /**
     * {@inheritDoc}
     */
    public function userEmail($data, TokenCredentials $tokenCredentials)
    {
        return;
    }

    /**
     * {@inheritDoc}
     */
    public function userScreenName($data, TokenCredentials $tokenCredentials)
    {
        if (!isset($data['response']['user']) || !is_array($data['response']['user'])) {
            return;
        }

        $data = $data['response']['user'];

        return $data['name'];
    }
}
PK       ! qc      oauth1-client/composer.jsonnu         {
    "name": "league/oauth1-client",
    "description": "OAuth 1.0 Client Library",
    "license": "MIT",
    "require": {
        "php": ">=5.5.0",
        "guzzlehttp/guzzle": "^6.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^4.0",
        "mockery/mockery": "^0.9",
        "squizlabs/php_codesniffer": "^2.0"
    },
    "keywords": [
        "oauth",
        "oauth1",
        "authorization",
        "authentication",
        "idp",
        "identity",
        "sso",
        "single sign on",
        "bitbucket",
        "trello",
        "tumblr",
        "twitter"
    ],
    "authors": [
        {
            "name": "Ben Corlett",
            "email": "bencorlett@me.com",
            "homepage": "http://www.webcomm.com.au",
            "role": "Developer"
        }
    ],
    "autoload": {
        "psr-4": {
            "League\\OAuth1\\": "src/"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "1.0-dev"
        }
    }
}
PK       ! D      oauth1-client/phpunit.xmlnu         <?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true"
         stopOnFailure="false"
         bootstrap="./vendor/autoload.php"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true">
    <logging>
        <log type="coverage-html"
                target="./build/coverage/html"
                charset="UTF-8"
                highlight="false"
                lowUpperBound="35"
                highLowerBound="70"/>
          <log type="coverage-clover"
                target="./build/coverage/log/coverage.xml"/>
    </logging>
    <testsuites>
        <testsuite name="common">
            <directory suffix="Test.php">tests</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist>
            <directory suffix=".php">./src/</directory>
        </whitelist>
    </filter>
</phpunit>
PK       ! {_(   (     oauth1-client/.gitignorenu         /build
/vendor
/composer.lock
.DS_Store
PK       !     *  fractal/src/Resource/ResourceInterface.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Resource;

interface ResourceInterface
{
    /**
     * Get the resource key.
     *
     * @return string
     */
    public function getResourceKey();

    /**
     * Get the data.
     *
     * @return mixed
     */
    public function getData();

    /**
     * Get the transformer.
     *
     * @return callable|\League\Fractal\TransformerAbstract
     */
    public function getTransformer();

    /**
     * Set the data.
     *
     * @param mixed $data
     *
     * @return $this
     */
    public function setData($data);

    /**
     * Set the transformer.
     *
     * @param callable|\League\Fractal\TransformerAbstract $transformer
     *
     * @return $this
     */
    public function setTransformer($transformer);
}
PK       ! 9    )  fractal/src/Resource/ResourceAbstract.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Resource;

use League\Fractal\TransformerAbstract;

abstract class ResourceAbstract implements ResourceInterface
{
    /**
     * Any item to process.
     *
     * @var mixed
     */
    protected $data;

    /**
     * Array of meta data.
     *
     * @var array
     */
    protected $meta = [];

    /**
     * The resource key.
     *
     * @var string
     */
    protected $resourceKey;

    /**
     * A callable to process the data attached to this resource.
     *
     * @var callable|TransformerAbstract|null
     */
    protected $transformer;

    /**
     * Create a new resource instance.
     *
     * @param mixed                             $data
     * @param callable|TransformerAbstract|null $transformer
     * @param string                            $resourceKey
     */
    public function __construct($data = null, $transformer = null, $resourceKey = null)
    {
        $this->data = $data;
        $this->transformer = $transformer;
        $this->resourceKey = $resourceKey;
    }

    /**
     * Get the data.
     *
     * @return mixed
     */
    public function getData()
    {
        return $this->data;
    }

    /**
     * Set the data.
     *
     * @param mixed $data
     *
     * @return $this
     */
    public function setData($data)
    {
         $this->data = $data;

         return $this;
    }

    /**
     * Get the meta data.
     *
     * @return array
     */
    public function getMeta()
    {
        return $this->meta;
    }

    /**
     * Get the meta data.
     *
     * @param string $metaKey
     *
     * @return array
     */
    public function getMetaValue($metaKey)
    {
        return $this->meta[$metaKey];
    }

    /**
     * Get the resource key.
     *
     * @return string
     */
    public function getResourceKey()
    {
        return $this->resourceKey;
    }

    /**
     * Get the transformer.
     *
     * @return callable|TransformerAbstract
     */
    public function getTransformer()
    {
        return $this->transformer;
    }

    /**
     * Set the transformer.
     *
     * @param callable|TransformerAbstract $transformer
     *
     * @return $this
     */
    public function setTransformer($transformer)
    {
        $this->transformer = $transformer;

        return $this;
    }

    /**
     * Set the meta data.
     *
     * @param array $meta
     *
     * @return $this
     */
    public function setMeta(array $meta)
    {
        $this->meta = $meta;

        return $this;
    }

    /**
     * Set the meta data.
     *
     * @param string $metaKey
     * @param mixed  $metaValue
     *
     * @return $this
     */
    public function setMetaValue($metaKey, $metaValue)
    {
        $this->meta[$metaKey] = $metaValue;

        return $this;
    }

    /**
     * Set the resource key.
     *
     * @param string $resourceKey
     *
     * @return $this
     */
    public function setResourceKey($resourceKey)
    {
        $this->resourceKey = $resourceKey;

        return $this;
    }
}
PK       ! x    #  fractal/src/Resource/Collection.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Resource;

use ArrayIterator;
use League\Fractal\Pagination\CursorInterface;
use League\Fractal\Pagination\PaginatorInterface;

/**
 * Resource Collection
 *
 * The data can be a collection of any sort of data, as long as the
 * "collection" is either array or an object implementing ArrayIterator.
 */
class Collection extends ResourceAbstract
{
    /**
     * A collection of data.
     *
     * @var array|ArrayIterator
     */
    protected $data;

    /**
     * The paginator instance.
     *
     * @var PaginatorInterface
     */
    protected $paginator;

    /**
     * The cursor instance.
     *
     * @var CursorInterface
     */
    protected $cursor;

    /**
     * Get the paginator instance.
     *
     * @return PaginatorInterface
     */
    public function getPaginator()
    {
        return $this->paginator;
    }

    /**
     * Determine if the resource has a paginator implementation.
     *
     * @return bool
     */
    public function hasPaginator()
    {
        return $this->paginator instanceof PaginatorInterface;
    }

    /**
     * Get the cursor instance.
     *
     * @return CursorInterface
     */
    public function getCursor()
    {
        return $this->cursor;
    }

    /**
     * Determine if the resource has a cursor implementation.
     *
     * @return bool
     */
    public function hasCursor()
    {
        return $this->cursor instanceof CursorInterface;
    }

    /**
     * Set the paginator instance.
     *
     * @param PaginatorInterface $paginator
     *
     * @return $this
     */
    public function setPaginator(PaginatorInterface $paginator)
    {
        $this->paginator = $paginator;

        return $this;
    }

    /**
     * Set the cursor instance.
     *
     * @param CursorInterface $cursor
     *
     * @return $this
     */
    public function setCursor(CursorInterface $cursor)
    {
        $this->cursor = $cursor;

        return $this;
    }
}
PK       ! %    %  fractal/src/Resource/NullResource.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Resource;

/**
 * Null Resource
 *
 * The Null Resource represents a resource that doesn't exist. This can be
 * useful to indicate that a certain relationship is null in some output
 * formats (e.g. JSON API), which require even a relationship that is null at
 * the moment to be listed.
 */
class NullResource extends ResourceAbstract
{
    /**
     * Get the data.
     *
     * @return mixed
     */
    public function getData()
    {
        // Null has no data associated with it.
    }
}
PK       !       fractal/src/Resource/Item.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Resource;

/**
 * Item Resource
 *
 * The Item Resource can store any mixed data, usually an ORM, ODM or
 * other sort of intelligent result, DataMapper model, etc but could
 * be a basic array, object, or whatever you like.
 */
class Item extends ResourceAbstract
{
    //
}
PK       ! }    "  fractal/src/Resource/Primitive.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Resource;

/**
 * Primitive Resource
 *
 * The Primitive Resource can store any primitive data, like a string, integer,
 * float, double etc.
 */
class Primitive extends ResourceAbstract
{
    //
}
PK       ! s      fractal/src/ScopeFactory.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal;

use League\Fractal\Resource\ResourceInterface;

class ScopeFactory implements ScopeFactoryInterface
{
    /**
     * @param Manager $manager
     * @param ResourceInterface $resource
     * @param string|null $scopeIdentifier
     * @return Scope
     */
    public function createScopeFor(Manager $manager, ResourceInterface $resource, $scopeIdentifier = null)
    {
        return new Scope($manager, $resource, $scopeIdentifier);
    }

    /**
     * @param Manager $manager
     * @param Scope $parentScopeInstance
     * @param ResourceInterface $resource
     * @param string|null $scopeIdentifier
     * @return Scope
     */
    public function createChildScopeFor(Manager $manager, Scope $parentScopeInstance, ResourceInterface $resource, $scopeIdentifier = null)
    {
        $scopeInstance = $this->createScopeFor($manager, $resource, $scopeIdentifier);

        // This will be the new children list of parents (parents parents, plus the parent)
        $scopeArray = $parentScopeInstance->getParentScopes();
        $scopeArray[] = $parentScopeInstance->getScopeIdentifier();

        $scopeInstance->setParentScopes($scopeArray);

        return $scopeInstance;
    }
}
PK       ! O$q  q  %  fractal/src/Serializer/Serializer.phpnu         <?php
namespace League\Fractal\Serializer;

use League\Fractal\Pagination\CursorInterface;
use League\Fractal\Pagination\PaginatorInterface;
use League\Fractal\Resource\ResourceInterface;

interface Serializer
{
    /**
     * Serialize a collection.
     *
     * @param string $resourceKey
     * @param array $data
     *
     * @return array
     */
    public function collection($resourceKey, array $data);

    /**
     * Serialize an item.
     *
     * @param string $resourceKey
     * @param array $data
     *
     * @return array
     */
    public function item($resourceKey, array $data);

    /**
     * Serialize null resource.
     *
     * @return array
     */
    public function null();

    /**
     * Serialize the included data.
     *
     * @param ResourceInterface $resource
     * @param array $data
     *
     * @return array
     */
    public function includedData(ResourceInterface $resource, array $data);

    /**
     * Serialize the meta.
     *
     * @param array $meta
     *
     * @return array
     */
    public function meta(array $meta);

    /**
     * Serialize the paginator.
     *
     * @param PaginatorInterface $paginator
     *
     * @return array
     */
    public function paginator(PaginatorInterface $paginator);

    /**
     * Serialize the cursor.
     *
     * @param CursorInterface $cursor
     *
     * @return array
     */
    public function cursor(CursorInterface $cursor);

    public function mergeIncludes($transformedData, $includedData);

    /**
     * Indicates if includes should be side-loaded.
     *
     * @return bool
     */
    public function sideloadIncludes();

    /**
     * Hook for the serializer to inject custom data based on the relationships of the resource.
     *
     * @param array $data
     * @param array $rawIncludedData
     *
     * @return array
     */
    public function injectData($data, $rawIncludedData);

    /**
     * Hook for the serializer to modify the final list of includes.
     *
     * @param array $includedData
     * @param array $data
     *
     * @return array
     */
    public function filterIncludes($includedData, $data);
}PK       ! dH    .  fractal/src/Serializer/DataArraySerializer.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Serializer;

class DataArraySerializer extends ArraySerializer
{
    /**
     * Serialize a collection.
     *
     * @param string $resourceKey
     * @param array  $data
     *
     * @return array
     */
    public function collection($resourceKey, array $data)
    {
        return ['data' => $data];
    }

    /**
     * Serialize an item.
     *
     * @param string $resourceKey
     * @param array  $data
     *
     * @return array
     */
    public function item($resourceKey, array $data)
    {
        return ['data' => $data];
    }

    /**
     * Serialize null resource.
     *
     * @return array
     */
    public function null()
    {
        return ['data' => []];
    }
}
PK       ! ̩2  2  -  fractal/src/Serializer/SerializerAbstract.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Serializer;

use League\Fractal\Pagination\CursorInterface;
use League\Fractal\Pagination\PaginatorInterface;
use League\Fractal\Resource\ResourceInterface;

abstract class SerializerAbstract implements Serializer
{
    /**
     * Serialize a collection.
     *
     * @param string $resourceKey
     * @param array  $data
     *
     * @return array
     */
    abstract public function collection($resourceKey, array $data);

    /**
     * Serialize an item.
     *
     * @param string $resourceKey
     * @param array  $data
     *
     * @return array
     */
    abstract public function item($resourceKey, array $data);

    /**
     * Serialize null resource.
     *
     * @return array
     */
    abstract public function null();

    /**
     * Serialize the included data.
     *
     * @param ResourceInterface $resource
     * @param array             $data
     *
     * @return array
     */
    abstract public function includedData(ResourceInterface $resource, array $data);

    /**
     * Serialize the meta.
     *
     * @param array $meta
     *
     * @return array
     */
    abstract public function meta(array $meta);

    /**
     * Serialize the paginator.
     *
     * @param PaginatorInterface $paginator
     *
     * @return array
     */
    abstract public function paginator(PaginatorInterface $paginator);

    /**
     * Serialize the cursor.
     *
     * @param CursorInterface $cursor
     *
     * @return array
     */
    abstract public function cursor(CursorInterface $cursor);

    public function mergeIncludes($transformedData, $includedData)
    {
        // If the serializer does not want the includes to be side-loaded then
        // the included data must be merged with the transformed data.
        if (! $this->sideloadIncludes()) {
            return array_merge($transformedData, $includedData);
        }

        return $transformedData;
    }

    /**
     * Indicates if includes should be side-loaded.
     *
     * @return bool
     */
    public function sideloadIncludes()
    {
        return false;
    }

    /**
     * Hook for the serializer to inject custom data based on the relationships of the resource.
     *
     * @param array $data
     * @param array $rawIncludedData
     *
     * @return array
     */
    public function injectData($data, $rawIncludedData)
    {
        return $data;
    }

    /**
     * Hook for the serializer to modify the final list of includes.
     *
     * @param array             $includedData
     * @param array             $data
     *
     * @return array
     */
    public function filterIncludes($includedData, $data)
    {
        return $includedData;
    }

    /**
     * Get the mandatory fields for the serializer
     *
     * @return array
     */
    public function getMandatoryFields()
    {
        return [];
    }
}
PK       ! "м    *  fractal/src/Serializer/ArraySerializer.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Serializer;

use League\Fractal\Pagination\CursorInterface;
use League\Fractal\Pagination\PaginatorInterface;
use League\Fractal\Resource\ResourceInterface;

class ArraySerializer extends SerializerAbstract
{
    /**
     * Serialize a collection.
     *
     * @param string $resourceKey
     * @param array  $data
     *
     * @return array
     */
    public function collection($resourceKey, array $data)
    {
        return [$resourceKey ?: 'data' => $data];
    }

    /**
     * Serialize an item.
     *
     * @param string $resourceKey
     * @param array  $data
     *
     * @return array
     */
    public function item($resourceKey, array $data)
    {
        return $data;
    }

    /**
     * Serialize null resource.
     *
     * @return array
     */
    public function null()
    {
        return [];
    }

    /**
     * Serialize the included data.
     *
     * @param ResourceInterface $resource
     * @param array             $data
     *
     * @return array
     */
    public function includedData(ResourceInterface $resource, array $data)
    {
        return $data;
    }

    /**
     * Serialize the meta.
     *
     * @param array $meta
     *
     * @return array
     */
    public function meta(array $meta)
    {
        if (empty($meta)) {
            return [];
        }

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

    /**
     * Serialize the paginator.
     *
     * @param PaginatorInterface $paginator
     *
     * @return array
     */
    public function paginator(PaginatorInterface $paginator)
    {
        $currentPage = (int) $paginator->getCurrentPage();
        $lastPage = (int) $paginator->getLastPage();

        $pagination = [
            'total' => (int) $paginator->getTotal(),
            'count' => (int) $paginator->getCount(),
            'per_page' => (int) $paginator->getPerPage(),
            'current_page' => $currentPage,
            'total_pages' => $lastPage,
        ];

        $pagination['links'] = [];

        if ($currentPage > 1) {
            $pagination['links']['previous'] = $paginator->getUrl($currentPage - 1);
        }

        if ($currentPage < $lastPage) {
            $pagination['links']['next'] = $paginator->getUrl($currentPage + 1);
        }

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

    /**
     * Serialize the cursor.
     *
     * @param CursorInterface $cursor
     *
     * @return array
     */
    public function cursor(CursorInterface $cursor)
    {
        $cursor = [
            'current' => $cursor->getCurrent(),
            'prev' => $cursor->getPrev(),
            'next' => $cursor->getNext(),
            'count' => (int) $cursor->getCount(),
        ];

        return ['cursor' => $cursor];
    }
}
PK       ! u<  <  ,  fractal/src/Serializer/JsonApiSerializer.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Serializer;

use InvalidArgumentException;
use League\Fractal\Pagination\PaginatorInterface;
use League\Fractal\Resource\ResourceInterface;

class JsonApiSerializer extends ArraySerializer
{
    protected $baseUrl;
    protected $rootObjects;

    /**
     * JsonApiSerializer constructor.
     *
     * @param string $baseUrl
     */
    public function __construct($baseUrl = null)
    {
        $this->baseUrl = $baseUrl;
        $this->rootObjects = [];
    }

    /**
     * Serialize a collection.
     *
     * @param string $resourceKey
     * @param array $data
     *
     * @return array
     */
    public function collection($resourceKey, array $data)
    {
        $resources = [];

        foreach ($data as $resource) {
            $resources[] = $this->item($resourceKey, $resource)['data'];
        }

        return ['data' => $resources];
    }

    /**
     * Serialize an item.
     *
     * @param string $resourceKey
     * @param array $data
     *
     * @return array
     */
    public function item($resourceKey, array $data)
    {
        $id = $this->getIdFromData($data);

        $resource = [
            'data' => [
                'type' => $resourceKey,
                'id' => "$id",
                'attributes' => $data,
            ],
        ];

        unset($resource['data']['attributes']['id']);

        if(isset($resource['data']['attributes']['links'])) {
            $custom_links = $data['links'];
            unset($resource['data']['attributes']['links']);
        }

        if (isset($resource['data']['attributes']['meta'])){
            $resource['data']['meta'] = $data['meta'];
            unset($resource['data']['attributes']['meta']);
        }

        if ($this->shouldIncludeLinks()) {
            $resource['data']['links'] = [
                'self' => "{$this->baseUrl}/$resourceKey/$id",
            ];
            if(isset($custom_links)) {
                $resource['data']['links'] = array_merge($custom_links, $resource['data']['links']);
            }
        }

        return $resource;
    }

    /**
     * Serialize the paginator.
     *
     * @param PaginatorInterface $paginator
     *
     * @return array
     */
    public function paginator(PaginatorInterface $paginator)
    {
        $currentPage = (int)$paginator->getCurrentPage();
        $lastPage = (int)$paginator->getLastPage();

        $pagination = [
            'total' => (int)$paginator->getTotal(),
            'count' => (int)$paginator->getCount(),
            'per_page' => (int)$paginator->getPerPage(),
            'current_page' => $currentPage,
            'total_pages' => $lastPage,
        ];

        $pagination['links'] = [];

        $pagination['links']['self'] = $paginator->getUrl($currentPage);
        $pagination['links']['first'] = $paginator->getUrl(1);

        if ($currentPage > 1) {
            $pagination['links']['prev'] = $paginator->getUrl($currentPage - 1);
        }

        if ($currentPage < $lastPage) {
            $pagination['links']['next'] = $paginator->getUrl($currentPage + 1);
        }

        $pagination['links']['last'] = $paginator->getUrl($lastPage);

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

    /**
     * Serialize the meta.
     *
     * @param array $meta
     *
     * @return array
     */
    public function meta(array $meta)
    {
        if (empty($meta)) {
            return [];
        }

        $result['meta'] = $meta;

        if (array_key_exists('pagination', $result['meta'])) {
            $result['links'] = $result['meta']['pagination']['links'];
            unset($result['meta']['pagination']['links']);
        }

        return $result;
    }

    /**
     * @return array
     */
    public function null()
    {
        return [
            'data' => null,
        ];
    }

    /**
     * Serialize the included data.
     *
     * @param ResourceInterface $resource
     * @param array $data
     *
     * @return array
     */
    public function includedData(ResourceInterface $resource, array $data)
    {
        list($serializedData, $linkedIds) = $this->pullOutNestedIncludedData($data);

        foreach ($data as $value) {
            foreach ($value as $includeObject) {
                if ($this->isNull($includeObject) || $this->isEmpty($includeObject)) {
                    continue;
                }

                $includeObjects = $this->createIncludeObjects($includeObject);
                list($serializedData, $linkedIds) = $this->serializeIncludedObjectsWithCacheKey($includeObjects, $linkedIds, $serializedData);
            }
        }

        return empty($serializedData) ? [] : ['included' => $serializedData];
    }

    /**
     * Indicates if includes should be side-loaded.
     *
     * @return bool
     */
    public function sideloadIncludes()
    {
        return true;
    }

    /**
     * @param array $data
     * @param array $includedData
     *
     * @return array
     */
    public function injectData($data, $includedData)
    {
        $relationships = $this->parseRelationships($includedData);

        if (!empty($relationships)) {
            $data = $this->fillRelationships($data, $relationships);
        }

        return $data;
    }

    /**
     * Hook to manipulate the final sideloaded includes.
     * The JSON API specification does not allow the root object to be included
     * into the sideloaded `included`-array. We have to make sure it is
     * filtered out, in case some object links to the root object in a
     * relationship.
     *
     * @param array $includedData
     * @param array $data
     *
     * @return array
     */
    public function filterIncludes($includedData, $data)
    {
        if (!isset($includedData['included'])) {
            return $includedData;
        }

        // Create the RootObjects
        $this->createRootObjects($data);

        // Filter out the root objects
        $filteredIncludes = array_filter($includedData['included'], [$this, 'filterRootObject']);

        // Reset array indizes
        $includedData['included'] = array_merge([], $filteredIncludes);

        return $includedData;
    }

    /**
     * Get the mandatory fields for the serializer
     *
     * @return array
     */
    public function getMandatoryFields()
    {
        return ['id'];
    }

    /**
     * Filter function to delete root objects from array.
     *
     * @param array $object
     *
     * @return bool
     */
    protected function filterRootObject($object)
    {
        return !$this->isRootObject($object);
    }

    /**
     * Set the root objects of the JSON API tree.
     *
     * @param array $objects
     */
    protected function setRootObjects(array $objects = [])
    {
        $this->rootObjects = array_map(function ($object) {
            return "{$object['type']}:{$object['id']}";
        }, $objects);
    }

    /**
     * Determines whether an object is a root object of the JSON API tree.
     *
     * @param array $object
     *
     * @return bool
     */
    protected function isRootObject($object)
    {
        $objectKey = "{$object['type']}:{$object['id']}";
        return in_array($objectKey, $this->rootObjects);
    }

    /**
     * @param array $data
     *
     * @return bool
     */
    protected function isCollection($data)
    {
        return array_key_exists('data', $data) &&
        array_key_exists(0, $data['data']);
    }

    /**
     * @param array $data
     *
     * @return bool
     */
    protected function isNull($data)
    {
        return array_key_exists('data', $data) && $data['data'] === null;
    }

    /**
     * @param array $data
     *
     * @return bool
     */
    protected function isEmpty($data)
    {
        return array_key_exists('data', $data) && $data['data'] === [];
    }

    /**
     * @param array $data
     * @param array $relationships
     *
     * @return array
     */
    protected function fillRelationships($data, $relationships)
    {
        if ($this->isCollection($data)) {
            foreach ($relationships as $key => $relationship) {
                $data = $this->fillRelationshipAsCollection($data, $relationship, $key);
            }
        } else { // Single resource
            foreach ($relationships as $key => $relationship) {
                $data = $this->fillRelationshipAsSingleResource($data, $relationship, $key);
            }
        }

        return $data;
    }

    /**
     * @param array $includedData
     *
     * @return array
     */
    protected function parseRelationships($includedData)
    {
        $relationships = [];

        foreach ($includedData as $key => $inclusion) {
            foreach ($inclusion as $includeKey => $includeObject) {
                $relationships = $this->buildRelationships($includeKey, $relationships, $includeObject, $key);
            }
        }

        return $relationships;
    }

    /**
     * @param array $data
     *
     * @return integer
     */
    protected function getIdFromData(array $data)
    {
        if (!array_key_exists('id', $data)) {
            throw new InvalidArgumentException(
                'JSON API resource objects MUST have a valid id'
            );
        }
        return $data['id'];
    }

    /**
     * Keep all sideloaded inclusion data on the top level.
     *
     * @param array $data
     *
     * @return array
     */
    protected function pullOutNestedIncludedData(array $data)
    {
        $includedData = [];
        $linkedIds = [];

        foreach ($data as $value) {
            foreach ($value as $includeObject) {
                if (isset($includeObject['included'])) {
                    list($includedData, $linkedIds) = $this->serializeIncludedObjectsWithCacheKey($includeObject['included'], $linkedIds, $includedData);
                }
            }
        }

        return [$includedData, $linkedIds];
    }

    /**
     * Whether or not the serializer should include `links` for resource objects.
     *
     * @return bool
     */
    protected function shouldIncludeLinks()
    {
        return $this->baseUrl !== null;
    }

    /**
     * Check if the objects are part of a collection or not
     *
     * @param $includeObject
     *
     * @return array
     */
    private function createIncludeObjects($includeObject)
    {
        if ($this->isCollection($includeObject)) {
            $includeObjects = $includeObject['data'];

            return $includeObjects;
        } else {
            $includeObjects = [$includeObject['data']];

            return $includeObjects;
        }
    }

    /**
     * Sets the RootObjects, either as collection or not.
     *
     * @param $data
     */
    private function createRootObjects($data)
    {
        if ($this->isCollection($data)) {
            $this->setRootObjects($data['data']);
        } else {
            $this->setRootObjects([$data['data']]);
        }
    }


    /**
     * Loops over the relationships of the provided data and formats it
     *
     * @param $data
     * @param $relationship
     * @param $key
     *
     * @return array
     */
    private function fillRelationshipAsCollection($data, $relationship, $key)
    {
        foreach ($relationship as $index => $relationshipData) {
            $data['data'][$index]['relationships'][$key] = $relationshipData;

            if ($this->shouldIncludeLinks()) {
                $data['data'][$index]['relationships'][$key] = array_merge([
                    'links' => [
                        'self' => "{$this->baseUrl}/{$data['data'][$index]['type']}/{$data['data'][$index]['id']}/relationships/$key",
                        'related' => "{$this->baseUrl}/{$data['data'][$index]['type']}/{$data['data'][$index]['id']}/$key",
                    ],
                ], $data['data'][$index]['relationships'][$key]);
            }
        }

        return $data;
    }


    /**
     * @param $data
     * @param $relationship
     * @param $key
     *
     * @return array
     */
    private function fillRelationshipAsSingleResource($data, $relationship, $key)
    {
        $data['data']['relationships'][$key] = $relationship[0];

        if ($this->shouldIncludeLinks()) {
            $data['data']['relationships'][$key] = array_merge([
                'links' => [
                    'self' => "{$this->baseUrl}/{$data['data']['type']}/{$data['data']['id']}/relationships/$key",
                    'related' => "{$this->baseUrl}/{$data['data']['type']}/{$data['data']['id']}/$key",
                ],
            ], $data['data']['relationships'][$key]);

            return $data;
        }
        return $data;
    }

    /**
     * @param $includeKey
     * @param $relationships
     * @param $includeObject
     * @param $key
     *
     * @return array
     */
    private function buildRelationships($includeKey, $relationships, $includeObject, $key)
    {
        $relationships = $this->addIncludekeyToRelationsIfNotSet($includeKey, $relationships);

        if ($this->isNull($includeObject)) {
            $relationship = $this->null();
        } elseif ($this->isEmpty($includeObject)) {
            $relationship = [
                'data' => [],
            ];
        } elseif ($this->isCollection($includeObject)) {
            $relationship = ['data' => []];

            $relationship = $this->addIncludedDataToRelationship($includeObject, $relationship);
        } else {
            $relationship = [
                'data' => [
                    'type' => $includeObject['data']['type'],
                    'id' => $includeObject['data']['id'],
                ],
            ];
        }

        $relationships[$includeKey][$key] = $relationship;

        return $relationships;
    }

    /**
     * @param $includeKey
     * @param $relationships
     *
     * @return array
     */
    private function addIncludekeyToRelationsIfNotSet($includeKey, $relationships)
    {
        if (!array_key_exists($includeKey, $relationships)) {
            $relationships[$includeKey] = [];
            return $relationships;
        }

        return $relationships;
    }

    /**
     * @param $includeObject
     * @param $relationship
     *
     * @return array
     */
    private function addIncludedDataToRelationship($includeObject, $relationship)
    {
        foreach ($includeObject['data'] as $object) {
            $relationship['data'][] = [
                'type' => $object['type'],
                'id' => $object['id'],
            ];
        }

        return $relationship;
    }

    /**
     * @param $includeObjects
     * @param $linkedIds
     * @param $serializedData
     *
     * @return array
     */
    private function serializeIncludedObjectsWithCacheKey($includeObjects, $linkedIds, $serializedData)
    {
        foreach ($includeObjects as $object) {
            $includeType = $object['type'];
            $includeId = $object['id'];
            $cacheKey = "$includeType:$includeId";
            if (!array_key_exists($cacheKey, $linkedIds)) {
                $serializedData[] = $object;
                $linkedIds[$cacheKey] = $object;
            }
        }
        return [$serializedData, $linkedIds];
    }
}
PK       !  c    #  fractal/src/TransformerAbstract.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal;

use League\Fractal\Resource\Collection;
use League\Fractal\Resource\Item;
use League\Fractal\Resource\NullResource;
use League\Fractal\Resource\Primitive;
use League\Fractal\Resource\ResourceInterface;

/**
 * Transformer Abstract
 *
 * All Transformer classes should extend this to utilize the convenience methods
 * collection() and item(), and make the self::$availableIncludes property available.
 * Extend it and add a `transform()` method to transform any default or included data
 * into a basic array.
 */
abstract class TransformerAbstract
{
    /**
     * Resources that can be included if requested.
     *
     * @var array
     */
    protected $availableIncludes = [];

    /**
     * Include resources without needing it to be requested.
     *
     * @var array
     */
    protected $defaultIncludes = [];

    /**
     * The transformer should know about the current scope, so we can fetch relevant params.
     *
     * @var Scope
     */
    protected $currentScope;

    /**
     * Getter for availableIncludes.
     *
     * @return array
     */
    public function getAvailableIncludes()
    {
        return $this->availableIncludes;
    }

    /**
     * Getter for defaultIncludes.
     *
     * @return array
     */
    public function getDefaultIncludes()
    {
        return $this->defaultIncludes;
    }

    /**
     * Getter for currentScope.
     *
     * @return \League\Fractal\Scope
     */
    public function getCurrentScope()
    {
        return $this->currentScope;
    }

    /**
     * Figure out which includes we need.
     *
     * @internal
     *
     * @param Scope $scope
     *
     * @return array
     */
    private function figureOutWhichIncludes(Scope $scope)
    {
        $includes = $this->getDefaultIncludes();

        foreach ($this->getAvailableIncludes() as $include) {
            if ($scope->isRequested($include)) {
                $includes[] = $include;
            }
        }

        foreach ($includes as $include) {
            if ($scope->isExcluded($include)) {
                $includes = array_diff($includes, [$include]);
            }
        }

        return $includes;
    }

    /**
     * This method is fired to loop through available includes, see if any of
     * them are requested and permitted for this scope.
     *
     * @internal
     *
     * @param Scope $scope
     * @param mixed $data
     *
     * @return array
     */
    public function processIncludedResources(Scope $scope, $data)
    {
        $includedData = [];

        $includes = $this->figureOutWhichIncludes($scope);

        foreach ($includes as $include) {
            $includedData = $this->includeResourceIfAvailable(
                $scope,
                $data,
                $includedData,
                $include
            );
        }

        return $includedData === [] ? false : $includedData;
    }

    /**
     * Include a resource only if it is available on the method.
     *
     * @internal
     *
     * @param Scope  $scope
     * @param mixed  $data
     * @param array  $includedData
     * @param string $include
     *
     * @return array
     */
    private function includeResourceIfAvailable(
        Scope $scope,
        $data,
        $includedData,
        $include
    ) {
        if ($resource = $this->callIncludeMethod($scope, $include, $data)) {
            $childScope = $scope->embedChildScope($include, $resource);

            if ($childScope->getResource() instanceof Primitive) {
                $includedData[$include] = $childScope->transformPrimitiveResource();
            } else {
                $includedData[$include] = $childScope->toArray();
            }
        }

        return $includedData;
    }

    /**
     * Call Include Method.
     *
     * @internal
     *
     * @param Scope  $scope
     * @param string $includeName
     * @param mixed  $data
     *
     * @throws \Exception
     *
     * @return \League\Fractal\Resource\ResourceInterface
     */
    protected function callIncludeMethod(Scope $scope, $includeName, $data)
    {
        $scopeIdentifier = $scope->getIdentifier($includeName);
        $params = $scope->getManager()->getIncludeParams($scopeIdentifier);

        // Check if the method name actually exists
        $methodName = 'include'.str_replace(' ', '', ucwords(str_replace('_', ' ', str_replace('-', ' ', $includeName))));

        $resource = call_user_func([$this, $methodName], $data, $params);

        if ($resource === null) {
            return false;
        }

        if (! $resource instanceof ResourceInterface) {
            throw new \Exception(sprintf(
                'Invalid return value from %s::%s(). Expected %s, received %s.',
                __CLASS__,
                $methodName,
                'League\Fractal\Resource\ResourceInterface',
                is_object($resource) ? get_class($resource) : gettype($resource)
            ));
        }

        return $resource;
    }

    /**
     * Setter for availableIncludes.
     *
     * @param array $availableIncludes
     *
     * @return $this
     */
    public function setAvailableIncludes($availableIncludes)
    {
        $this->availableIncludes = $availableIncludes;

        return $this;
    }

    /**
     * Setter for defaultIncludes.
     *
     * @param array $defaultIncludes
     *
     * @return $this
     */
    public function setDefaultIncludes($defaultIncludes)
    {
        $this->defaultIncludes = $defaultIncludes;

        return $this;
    }

    /**
     * Setter for currentScope.
     *
     * @param Scope $currentScope
     *
     * @return $this
     */
    public function setCurrentScope($currentScope)
    {
        $this->currentScope = $currentScope;

        return $this;
    }

    /**
     * Create a new primitive resource object.
     *
     * @param mixed                        $data
     * @param callable|null                $transformer
     * @param string                       $resourceKey
     *
     * @return Primitive
     */
    protected function primitive($data, $transformer = null, $resourceKey = null)
    {
        return new Primitive($data, $transformer, $resourceKey);
    }

    /**
     * Create a new item resource object.
     *
     * @param mixed                        $data
     * @param TransformerAbstract|callable $transformer
     * @param string                       $resourceKey
     *
     * @return Item
     */
    protected function item($data, $transformer, $resourceKey = null)
    {
        return new Item($data, $transformer, $resourceKey);
    }

    /**
     * Create a new collection resource object.
     *
     * @param mixed                        $data
     * @param TransformerAbstract|callable $transformer
     * @param string                       $resourceKey
     *
     * @return Collection
     */
    protected function collection($data, $transformer, $resourceKey = null)
    {
        return new Collection($data, $transformer, $resourceKey);
    }

    /**
     * Create a new null resource object.
     *
     * @return NullResource
     */
    protected function null()
    {
        return new NullResource();
    }
}
PK       ! ks%  %    fractal/src/Manager.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal;

use League\Fractal\Resource\ResourceInterface;
use League\Fractal\Serializer\DataArraySerializer;
use League\Fractal\Serializer\SerializerAbstract;

/**
 * Manager
 *
 * Not a wildly creative name, but the manager is what a Fractal user will interact
 * with the most. The manager has various configurable options, and allows users
 * to create the "root scope" easily.
 */
class Manager
{
    /**
     * Array of scope identifiers for resources to include.
     *
     * @var array
     */
    protected $requestedIncludes = [];

    /**
     * Array of scope identifiers for resources to exclude.
     *
     * @var array
     */
    protected $requestedExcludes = [];

    /**
     * Array of requested fieldsets.
     *
     * @var array
     */
    protected $requestedFieldsets = [];

    /**
     * Array containing modifiers as keys and an array value of params.
     *
     * @var array
     */
    protected $includeParams = [];

    /**
     * The character used to separate modifier parameters.
     *
     * @var string
     */
    protected $paramDelimiter = '|';

    /**
     * Upper limit to how many levels of included data are allowed.
     *
     * @var int
     */
    protected $recursionLimit = 10;

    /**
     * Serializer.
     *
     * @var SerializerAbstract
     */
    protected $serializer;

    /**
     * Factory used to create new configured scopes.
     *
     * @var ScopeFactoryInterface
     */
    private $scopeFactory;

    public function __construct(ScopeFactoryInterface $scopeFactory = null)
    {
        $this->scopeFactory = $scopeFactory ?: new ScopeFactory();
    }

    /**
     * Create Data.
     *
     * Main method to kick this all off. Make a resource then pass it over, and use toArray()
     *
     * @param ResourceInterface $resource
     * @param string            $scopeIdentifier
     * @param Scope             $parentScopeInstance
     *
     * @return Scope
     */
    public function createData(ResourceInterface $resource, $scopeIdentifier = null, Scope $parentScopeInstance = null)
    {
        if ($parentScopeInstance !== null) {
            return $this->scopeFactory->createChildScopeFor($this, $parentScopeInstance, $resource, $scopeIdentifier);
        }

        return $this->scopeFactory->createScopeFor($this, $resource, $scopeIdentifier);
    }

    /**
     * Get Include Params.
     *
     * @param string $include
     *
     * @return \League\Fractal\ParamBag
     */
    public function getIncludeParams($include)
    {
        $params = isset($this->includeParams[$include]) ? $this->includeParams[$include] : [];

        return new ParamBag($params);
    }

    /**
     * Get Requested Includes.
     *
     * @return array
     */
    public function getRequestedIncludes()
    {
        return $this->requestedIncludes;
    }

    /**
     * Get Requested Excludes.
     *
     * @return array
     */
    public function getRequestedExcludes()
    {
        return $this->requestedExcludes;
    }

    /**
     * Get Serializer.
     *
     * @return SerializerAbstract
     */
    public function getSerializer()
    {
        if (! $this->serializer) {
            $this->setSerializer(new DataArraySerializer());
        }

        return $this->serializer;
    }

    /**
     * Parse Include String.
     *
     * @param array|string $includes Array or csv string of resources to include
     *
     * @return $this
     */
    public function parseIncludes($includes)
    {
        // Wipe these before we go again
        $this->requestedIncludes = $this->includeParams = [];

        if (is_string($includes)) {
            $includes = explode(',', $includes);
        }

        if (! is_array($includes)) {
            throw new \InvalidArgumentException(
                'The parseIncludes() method expects a string or an array. '.gettype($includes).' given'
            );
        }

        foreach ($includes as $include) {
            list($includeName, $allModifiersStr) = array_pad(explode(':', $include, 2), 2, null);

            // Trim it down to a cool level of recursion
            $includeName = $this->trimToAcceptableRecursionLevel($includeName);

            if (in_array($includeName, $this->requestedIncludes)) {
                continue;
            }
            $this->requestedIncludes[] = $includeName;

            // No Params? Bored
            if ($allModifiersStr === null) {
                continue;
            }

            // Matches multiple instances of 'something(foo|bar|baz)' in the string
            // I guess it ignores : so you could use anything, but probably don't do that
            preg_match_all('/([\w]+)(\(([^\)]+)\))?/', $allModifiersStr, $allModifiersArr);

            // [0] is full matched strings...
            $modifierCount = count($allModifiersArr[0]);

            $modifierArr = [];

            for ($modifierIt = 0; $modifierIt < $modifierCount; $modifierIt++) {
                // [1] is the modifier
                $modifierName = $allModifiersArr[1][$modifierIt];

                // and [3] is delimited params
                $modifierParamStr = $allModifiersArr[3][$modifierIt];

                // Make modifier array key with an array of params as the value
                $modifierArr[$modifierName] = explode($this->paramDelimiter, $modifierParamStr);
            }

            $this->includeParams[$includeName] = $modifierArr;
        }

        // This should be optional and public someday, but without it includes would never show up
        $this->autoIncludeParents();

        return $this;
    }

    /**
     * Parse field parameter.
     *
     * @param array $fieldsets Array of fields to include. It must be an array
     *                         whose keys are resource types and values a string
     *                         of the fields to return, separated by a comma
     *
     * @return $this
     */
    public function parseFieldsets(array $fieldsets)
    {
        $this->requestedFieldsets = [];
        foreach ($fieldsets as $type => $fields) {
            //Remove empty and repeated fields
            $this->requestedFieldsets[$type] = array_unique(array_filter(explode(',', $fields)));
        }
        return $this;
    }

    /**
     * Get requested fieldsets.
     *
     * @return array
     */
    public function getRequestedFieldsets()
    {
        return $this->requestedFieldsets;
    }

    /**
     * Get fieldset params for the specified type.
     *
     * @param string $type
     *
     * @return \League\Fractal\ParamBag|null
     */
    public function getFieldset($type)
    {
        return !isset($this->requestedFieldsets[$type]) ?
            null :
            new ParamBag($this->requestedFieldsets[$type]);
    }

    /**
     * Parse Exclude String.
     *
     * @param array|string $excludes Array or csv string of resources to exclude
     *
     * @return $this
     */
    public function parseExcludes($excludes)
    {
        $this->requestedExcludes = [];

        if (is_string($excludes)) {
            $excludes = explode(',', $excludes);
        }

        if (! is_array($excludes)) {
            throw new \InvalidArgumentException(
                'The parseExcludes() method expects a string or an array. '.gettype($excludes).' given'
            );
        }

        foreach ($excludes as $excludeName) {
            $excludeName = $this->trimToAcceptableRecursionLevel($excludeName);

            if (in_array($excludeName, $this->requestedExcludes)) {
                continue;
            }

            $this->requestedExcludes[] = $excludeName;
        }

        return $this;
    }

    /**
     * Set Recursion Limit.
     *
     * @param int $recursionLimit
     *
     * @return $this
     */
    public function setRecursionLimit($recursionLimit)
    {
        $this->recursionLimit = $recursionLimit;

        return $this;
    }

    /**
     * Set Serializer
     *
     * @param SerializerAbstract $serializer
     *
     * @return $this
     */
    public function setSerializer(SerializerAbstract $serializer)
    {
        $this->serializer = $serializer;

        return $this;
    }

    /**
     * Auto-include Parents
     *
     * Look at the requested includes and automatically include the parents if they
     * are not explicitly requested. E.g: [foo, bar.baz] becomes [foo, bar, bar.baz]
     *
     * @internal
     *
     * @return void
     */
    protected function autoIncludeParents()
    {
        $parsed = [];

        foreach ($this->requestedIncludes as $include) {
            $nested = explode('.', $include);

            $part = array_shift($nested);
            $parsed[] = $part;

            while (count($nested) > 0) {
                $part .= '.'.array_shift($nested);
                $parsed[] = $part;
            }
        }

        $this->requestedIncludes = array_values(array_unique($parsed));
    }

    /**
     * Trim to Acceptable Recursion Level
     *
     * Strip off any requested resources that are too many levels deep, to avoid DiCaprio being chased
     * by trains or whatever the hell that movie was about.
     *
     * @internal
     *
     * @param string $includeName
     *
     * @return string
     */
    protected function trimToAcceptableRecursionLevel($includeName)
    {
        return implode('.', array_slice(explode('.', $includeName), 0, $this->recursionLimit));
    }
}
PK       ! p~>E  E    fractal/src/ParamBag.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal;

/**
 * A handy interface for getting at include parameters.
 */
class ParamBag implements \ArrayAccess, \IteratorAggregate
{
    /**
     * @var array
     */
    protected $params = [];

    /**
     * Create a new parameter bag instance.
     *
     * @param array $params
     *
     * @return void
     */
    public function __construct(array $params)
    {
        $this->params = $params;
    }

    /**
     * Get parameter values out of the bag.
     *
     * @param string $key
     *
     * @return mixed
     */
    public function get($key)
    {
        return $this->__get($key);
    }

    /**
     * Get parameter values out of the bag via the property access magic method.
     *
     * @param string $key
     *
     * @return mixed
     */
    public function __get($key)
    {
        return isset($this->params[$key]) ? $this->params[$key] : null;
    }

    /**
     * Check if a param exists in the bag via an isset() check on the property.
     *
     * @param string $key
     *
     * @return bool
     */
    public function __isset($key)
    {
        return isset($this->params[$key]);
    }

    /**
     * Disallow changing the value of params in the data bag via property access.
     *
     * @param string $key
     * @param mixed  $value
     *
     * @throws \LogicException
     *
     * @return void
     */
    public function __set($key, $value)
    {
        throw new \LogicException('Modifying parameters is not permitted');
    }

    /**
     * Disallow unsetting params in the data bag via property access.
     *
     * @param string $key
     *
     * @throws \LogicException
     *
     * @return void
     */
    public function __unset($key)
    {
        throw new \LogicException('Modifying parameters is not permitted');
    }

    /**
     * Check if a param exists in the bag via an isset() and array access.
     *
     * @param string $key
     *
     * @return bool
     */
    public function offsetExists($key)
    {
        return $this->__isset($key);
    }

    /**
     * Get parameter values out of the bag via array access.
     *
     * @param string $key
     *
     * @return mixed
     */
    public function offsetGet($key)
    {
        return $this->__get($key);
    }

    /**
     * Disallow changing the value of params in the data bag via array access.
     *
     * @param string $key
     * @param mixed  $value
     *
     * @throws \LogicException
     *
     * @return void
     */
    public function offsetSet($key, $value)
    {
        throw new \LogicException('Modifying parameters is not permitted');
    }

    /**
     * Disallow unsetting params in the data bag via array access.
     *
     * @param string $key
     *
     * @throws \LogicException
     *
     * @return void
     */
    public function offsetUnset($key)
    {
        throw new \LogicException('Modifying parameters is not permitted');
    }

    /**
     * IteratorAggregate for iterating over the object like an array.
     *
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->params);
    }
}
PK       ! #T8  8    fractal/src/Scope.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 **/

namespace League\Fractal;

use InvalidArgumentException;
use League\Fractal\Resource\Collection;
use League\Fractal\Resource\Item;
use League\Fractal\Resource\Primitive;
use League\Fractal\Resource\NullResource;
use League\Fractal\Resource\ResourceInterface;
use League\Fractal\Serializer\SerializerAbstract;

/**
 * Scope
 *
 * The scope class acts as a tracker, relating a specific resource in a specific
 * context. For example, the same resource could be attached to multiple scopes.
 * There are root scopes, parent scopes and child scopes.
 */
class Scope
{
    /**
     * @var array
     */
    protected $availableIncludes = [];

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

    /**
     * @var \League\Fractal\Manager
     */
    protected $manager;

    /**
     * @var ResourceInterface
     */
    protected $resource;

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

    /**
     * Create a new scope instance.
     *
     * @param Manager           $manager
     * @param ResourceInterface $resource
     * @param string            $scopeIdentifier
     *
     * @return void
     */
    public function __construct(Manager $manager, ResourceInterface $resource, $scopeIdentifier = null)
    {
        $this->manager = $manager;
        $this->resource = $resource;
        $this->scopeIdentifier = $scopeIdentifier;
    }

    /**
     * Embed a scope as a child of the current scope.
     *
     * @internal
     *
     * @param string            $scopeIdentifier
     * @param ResourceInterface $resource
     *
     * @return \League\Fractal\Scope
     */
    public function embedChildScope($scopeIdentifier, $resource)
    {
        return $this->manager->createData($resource, $scopeIdentifier, $this);
    }

    /**
     * Get the current identifier.
     *
     * @return string
     */
    public function getScopeIdentifier()
    {
        return $this->scopeIdentifier;
    }

    /**
     * Get the unique identifier for this scope.
     *
     * @param string $appendIdentifier
     *
     * @return string
     */
    public function getIdentifier($appendIdentifier = null)
    {
        $identifierParts = array_merge($this->parentScopes, [$this->scopeIdentifier, $appendIdentifier]);

        return implode('.', array_filter($identifierParts));
    }

    /**
     * Getter for parentScopes.
     *
     * @return mixed
     */
    public function getParentScopes()
    {
        return $this->parentScopes;
    }

    /**
     * Getter for resource.
     *
     * @return ResourceInterface
     */
    public function getResource()
    {
        return $this->resource;
    }

    /**
     * Getter for manager.
     *
     * @return \League\Fractal\Manager
     */
    public function getManager()
    {
        return $this->manager;
    }

    /**
     * Is Requested.
     *
     * Check if - in relation to the current scope - this specific segment is allowed.
     * That means, if a.b.c is requested and the current scope is a.b, then c is allowed. If the current
     * scope is a then c is not allowed, even if it is there and potentially transformable.
     *
     * @internal
     *
     * @param string $checkScopeSegment
     *
     * @return bool Returns the new number of elements in the array.
     */
    public function isRequested($checkScopeSegment)
    {
        if ($this->parentScopes) {
            $scopeArray = array_slice($this->parentScopes, 1);
            array_push($scopeArray, $this->scopeIdentifier, $checkScopeSegment);
        } else {
            $scopeArray = [$checkScopeSegment];
        }

        $scopeString = implode('.', (array) $scopeArray);

        return in_array($scopeString, $this->manager->getRequestedIncludes());
    }

    /**
     * Is Excluded.
     *
     * Check if - in relation to the current scope - this specific segment should
     * be excluded. That means, if a.b.c is excluded and the current scope is a.b,
     * then c will not be allowed in the transformation whether it appears in
     * the list of default or available, requested includes.
     *
     * @internal
     *
     * @param string $checkScopeSegment
     *
     * @return bool
     */
    public function isExcluded($checkScopeSegment)
    {
        if ($this->parentScopes) {
            $scopeArray = array_slice($this->parentScopes, 1);
            array_push($scopeArray, $this->scopeIdentifier, $checkScopeSegment);
        } else {
            $scopeArray = [$checkScopeSegment];
        }

        $scopeString = implode('.', (array) $scopeArray);

        return in_array($scopeString, $this->manager->getRequestedExcludes());
    }

    /**
     * Push Parent Scope.
     *
     * Push a scope identifier into parentScopes
     *
     * @internal
     *
     * @param string $identifierSegment
     *
     * @return int Returns the new number of elements in the array.
     */
    public function pushParentScope($identifierSegment)
    {
        return array_push($this->parentScopes, $identifierSegment);
    }

    /**
     * Set parent scopes.
     *
     * @internal
     *
     * @param string[] $parentScopes Value to set.
     *
     * @return $this
     */
    public function setParentScopes($parentScopes)
    {
        $this->parentScopes = $parentScopes;

        return $this;
    }

    /**
     * Convert the current data for this scope to an array.
     *
     * @return array
     */
    public function toArray()
    {
        list($rawData, $rawIncludedData) = $this->executeResourceTransformers();

        $serializer = $this->manager->getSerializer();

        $data = $this->serializeResource($serializer, $rawData);

        // If the serializer wants the includes to be side-loaded then we'll
        // serialize the included data and merge it with the data.
        if ($serializer->sideloadIncludes()) {
            //Filter out any relation that wasn't requested
            $rawIncludedData = array_map(array($this, 'filterFieldsets'), $rawIncludedData);

            $includedData = $serializer->includedData($this->resource, $rawIncludedData);

            // If the serializer wants to inject additional information
            // about the included resources, it can do so now.
            $data = $serializer->injectData($data, $rawIncludedData);

            if ($this->isRootScope()) {
                // If the serializer wants to have a final word about all
                // the objects that are sideloaded, it can do so now.
                $includedData = $serializer->filterIncludes(
                    $includedData,
                    $data
                );
            }

            $data = array_merge($data, $includedData);
        }

        if ($this->resource instanceof Collection) {
            if ($this->resource->hasCursor()) {
                $pagination = $serializer->cursor($this->resource->getCursor());
            } elseif ($this->resource->hasPaginator()) {
                $pagination = $serializer->paginator($this->resource->getPaginator());
            }

            if (! empty($pagination)) {
                $this->resource->setMetaValue(key($pagination), current($pagination));
            }
        }

        // Pull out all of OUR metadata and any custom meta data to merge with the main level data
        $meta = $serializer->meta($this->resource->getMeta());

        // in case of returning NullResource we should return null and not to go with array_merge
        if (is_null($data)) {
            if (!empty($meta)) {
                return $meta;
            }
            return null;
        }

        return array_merge($data, $meta);
    }

    /**
     * Convert the current data for this scope to JSON.
     *
     * @param int $options
     *
     * @return string
     */
    public function toJson($options = 0)
    {
        return json_encode($this->toArray(), $options);
    }

    /**
     * Transformer a primitive resource
     *
     * @return mixed
     */
    public function transformPrimitiveResource()
    {
        if (! ($this->resource instanceof Primitive)) {
            throw new InvalidArgumentException(
                'Argument $resource should be an instance of League\Fractal\Resource\Primitive'
            );
        }

        $transformer = $this->resource->getTransformer();
        $data = $this->resource->getData();

        if (null === $transformer) {
            $transformedData = $data;
        } elseif (is_callable($transformer)) {
            $transformedData = call_user_func($transformer, $data);
        } else {
            $transformer->setCurrentScope($this);
            $transformedData = $transformer->transform($data);
        }

        return $transformedData;
    }

    /**
     * Execute the resources transformer and return the data and included data.
     *
     * @internal
     *
     * @return array
     */
    protected function executeResourceTransformers()
    {
        $transformer = $this->resource->getTransformer();
        $data = $this->resource->getData();

        $transformedData = $includedData = [];

        if ($this->resource instanceof Item) {
            list($transformedData, $includedData[]) = $this->fireTransformer($transformer, $data);
        } elseif ($this->resource instanceof Collection) {
            foreach ($data as $value) {
                list($transformedData[], $includedData[]) = $this->fireTransformer($transformer, $value);
            }
        } elseif ($this->resource instanceof NullResource) {
            $transformedData = null;
            $includedData = [];
        } else {
            throw new InvalidArgumentException(
                'Argument $resource should be an instance of League\Fractal\Resource\Item'
                .' or League\Fractal\Resource\Collection'
            );
        }

        return [$transformedData, $includedData];
    }

    /**
     * Serialize a resource
     *
     * @internal
     *
     * @param SerializerAbstract $serializer
     * @param mixed              $data
     *
     * @return array
     */
    protected function serializeResource(SerializerAbstract $serializer, $data)
    {
        $resourceKey = $this->resource->getResourceKey();

        if ($this->resource instanceof Collection) {
            return $serializer->collection($resourceKey, $data);
        }

        if ($this->resource instanceof Item) {
            return $serializer->item($resourceKey, $data);
        }

        return $serializer->null();
    }

    /**
     * Fire the main transformer.
     *
     * @internal
     *
     * @param TransformerAbstract|callable $transformer
     * @param mixed                        $data
     *
     * @return array
     */
    protected function fireTransformer($transformer, $data)
    {
        $includedData = [];

        if (is_callable($transformer)) {
            $transformedData = call_user_func($transformer, $data);
        } else {
            $transformer->setCurrentScope($this);
            $transformedData = $transformer->transform($data);
        }

        if ($this->transformerHasIncludes($transformer)) {
            $includedData = $this->fireIncludedTransformers($transformer, $data);
            $transformedData = $this->manager->getSerializer()->mergeIncludes($transformedData, $includedData);
        }

        //Stick only with requested fields
        $transformedData = $this->filterFieldsets($transformedData);

        return [$transformedData, $includedData];
    }

    /**
     * Fire the included transformers.
     *
     * @internal
     *
     * @param \League\Fractal\TransformerAbstract $transformer
     * @param mixed                               $data
     *
     * @return array
     */
    protected function fireIncludedTransformers($transformer, $data)
    {
        $this->availableIncludes = $transformer->getAvailableIncludes();

        return $transformer->processIncludedResources($this, $data) ?: [];
    }

    /**
     * Determine if a transformer has any available includes.
     *
     * @internal
     *
     * @param TransformerAbstract|callable $transformer
     *
     * @return bool
     */
    protected function transformerHasIncludes($transformer)
    {
        if (! $transformer instanceof TransformerAbstract) {
            return false;
        }

        $defaultIncludes = $transformer->getDefaultIncludes();
        $availableIncludes = $transformer->getAvailableIncludes();

        return ! empty($defaultIncludes) || ! empty($availableIncludes);
    }

    /**
     * Check, if this is the root scope.
     *
     * @return bool
     */
    protected function isRootScope()
    {
        return empty($this->parentScopes);
    }

    /**
     * Filter the provided data with the requested filter fieldset for
     * the scope resource
     *
     * @internal
     *
     * @param array  $data
     *
     * @return array
     */
    protected function filterFieldsets(array $data)
    {
        if (!$this->hasFilterFieldset()) {
            return $data;
        }
        $serializer = $this->manager->getSerializer();
        $requestedFieldset = iterator_to_array($this->getFilterFieldset());
        //Build the array of requested fieldsets with the mandatory serializer fields
        $filterFieldset = array_flip(
            array_merge(
                $serializer->getMandatoryFields(),
                $requestedFieldset
            )
        );
        return array_intersect_key($data, $filterFieldset);
    }

    /**
     * Return the requested filter fieldset for the scope resource
     *
     * @internal
     *
     * @return \League\Fractal\ParamBag|null
     */
    protected function getFilterFieldset()
    {
        return $this->manager->getFieldset($this->getResourceType());
    }

    /**
     * Check if there are requested filter fieldsets for the scope resource.
     *
     * @internal
     *
     * @return bool
     */
    protected function hasFilterFieldset()
    {
        return $this->getFilterFieldset() !== null;
    }

    /**
     * Return the scope resource type.
     *
     * @internal
     *
     * @return string
     */
    protected function getResourceType()
    {
        return $this->resource->getResourceKey();
    }
}
PK       ! $    %  fractal/src/ScopeFactoryInterface.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal;

use League\Fractal\Resource\ResourceInterface;

/**
 * ScopeFactoryInterface
 *
 * Creates Scope Instances for resources
 */
interface ScopeFactoryInterface
{
    /**
     * @param Manager $manager
     * @param ResourceInterface $resource
     * @param string|null $scopeIdentifier
     * @return Scope
     */
    public function createScopeFor(Manager $manager, ResourceInterface $resource, $scopeIdentifier = null);

    /**
     * @param Manager $manager
     * @param Scope $parentScope
     * @param ResourceInterface $resource
     * @param string|null $scopeIdentifier
     * @return Scope
     */
    public function createChildScopeFor(Manager $manager, Scope $parentScope, ResourceInterface $resource, $scopeIdentifier = null);
}
PK       ! 
  
  !  fractal/src/Pagination/Cursor.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Pagination;

/**
 * A generic cursor adapter.
 *
 * @author Isern Palaus <ipalaus@ipalaus.com>
 * @author Michele Massari <michele@michelemassari.net>
 */
class Cursor implements CursorInterface
{
    /**
     * Current cursor value.
     *
     * @var mixed
     */
    protected $current;

    /**
     * Previous cursor value.
     *
     * @var mixed
     */
    protected $prev;

    /**
     * Next cursor value.
     *
     * @var mixed
     */
    protected $next;

    /**
     * Items being held for the current cursor position.
     *
     * @var int
     */
    protected $count;

    /**
     * Create a new Cursor instance.
     *
     * @param int   $current
     * @param null  $prev
     * @param mixed $next
     * @param int   $count
     *
     * @return void
     */
    public function __construct($current = null, $prev = null, $next = null, $count = null)
    {
        $this->current = $current;
        $this->prev = $prev;
        $this->next = $next;
        $this->count = $count;
    }

    /**
     * Get the current cursor value.
     *
     * @return mixed
     */
    public function getCurrent()
    {
        return $this->current;
    }

    /**
     * Set the current cursor value.
     *
     * @param int $current
     *
     * @return Cursor
     */
    public function setCurrent($current)
    {
        $this->current = $current;

        return $this;
    }

    /**
     * Get the prev cursor value.
     *
     * @return mixed
     */
    public function getPrev()
    {
        return $this->prev;
    }

    /**
     * Set the prev cursor value.
     *
     * @param int $prev
     *
     * @return Cursor
     */
    public function setPrev($prev)
    {
        $this->prev = $prev;

        return $this;
    }

    /**
     * Get the next cursor value.
     *
     * @return mixed
     */
    public function getNext()
    {
        return $this->next;
    }

    /**
     * Set the next cursor value.
     *
     * @param int $next
     *
     * @return Cursor
     */
    public function setNext($next)
    {
        $this->next = $next;

        return $this;
    }

    /**
     * Returns the total items in the current cursor.
     *
     * @return int
     */
    public function getCount()
    {
        return $this->count;
    }

    /**
     * Set the total items in the current cursor.
     *
     * @param int $count
     *
     * @return Cursor
     */
    public function setCount($count)
    {
        $this->count = $count;

        return $this;
    }
}
PK       ! vV@    5  fractal/src/Pagination/IlluminatePaginatorAdapter.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Pagination;

use Illuminate\Contracts\Pagination\LengthAwarePaginator;

/**
 * A paginator adapter for illuminate/pagination.
 *
 * @author Maxime Beaudoin <firalabs@gmail.com>
 * @author Marc Addeo <marcaddeo@gmail.com>
 */
class IlluminatePaginatorAdapter implements PaginatorInterface
{
    /**
     * The paginator instance.
     *
     * @var \Illuminate\Contracts\Pagination\LengthAwarePaginator
     */
    protected $paginator;

    /**
     * Create a new illuminate pagination adapter.
     *
     * @param \Illuminate\Contracts\Pagination\LengthAwarePaginator $paginator
     *
     * @return void
     */
    public function __construct(LengthAwarePaginator $paginator)
    {
        $this->paginator = $paginator;
    }

    /**
     * Get the current page.
     *
     * @return int
     */
    public function getCurrentPage()
    {
        return $this->paginator->currentPage();
    }

    /**
     * Get the last page.
     *
     * @return int
     */
    public function getLastPage()
    {
        return $this->paginator->lastPage();
    }

    /**
     * Get the total.
     *
     * @return int
     */
    public function getTotal()
    {
        return $this->paginator->total();
    }

    /**
     * Get the count.
     *
     * @return int
     */
    public function getCount()
    {
        return $this->paginator->count();
    }

    /**
     * Get the number per page.
     *
     * @return int
     */
    public function getPerPage()
    {
        return $this->paginator->perPage();
    }

    /**
     * Get the url for the given page.
     *
     * @param int $page
     *
     * @return string
     */
    public function getUrl($page)
    {
        return $this->paginator->url($page);
    }

    /**
     * Get the paginator instance.
     *
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
     */
    public function getPaginator()
    {
        return $this->paginator;
    }
}
PK       ! *7  7  -  fractal/src/Pagination/PaginatorInterface.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Pagination;

/**
 * A common interface for paginators to use
 *
 * @author Marc Addeo <marcaddeo@gmail.com>
 */
interface PaginatorInterface
{
    /**
     * Get the current page.
     *
     * @return int
     */
    public function getCurrentPage();

    /**
     * Get the last page.
     *
     * @return int
     */
    public function getLastPage();

    /**
     * Get the total.
     *
     * @return int
     */
    public function getTotal();

    /**
     * Get the count.
     *
     * @return int
     */
    public function getCount();

    /**
     * Get the number per page.
     *
     * @return int
     */
    public function getPerPage();

    /**
     * Get the url for the given page.
     *
     * @param int $page
     *
     * @return string
     */
    public function getUrl($page);
}
PK       ! Ұ    ;  fractal/src/Pagination/PhalconFrameworkPaginatorAdapter.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Pagination;

/**
 * A paginator adapter for PhalconPHP/pagination.
 *
 * @author Thien Tran <fcduythien@gmail.com>
 * 
 */
class PhalconFrameworkPaginatorAdapter implements PaginatorInterface
{
    /**
     * A slice of the result set to show in the pagination
     *
     * @var \Phalcon\Paginator\AdapterInterface
     */
    private $paginator;

    
    public function __construct($paginator)
    {
        $this->paginator = $paginator->getPaginate();
    }

    /**
     * Get the current page.
     *
     * @return int
     */
    public function getCurrentPage()
    {
        return $this->paginator->current;
    }

    /**
     * Get the last page.
     *
     * @return int
     */
    public function getLastPage()
    {
        return $this->paginator->last;
    }

    /**
     * Get the total.
     *
     * @return int
     */
    public function getTotal()
    {
        return $this->paginator->total_items;
    }

    /**
     * Get the count.
     *
     * @return int
     */
    public function getCount()
    {
        return $this->paginator->total_pages;
    }

    /**
     * Get the number per page.
     *
     * @return int
     */
    public function getPerPage()
    {
        // $this->paginator->items->count()
        // Because when we use raw sql have not this method
        return count($this->paginator->items);
    }

    /**
     * Get the next.
     *
     * @return int
     */ 
    public function getNext()
    {
        return $this->paginator->next;
    }

    /**
     * Get the url for the given page.
     *
     * @param int $page
     *
     * @return string
     */
    public function getUrl($page)
    {
        return $page;
    }
}
PK       ! YA	  A	  3  fractal/src/Pagination/DoctrinePaginatorAdapter.phpnu         <?php
/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Pagination;

use Doctrine\ORM\Tools\Pagination\Paginator;

/**
 * A paginator adapter for doctrine pagination.
 *
 * @author Fraser Stockley <fraser.stockley@gmail.com>
 */
class DoctrinePaginatorAdapter implements PaginatorInterface
{
    /**
     * The paginator instance.
     * @var  Paginator
     */
    private $paginator;

    /**
     * The route generator.
     *
     * @var callable
     */
    private $routeGenerator;

    /**
     * Create a new DoctrinePaginatorAdapter.
     * @param Paginator $paginator
     * @param callable $routeGenerator
     *
     */
    public function __construct(Paginator $paginator, callable $routeGenerator)
    {
        $this->paginator = $paginator;
        $this->routeGenerator = $routeGenerator;
    }

    /**
     * Get the current page.
     *
     * @return int
     */
    public function getCurrentPage()
    {
        return ($this->paginator->getQuery()->getFirstResult() / $this->paginator->getQuery()->getMaxResults()) + 1;
    }

    /**
     * Get the last page.
     *
     * @return int
     */
    public function getLastPage()
    {
        return (int) ceil($this->getTotal() / $this->paginator->getQuery()->getMaxResults());
    }

    /**
     * Get the total.
     *
     * @return int
     */
    public function getTotal()
    {
        return count($this->paginator);
    }

    /**
     * Get the count.
     *
     * @return int
     */
    public function getCount()
    {
        return $this->paginator->getIterator()->count();
    }

    /**
     * Get the number per page.
     *
     * @return int
     */
    public function getPerPage()
    {
        return $this->paginator->getQuery()->getMaxResults();
    }

    /**
     * Get the url for the given page.
     *
     * @param int $page
     *
     * @return string
     */
    public function getUrl($page)
    {
        return call_user_func($this->getRouteGenerator(), $page);
    }

    /**
     * Get the the route generator.
     *
     * @return callable
     */
    private function getRouteGenerator()
    {
        return $this->routeGenerator;
    }
}
PK       ! R\ȭ	  	  5  fractal/src/Pagination/PagerfantaPaginatorAdapter.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Pagination;

use Pagerfanta\Pagerfanta;

/**
 * A paginator adapter for pagerfanta/pagerfanta.
 *
 * @author Márk Sági-Kazár <mark.sagikazar@gmail.com>
 */
class PagerfantaPaginatorAdapter implements PaginatorInterface
{
    /**
     * The paginator instance.
     *
     * @var \Pagerfanta\Pagerfanta
     */
    protected $paginator;

    /**
     * The route generator.
     *
     * @var callable
     */
    protected $routeGenerator;

    /**
     * Create a new pagerfanta pagination adapter.
     *
     * @param \Pagerfanta\Pagerfanta $paginator
     * @param callable               $routeGenerator
     *
     * @return void
     */
    public function __construct(Pagerfanta $paginator, $routeGenerator)
    {
        $this->paginator = $paginator;
        $this->routeGenerator = $routeGenerator;
    }

    /**
     * Get the current page.
     *
     * @return int
     */
    public function getCurrentPage()
    {
        return $this->paginator->getCurrentPage();
    }

    /**
     * Get the last page.
     *
     * @return int
     */
    public function getLastPage()
    {
        return $this->paginator->getNbPages();
    }

    /**
     * Get the total.
     *
     * @return int
     */
    public function getTotal()
    {
        return count($this->paginator);
    }

    /**
     * Get the count.
     *
     * @return int
     */
    public function getCount()
    {
        return count($this->paginator->getCurrentPageResults());
    }

    /**
     * Get the number per page.
     *
     * @return int
     */
    public function getPerPage()
    {
        return $this->paginator->getMaxPerPage();
    }

    /**
     * Get the url for the given page.
     *
     * @param int $page
     *
     * @return string
     */
    public function getUrl($page)
    {
        return call_user_func($this->routeGenerator, $page);
    }

    /**
     * Get the paginator instance.
     *
     * @return \Pagerfanta\Pagerfanta
     */
    public function getPaginator()
    {
        return $this->paginator;
    }

    /**
     * Get the the route generator.
     *
     * @return callable
     */
    public function getRouteGenerator()
    {
        return $this->routeGenerator;
    }
}
PK       ! &i  i  *  fractal/src/Pagination/CursorInterface.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Pagination;

/**
 * A common interface for cursors to use.
 *
 * @author Isern Palaus <ipalaus@ipalaus.com>
 */
interface CursorInterface
{
    /**
     * Get the current cursor value.
     *
     * @return mixed
     */
    public function getCurrent();

    /**
     * Get the prev cursor value.
     *
     * @return mixed
     */
    public function getPrev();

    /**
     * Get the next cursor value.
     *
     * @return mixed
     */
    public function getNext();

    /**
     * Returns the total items in the current cursor.
     *
     * @return int
     */
    public function getCount();
}
PK       ! "	  	  8  fractal/src/Pagination/ZendFrameworkPaginatorAdapter.phpnu         <?php

/*
 * This file is part of the League\Fractal package.
 *
 * (c) Phil Sturgeon <me@philsturgeon.uk>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace League\Fractal\Pagination;

use Zend\Paginator\Paginator;

/**
 * A paginator adapter for zendframework/zend-paginator.
 *
 * @author Abdul Malik Ikhsan <samsonasik@gmail.com>
 */
class ZendFrameworkPaginatorAdapter implements PaginatorInterface
{
    /**
     * The paginator instance.
     *
     * @var \Zend\Paginator\Paginator
     */
    protected $paginator;

    /**
     * The route generator.
     *
     * @var callable
     */
    protected $routeGenerator;

    /**
     * Create a new zendframework pagination adapter.
     *
     * @param \Zend\Paginator\Paginator $paginator
     * @param callable                  $routeGenerator
     *
     * @return void
     */
    public function __construct(Paginator $paginator, $routeGenerator)
    {
        $this->paginator = $paginator;
        $this->routeGenerator = $routeGenerator;
    }

    /**
     * Get the current page.
     *
     * @return int
     */
    public function getCurrentPage()
    {
        return $this->paginator->getCurrentPageNumber();
    }

    /**
     * Get the last page.
     *
     * @return int
     */
    public function getLastPage()
    {
        return $this->paginator->count();
    }

    /**
     * Get the total.
     *
     * @return int
     */
    public function getTotal()
    {
        return $this->paginator->getTotalItemCount();
    }

    /**
     * Get the count.
     *
     * @return int
     */
    public function getCount()
    {
        return $this->paginator->getCurrentItemCount();
    }

    /**
     * Get the number per page.
     *
     * @return int
     */
    public function getPerPage()
    {
        return $this->paginator->getItemCountPerPage();
    }

    /**
     * Get the url for the given page.
     *
     * @param int $page
     *
     * @return string
     */
    public function getUrl($page)
    {
        return call_user_func($this->routeGenerator, $page);
    }

    /**
     * Get the paginator instance.
     *
     * @return \Zend\Paginator\Paginator
     */
    public function getPaginator()
    {
        return $this->paginator;
    }

    /**
     * Get the the route generator.
     *
     * @return callable
     */
    public function getRouteGenerator()
    {
        return $this->routeGenerator;
    }
}
PK       ! M  M    fractal/LICENSEnu         The MIT License (MIT)

Copyright (c) 2013 Phil Sturgeon <me@philsturgeon.uk>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
PK       ! .^Q      fractal/composer.jsonnu         {
    "name": "league/fractal",
    "description": "Handle the output of complex data structures ready for API output.",
    "keywords": [
        "league",
        "api",
        "json",
        "rest"
    ],
    "homepage": "http://fractal.thephpleague.com/",
    "license": "MIT",
    "authors": [
        {
            "name": "Phil Sturgeon",
            "email": "me@philsturgeon.uk",
            "homepage": "http://philsturgeon.uk/",
            "role": "Developer"
        }
    ],
    "config": {
        "sort-packages": true
    },
    "require": {
        "php": ">=5.4"
    },
    "require-dev": {
        "doctrine/orm": "^2.5",
        "illuminate/contracts": "~5.0",
        "mockery/mockery": "~0.9",
        "pagerfanta/pagerfanta": "~1.0.0",
        "phpunit/phpunit": "~4.0",
        "squizlabs/php_codesniffer": "~1.5",
        "zendframework/zend-paginator": "~2.3"
    },
    "suggest": {
        "illuminate/pagination": "The Illuminate Pagination component.",
        "pagerfanta/pagerfanta": "Pagerfanta Paginator",
        "zendframework/zend-paginator": "Zend Framework Paginator"
    },
    "autoload": {
        "psr-4": {
            "League\\Fractal\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "League\\Fractal\\Test\\": "test"
        }
    },
    "extra": {
        "branch-alias": {
            "dev-master": "0.13-dev"
        }
    }
}
PK         ! H=                    flysystem/src/Directory.phpnu         PK         ! X    !            ^  flysystem/src/Plugin/EmptyDir.phpnu         PK         ! 0W    #            T  flysystem/src/Plugin/ForcedCopy.phpnu         PK         ! N    '            k	  flysystem/src/Plugin/PluggableTrait.phpnu         PK         ! ٘    %              flysystem/src/Plugin/ForcedRename.phpnu         PK         ! X4U    !              flysystem/src/Plugin/ListWith.phpnu         PK         !       0              flysystem/src/Plugin/PluginNotFoundException.phpnu         PK         ! \J    "              flysystem/src/Plugin/ListPaths.phpnu         PK         ! 
N    "               flysystem/src/Plugin/ListFiles.phpnu         PK         ! LgR  R  (            #  flysystem/src/Plugin/GetWithMetadata.phpnu         PK         ! $    '            |(  flysystem/src/Plugin/AbstractPlugin.phpnu         PK         ! u:    %            *  flysystem/src/FileExistsException.phpnu         PK         ! h<h'  h'              -  flysystem/src/Filesystem.phpnu         PK         ! $<                {U  flysystem/src/Config.phpnu         PK         ! ^_S  S  "            ]  flysystem/src/ConfigAwareTrait.phpnu         PK         !                 $a  flysystem/src/Util.phpnu         PK         ! >i                }  flysystem/src/ReadInterface.phpnu         PK         ! "    '            W  flysystem/src/NotSupportedException.phpnu         PK         ! 5H  H  #              flysystem/src/Util/StreamHasher.phpnu         PK         ! &]
   
               N  flysystem/src/Util/MimeType.phpnu         PK         ! 텁+	  +	  .              flysystem/src/Util/ContentListingFormatter.phpnu         PK         ! '\      -            0  flysystem/src/FilesystemNotFoundException.phpnu         PK         ! y  y  %            E  flysystem/src/FilesystemInterface.phpnu         PK         ! LYm	  	                flysystem/src/Handler.phpnu         PK         ! ]-/=                P  flysystem/src/Adapter/Ftpd.phpnu         PK         ! tU!  !  7            ?  flysystem/src/Adapter/Polyfill/StreamedWritingTrait.phpnu         PK         ! {E}  }  7              flysystem/src/Adapter/Polyfill/StreamedReadingTrait.phpnu         PK         ! U    4              flysystem/src/Adapter/Polyfill/StreamedCopyTrait.phpnu         PK         ! :      0            ,  flysystem/src/Adapter/Polyfill/StreamedTrait.phpnu         PK         ! \I    ?              flysystem/src/Adapter/Polyfill/NotSupportingVisibilityTrait.phpnu         PK         ! .Q4  4              r  flysystem/src/Adapter/Ftp.phpnu         PK         ! ,A  A  +            ) flysystem/src/Adapter/CanOverwriteFiles.phpnu         PK         !  e@/  /              ,+ flysystem/src/Adapter/Local.phpnu         PK         ! ~   ~   %            $[ flysystem/src/Adapter/SynologyFtp.phpnu         PK         !  AD%  %  )            [ flysystem/src/Adapter/AbstractAdapter.phpnu         PK         ! mh3  3  ,            ua flysystem/src/Adapter/AbstractFtpAdapter.phpnu         PK         ! B	  	  %             flysystem/src/Adapter/NullAdapter.phpnu         PK         ! R   R               Z flysystem/src/Exception.phpnu         PK         ! e0                 flysystem/src/SafeStorage.phpnu         PK         ! '7s  s              , flysystem/src/File.phpnu         PK         ! wx   x   (             flysystem/src/RootViolationException.phpnu         PK         ! [!  !               flysystem/src/MountManager.phpnu         PK         ! *3    '             flysystem/src/FileNotFoundException.phpnu         PK         ! p}R 
   
  "             flysystem/src/AdapterInterface.phpnu         PK         ! 	-X  X  !            ' flysystem/src/PluginInterface.phpnu         PK         ! wdY  Y  )             flysystem/src/UnreadableFileException.phpnu         PK         ! @                 flysystem/docs/caching.mdnu         PK         ! 53w  w              c flysystem/docs/installation.mdnu         PK         ! {OG    "            ( flysystem/docs/upgrade-to-1.0.0.mdnu         PK         ! l                I flysystem/docs/integrations.mdnu         PK         ! ݢ                  flysystem/docs/plugins.mdnu         PK         ! g                   flysystem/docs/CNAMEnu         PK         ! Ι                 flysystem/docs/index.mdnu         PK         ! T                 flysystem/docs/api.mdnu         PK         ! g                  L" flysystem/docs/.gitignorenu         PK         ! PI 6  6              " flysystem/docs/core-concepts.mdnu         PK         ! ee+                   * flysystem/docs/_data/images.ymlnu         PK         ! Q`/                   ?+ flysystem/docs/_data/project.ymlnu         PK         ! CɃ                v, flysystem/docs/_data/menu.ymlnu         PK         ! Ze  e  %            s0 flysystem/docs/creating-an-adapter.mdnu         PK         ! Lu  u              -9 flysystem/docs/recipes.mdnu         PK         ! Иh                A flysystem/docs/mount-manager.mdnu         PK         ! {    #            J flysystem/docs/adapter/replicate.mdnu         PK         ! ;    !            M flysystem/docs/adapter/dropbox.mdnu         PK         ! N;u  u               P flysystem/docs/adapter/webdav.mdnu         PK         ! H                R flysystem/docs/adapter/copy.mdnu         PK         ! Ɏ    #            T flysystem/docs/adapter/rackspace.mdnu         PK         ! 7z0  0  #            W flysystem/docs/adapter/null-test.mdnu         PK         ! C<                &Y flysystem/docs/adapter/sftp.mdnu         PK         ! k 4y                 |[ flysystem/docs/adapter/gridfs.mdnu         PK         !                 ] flysystem/docs/adapter/ftp.mdnu         PK         ! S  S              _ flysystem/docs/adapter/azure.mdnu         PK         ! (VG  G  #            b flysystem/docs/adapter/aws-s3-v3.mdnu         PK         ! ?)?/  /              e flysystem/docs/adapter/local.mdnu         PK         ! r    #            j flysystem/docs/adapter/aws-s3-v2.mdnu         PK         ! iq0    %            o flysystem/docs/adapter/zip-archive.mdnu         PK         !                  -s flysystem/docs/adapter/memory.mdnu         PK         ! _Ã                9u flysystem/docs/adapter/phpcr.mdnu         PK         ! S                #{ flysystem/docs/performance.mdnu         PK         ! Xv  v  $            a~ flysystem/docs/_layouts/default.htmlnu         PK         ! ObA  A              + flysystem/composer.jsonnu         PK         ! x_'  '               flysystem/LICENSEnu         PK         ! O; ;              oauth1-client/rfc5849.txtnu         PK         ! K                 oauth1-client/CONTRIBUTING.mdnu         PK         ! :Hԏ                 oauth1-client/.scrutinizer.ymlnu         PK         ! 6`                 oauth1-client/.travis.ymlnu         PK         ! ȹBf  f               oauth1-client/CONDUCT.mdnu         PK         !  f-<  <  (            d oauth1-client/tests/TrelloServerTest.phpnu         PK         ! {3  3  .            B' oauth1-client/tests/PlainTextSignatureTest.phpnu         PK         ! 
  
  -            . oauth1-client/tests/ClientCredentialsTest.phpnu         PK         ! ba4  a4  "            :4 oauth1-client/tests/ServerTest.phpnu         PK         ! y,    (            h oauth1-client/tests/stubs/ServerStub.phpnu         PK         !     -            _o oauth1-client/tests/HmacSha1SignatureTest.phpnu         PK         ! )  )  &             oauth1-client/tests/XingServerTest.phpnu         PK         ! H    )             oauth1-client/resources/examples/xing.phpnu         PK         ! XyB    ,             oauth1-client/resources/examples/twitter.phpnu         PK         ! A    +            8 oauth1-client/resources/examples/tumblr.phpnu         PK         ! <zkJ  J              m oauth1-client/LICENSEnu         PK         ! {)  )               oauth1-client/README.mdnu         PK         ! O    0            %  oauth1-client/src/Client/Signature/Signature.phpnu         PK         ! ]v    9             oauth1-client/src/Client/Signature/PlainTextSignature.phpnu         PK         ! lo    9             oauth1-client/src/Client/Signature/SignatureInterface.phpnu         PK         ! B!    8             oauth1-client/src/Client/Signature/HmacSha1Signature.phpnu         PK         ! X1      9            Z oauth1-client/src/Client/Credentials/TokenCredentials.phpnu         PK         ! F&    C            F oauth1-client/src/Client/Credentials/ClientCredentialsInterface.phpnu         PK         ! u   u   =            8 oauth1-client/src/Client/Credentials/CredentialsException.phpnu         PK         ! SR  R  =             oauth1-client/src/Client/Credentials/CredentialsInterface.phpnu         PK         ! ֟n(  (  4             oauth1-client/src/Client/Credentials/Credentials.phpnu         PK         ! O      =            e# oauth1-client/src/Client/Credentials/TemporaryCredentials.phpnu         PK         !     :            Y$ oauth1-client/src/Client/Credentials/ClientCredentials.phpnu         PK         ! ڍO  O  *            & oauth1-client/src/Client/Server/Server.phpnu         PK         ! y	  	  (            v oauth1-client/src/Client/Server/Xing.phpnu         PK         ! ʱ(  (  -             oauth1-client/src/Client/Server/Uservoice.phpnu         PK         ! X7p    +             oauth1-client/src/Client/Server/Magento.phpnu         PK         !     +             oauth1-client/src/Client/Server/Twitter.phpnu         PK         ! $Tb  b  -            è oauth1-client/src/Client/Server/Bitbucket.phpnu         PK         ! j#'  '  *             oauth1-client/src/Client/Server/Trello.phpnu         PK         ! ?    (             oauth1-client/src/Client/Server/User.phpnu         PK         ! J    *            y oauth1-client/src/Client/Server/Tumblr.phpnu         PK         ! qc                 oauth1-client/composer.jsonnu         PK         ! D                 oauth1-client/phpunit.xmlnu         PK         ! {_(   (                oauth1-client/.gitignorenu         PK         !     *            H fractal/src/Resource/ResourceInterface.phpnu         PK         ! 9    )             fractal/src/Resource/ResourceAbstract.phpnu         PK         ! x    #             fractal/src/Resource/Collection.phpnu         PK         ! %    %             fractal/src/Resource/NullResource.phpnu         PK         !                  fractal/src/Resource/Item.phpnu         PK         ! }    "            q fractal/src/Resource/Primitive.phpnu         PK         ! s                  fractal/src/ScopeFactory.phpnu         PK         ! O$q  q  %             fractal/src/Serializer/Serializer.phpnu         PK         ! dH    .            U fractal/src/Serializer/DataArraySerializer.phpnu         PK         ! ̩2  2  -            x fractal/src/Serializer/SerializerAbstract.phpnu         PK         ! "м    *              fractal/src/Serializer/ArraySerializer.phpnu         PK         ! u<  <  ,            , fractal/src/Serializer/JsonApiSerializer.phpnu         PK         !  c    #            
i fractal/src/TransformerAbstract.phpnu         PK         ! ks%  %              x fractal/src/Manager.phpnu         PK         ! p~>E  E               fractal/src/ParamBag.phpnu         PK         ! #T8  8              * fractal/src/Scope.phpnu         PK         ! $    %             fractal/src/ScopeFactoryInterface.phpnu         PK         ! 
  
  !            ] fractal/src/Pagination/Cursor.phpnu         PK         ! vV@    5             fractal/src/Pagination/IlluminatePaginatorAdapter.phpnu         PK         ! *7  7  -             fractal/src/Pagination/PaginatorInterface.phpnu         PK         ! Ұ    ;            . fractal/src/Pagination/PhalconFrameworkPaginatorAdapter.phpnu         PK         ! YA	  A	  3            @ fractal/src/Pagination/DoctrinePaginatorAdapter.phpnu         PK         ! R\ȭ	  	  5            ! fractal/src/Pagination/PagerfantaPaginatorAdapter.phpnu         PK         ! &i  i  *            + fractal/src/Pagination/CursorInterface.phpnu         PK         ! "	  	  8            / fractal/src/Pagination/ZendFrameworkPaginatorAdapter.phpnu         PK         ! M  M              : fractal/LICENSEnu         PK         ! .^Q                > fractal/composer.jsonnu         PK  