updated-packages

This commit is contained in:
RafficMohammed
2023-01-08 00:13:22 +05:30
parent 3ff7df7487
commit da241bacb6
12659 changed files with 563377 additions and 510538 deletions

View File

@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
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, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at info+flysystem@frankdejonge.nl. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

View File

@@ -1,4 +1,4 @@
Copyright (c) 2013-2018 Frank de Jonge
Copyright (c) 2013-2019 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

16
vendor/league/flysystem/SECURITY.md vendored Normal file
View File

@@ -0,0 +1,16 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 1.0.x | :white_check_mark: |
| 2.0.x | :x: |
## Reporting a Vulnerability
When you've encountered a security vulnerability, please disclose it securely.
The security process is described at:
[https://flysystem.thephpleague.com/docs/security/](https://flysystem.thephpleague.com/docs/security/)

View File

@@ -1,11 +1,18 @@
{
"name": "league/flysystem",
"type": "library",
"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"
],
"funding": [
{
"type": "other",
"url": "https://offset.earth/frankdejonge"
}
],
"license": "MIT",
"authors": [
{
@@ -14,12 +21,13 @@
}
],
"require": {
"php": ">=5.5.9",
"ext-fileinfo": "*"
"php": "^7.2.5 || ^8.0",
"ext-fileinfo": "*",
"league/mime-type-detection": "^1.3"
},
"require-dev": {
"phpspec/phpspec": "^3.4",
"phpunit/phpunit": "^5.7.10"
"phpspec/prophecy": "^1.11.1",
"phpunit/phpunit": "^8.5.8"
},
"autoload": {
"psr-4": {
@@ -29,13 +37,9 @@
"autoload-dev": {
"psr-4": {
"League\\Flysystem\\Stub\\": "stub/"
},
"files": [
"tests/PHPUnitHacks.php"
]
}
},
"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",
@@ -53,12 +57,12 @@
"conflict": {
"league/flysystem-sftp": "<1.0.6"
},
"config": {
"bin-dir": "bin"
},
"extra": {
"branch-alias": {
"dev-master": "1.1-dev"
}
},
"scripts": {
"phpstan": "php phpstan.php"
}
}

View File

@@ -16,4 +16,4 @@ your application this is Flysystem) to leak into the application. The most
important part for Flysystem is that it improves portability and creates a
solid boundary between your application core and the infrastructure you use.
The OOP-style handling breaks this principle, therefore I want to stop
promoting it.
promoting it.

View File

@@ -29,6 +29,7 @@ abstract class AbstractAdapter implements AdapterInterface
if ($prefix === '') {
$this->pathPrefix = null;
return;
}
@@ -66,6 +67,6 @@ abstract class AbstractAdapter implements AdapterInterface
*/
public function removePathPrefix($path)
{
return substr($path, strlen($this->getPathPrefix()));
return substr($path, strlen((string) $this->getPathPrefix()));
}
}

View File

@@ -76,6 +76,13 @@ abstract class AbstractFtpAdapter extends AbstractAdapter
*/
protected $safeStorage;
/**
* True to enable timestamps for FTP servers that return unix-style listings.
*
* @var bool
*/
protected $enableTimestampsOnUnixListings = false;
/**
* Constructor.
*
@@ -309,6 +316,20 @@ abstract class AbstractFtpAdapter extends AbstractAdapter
return $this;
}
/**
* True to enable timestamps for FTP servers that return unix-style listings.
*
* @param bool $bool
*
* @return $this
*/
public function setEnableTimestampsOnUnixListings($bool = false)
{
$this->enableTimestampsOnUnixListings = $bool;
return $this;
}
/**
* @inheritdoc
*/
@@ -389,6 +410,18 @@ abstract class AbstractFtpAdapter extends AbstractAdapter
/**
* Normalize a Unix file entry.
*
* Given $item contains:
* '-rw-r--r-- 1 ftp ftp 409 Aug 19 09:01 file1.txt'
*
* This function will return:
* [
* 'type' => 'file',
* 'path' => 'file1.txt',
* 'visibility' => 'public',
* 'size' => 409,
* 'timestamp' => 1566205260
* ]
*
* @param string $item
* @param string $base
*
@@ -402,19 +435,62 @@ abstract class AbstractFtpAdapter extends AbstractAdapter
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);
list($permissions, /* $number */, /* $owner */, /* $group */, $size, $month, $day, $timeOrYear, $name) = explode(' ', $item, 9);
$type = $this->detectType($permissions);
$path = $base === '' ? $name : $base . $this->separator . $name;
if ($type === 'dir') {
return compact('type', 'path');
$result = compact('type', 'path');
if ($this->enableTimestampsOnUnixListings) {
$timestamp = $this->normalizeUnixTimestamp($month, $day, $timeOrYear);
$result += compact('timestamp');
}
return $result;
}
$permissions = $this->normalizePermissions($permissions);
$visibility = $permissions & 0044 ? AdapterInterface::VISIBILITY_PUBLIC : AdapterInterface::VISIBILITY_PRIVATE;
$size = (int) $size;
return compact('type', 'path', 'visibility', 'size');
$result = compact('type', 'path', 'visibility', 'size');
if ($this->enableTimestampsOnUnixListings) {
$timestamp = $this->normalizeUnixTimestamp($month, $day, $timeOrYear);
$result += compact('timestamp');
}
return $result;
}
/**
* Only accurate to the minute (current year), or to the day.
*
* Inadequacies in timestamp accuracy are due to limitations of the FTP 'LIST' command
*
* Note: The 'MLSD' command is a machine-readable replacement for 'LIST'
* but many FTP servers do not support it :(
*
* @param string $month e.g. 'Aug'
* @param string $day e.g. '19'
* @param string $timeOrYear e.g. '09:01' OR '2015'
*
* @return int
*/
protected function normalizeUnixTimestamp($month, $day, $timeOrYear)
{
if (is_numeric($timeOrYear)) {
$year = $timeOrYear;
$hour = '00';
$minute = '00';
$seconds = '00';
} else {
$year = date('Y');
list($hour, $minute) = explode(':', $timeOrYear);
$seconds = '00';
}
$dateTime = DateTime::createFromFormat('Y-M-j-G:i:s', "{$year}-{$month}-{$day}-{$hour}:{$minute}:{$seconds}");
return $dateTime->getTimestamp();
}
/**
@@ -463,7 +539,7 @@ abstract class AbstractFtpAdapter extends AbstractAdapter
*/
protected function detectSystemType($item)
{
return preg_match('/^[0-9]{2,4}-[0-9]{2}-[0-9]{2}/', $item) ? 'windows' : 'unix';
return preg_match('/^[0-9]{2,4}-[0-9]{2}-[0-9]{2}/', trim($item)) ? 'windows' : 'unix';
}
/**
@@ -487,6 +563,10 @@ abstract class AbstractFtpAdapter extends AbstractAdapter
*/
protected function normalizePermissions($permissions)
{
if (is_numeric($permissions)) {
return ((int) $permissions) & 0777;
}
// remove the type identifier
$permissions = substr($permissions, 1);
@@ -565,10 +645,7 @@ abstract class AbstractFtpAdapter extends AbstractAdapter
*/
public function getConnection()
{
$tries = 0;
while ( ! $this->isConnected() && $tries < 3) {
$tries++;
if ( ! $this->isConnected()) {
$this->disconnect();
$this->connect();
}
@@ -620,4 +697,9 @@ abstract class AbstractFtpAdapter extends AbstractAdapter
* @return bool
*/
abstract public function isConnected();
protected function escapePath($path)
{
return str_replace(['*', '[', ']'], ['\\*', '\\[', '\\]'], $path);
}
}

View File

@@ -4,7 +4,9 @@
namespace League\Flysystem\Adapter;
/**
* Adapters that implement this interface let the Filesystem know that it files can be overwritten using the write
* Adapters that implement this interface let the Filesystem know that 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 {}
interface CanOverwriteFiles
{
}

View File

@@ -2,13 +2,16 @@
namespace League\Flysystem\Adapter;
use ErrorException;
use League\Flysystem\Adapter\Polyfill\StreamedCopyTrait;
use League\Flysystem\AdapterInterface;
use League\Flysystem\Config;
use League\Flysystem\ConnectionErrorException;
use League\Flysystem\ConnectionRuntimeException;
use League\Flysystem\InvalidRootException;
use League\Flysystem\Util;
use League\Flysystem\Util\MimeType;
use RuntimeException;
use function in_array;
class Ftp extends AbstractFtpAdapter
{
@@ -53,6 +56,7 @@ class Ftp extends AbstractFtpAdapter
'ignorePassiveAddress',
'recurseManually',
'utf8',
'enableTimestampsOnUnixListings',
];
/**
@@ -127,14 +131,21 @@ class Ftp extends AbstractFtpAdapter
*/
public function connect()
{
$tries = 3;
start_connecting:
if ($this->ssl) {
$this->connection = ftp_ssl_connect($this->getHost(), $this->getPort(), $this->getTimeout());
$this->connection = @ftp_ssl_connect($this->getHost(), $this->getPort(), $this->getTimeout());
} else {
$this->connection = ftp_connect($this->getHost(), $this->getPort(), $this->getTimeout());
$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());
$tries--;
if ($tries > 0) goto start_connecting;
throw new ConnectionRuntimeException('Could not connect to host: ' . $this->getHost() . ', port:' . $this->getPort());
}
$this->login();
@@ -151,8 +162,8 @@ class Ftp extends AbstractFtpAdapter
{
if ($this->utf8) {
$response = ftp_raw($this->connection, "OPTS UTF8 ON");
if (substr($response[0], 0, 3) !== '200') {
throw new RuntimeException(
if (!in_array(substr($response[0], 0, 3), ['200', '202'])) {
throw new ConnectionRuntimeException(
'Could not set UTF-8 mode for connection: ' . $this->getHost() . '::' . $this->getPort()
);
}
@@ -162,7 +173,7 @@ class Ftp extends AbstractFtpAdapter
/**
* Set the connections to passive mode.
*
* @throws RuntimeException
* @throws ConnectionRuntimeException
*/
protected function setConnectionPassiveMode()
{
@@ -171,7 +182,7 @@ class Ftp extends AbstractFtpAdapter
}
if ( ! ftp_pasv($this->connection, $this->passive)) {
throw new RuntimeException(
throw new ConnectionRuntimeException(
'Could not set passive mode for connection: ' . $this->getHost() . '::' . $this->getPort()
);
}
@@ -186,7 +197,7 @@ class Ftp extends AbstractFtpAdapter
$connection = $this->connection;
if ($root && ! ftp_chdir($connection, $root)) {
throw new RuntimeException('Root is invalid or does not exist: ' . $this->getRoot());
throw new InvalidRootException('Root is invalid or does not exist: ' . $this->getRoot());
}
// Store absolute path for further reference.
@@ -199,11 +210,12 @@ class Ftp extends AbstractFtpAdapter
/**
* Login.
*
* @throws RuntimeException
* @throws ConnectionRuntimeException
*/
protected function login()
{
set_error_handler(function () {});
set_error_handler(function () {
});
$isLoggedIn = ftp_login(
$this->connection,
$this->getUsername(),
@@ -213,7 +225,7 @@ class Ftp extends AbstractFtpAdapter
if ( ! $isLoggedIn) {
$this->disconnect();
throw new RuntimeException(
throw new ConnectionRuntimeException(
'Could not login with connection: ' . $this->getHost() . '::' . $this->getPort(
) . ', username: ' . $this->getUsername()
);
@@ -225,8 +237,8 @@ class Ftp extends AbstractFtpAdapter
*/
public function disconnect()
{
if (is_resource($this->connection)) {
ftp_close($this->connection);
if ($this->hasFtpConnection()) {
@ftp_close($this->connection);
}
$this->connection = null;
@@ -248,7 +260,7 @@ class Ftp extends AbstractFtpAdapter
}
$result['contents'] = $contents;
$result['mimetype'] = Util::guessMimeType($path, $contents);
$result['mimetype'] = $config->get('mimetype') ?: Util::guessMimeType($path, $contents);
return $result;
}
@@ -380,19 +392,17 @@ class Ftp extends AbstractFtpAdapter
*/
public function getMetadata($path)
{
$connection = $this->getConnection();
if ($path === '') {
return ['type' => 'dir', 'path' => ''];
}
if (@ftp_chdir($connection, $path) === true) {
if (@ftp_chdir($this->getConnection(), $path) === true) {
$this->setConnectionRoot();
return ['type' => 'dir', 'path' => $path];
}
$listing = $this->ftpRawlist('-A', str_replace('*', '\\*', $path));
$listing = $this->ftpRawlist('-A', $path);
if (empty($listing) || in_array('total 0', $listing, true)) {
return false;
@@ -488,8 +498,6 @@ class Ftp extends AbstractFtpAdapter
*/
protected function listDirectoryContents($directory, $recursive = true)
{
$directory = str_replace('*', '\\*', $directory);
if ($recursive && $this->recurseManually) {
return $this->listDirectoryContentsRecursive($directory);
}
@@ -512,7 +520,9 @@ class Ftp extends AbstractFtpAdapter
foreach ($listing as $item) {
$output[] = $item;
if ($item['type'] !== 'dir') continue;
if ($item['type'] !== 'dir') {
continue;
}
$output = array_merge($output, $this->listDirectoryContentsRecursive($item['path']));
}
@@ -523,19 +533,12 @@ class Ftp extends AbstractFtpAdapter
* Check if the connection is open.
*
* @return bool
* @throws ErrorException
*
* @throws ConnectionErrorException
*/
public function isConnected()
{
try {
return is_resource($this->connection) && ftp_rawlist($this->connection, $this->getRoot()) !== false;
} catch (ErrorException $e) {
if (strpos($e->getMessage(), 'ftp_rawlist') === false) {
throw $e;
}
return false;
}
return $this->hasFtpConnection() && $this->getRawExecResponseCode('NOOP') === 200;
}
/**
@@ -561,8 +564,21 @@ class Ftp extends AbstractFtpAdapter
$connection = $this->getConnection();
if ($this->isPureFtpd) {
$path = str_replace(' ', '\ ', $path);
$path = str_replace([' ', '[', ']'], ['\ ', '\\[', '\\]'], $path);
}
return ftp_rawlist($connection, $options . ' ' . $path);
return ftp_rawlist($connection, $options . ' ' . $this->escapePath($path));
}
private function getRawExecResponseCode($command)
{
$response = @ftp_raw($this->connection, trim($command)) ?: [];
return (int) preg_replace('/\D/', '', implode(' ', (array) $response));
}
private function hasFtpConnection(): bool
{
return is_resource($this->connection) || $this->connection instanceof \FTP\Connection;
}
}

View File

@@ -13,7 +13,15 @@ class Ftpd extends Ftp
return ['type' => 'dir', 'path' => ''];
}
if ( ! ($object = ftp_raw($this->getConnection(), 'STAT ' . $path)) || count($object) < 3) {
if (@ftp_chdir($this->getConnection(), $path) === true) {
$this->setConnectionRoot();
return ['type' => 'dir', 'path' => $path];
}
$object = ftp_raw($this->getConnection(), 'STAT ' . $this->escapePath($path));
if ( ! $object || count($object) < 3) {
return false;
}
@@ -29,7 +37,7 @@ class Ftpd extends Ftp
*/
protected function listDirectoryContents($directory, $recursive = true)
{
$listing = ftp_rawlist($this->getConnection(), $directory, $recursive);
$listing = ftp_rawlist($this->getConnection(), $this->escapePath($directory), $recursive);
if ($listing === false || ( ! empty($listing) && substr($listing[0], 0, 5) === "ftpd:")) {
return [];

View File

@@ -5,7 +5,6 @@ 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;
@@ -33,11 +32,11 @@ class Local extends AbstractAdapter
*/
protected static $permissions = [
'file' => [
'public' => 0644,
'public' => 0644,
'private' => 0600,
],
'dir' => [
'public' => 0755,
'dir' => [
'public' => 0755,
'private' => 0700,
],
];
@@ -106,6 +105,7 @@ class Local extends AbstractAdapter
}
umask($umask);
clearstatcache(false, $root);
if ( ! is_dir($root)) {
$errorMessage = isset($mkdirError['message']) ? $mkdirError['message'] : '';
@@ -206,8 +206,9 @@ class Local extends AbstractAdapter
$result = compact('type', 'path', 'size', 'contents');
if ($mimetype = Util::guessMimeType($path, $contents)) {
$result['mimetype'] = $mimetype;
if ($visibility = $config->get('visibility')) {
$this->setVisibility($path, $visibility);
$result['visibility'] = $visibility;
}
return $result;
@@ -219,7 +220,7 @@ class Local extends AbstractAdapter
public function read($path)
{
$location = $this->applyPathPrefix($path);
$contents = file_get_contents($location);
$contents = @file_get_contents($location);
if ($contents === false) {
return false;
@@ -287,6 +288,8 @@ class Local extends AbstractAdapter
$result[] = $this->normalizeFileInfo($file);
}
unset($iterator);
return array_filter($result);
}
@@ -296,6 +299,7 @@ class Local extends AbstractAdapter
public function getMetadata($path)
{
$location = $this->applyPathPrefix($path);
clearstatcache(false, $location);
$info = new SplFileInfo($location);
return $this->normalizeFileInfo($info);
@@ -318,7 +322,7 @@ class Local extends AbstractAdapter
$finfo = new Finfo(FILEINFO_MIME_TYPE);
$mimetype = $finfo->file($location);
if (in_array($mimetype, ['application/octet-stream', 'inode/x-empty'])) {
if (in_array($mimetype, ['application/octet-stream', 'inode/x-empty', 'application/x-empty'])) {
$mimetype = Util\MimeType::detectByFilename($location);
}
@@ -341,7 +345,15 @@ class Local extends AbstractAdapter
$location = $this->applyPathPrefix($path);
clearstatcache(false, $location);
$permissions = octdec(substr(sprintf('%o', fileperms($location)), -4));
$visibility = $permissions & 0044 ? AdapterInterface::VISIBILITY_PUBLIC : AdapterInterface::VISIBILITY_PRIVATE;
$type = is_dir($location) ? 'dir' : 'file';
foreach ($this->permissionMap[$type] as $visibility => $visibilityPermissions) {
if ($visibilityPermissions == $permissions) {
return compact('path', 'visibility');
}
}
$visibility = substr(sprintf('%o', fileperms($location)), -4);
return compact('path', 'visibility');
}
@@ -370,11 +382,13 @@ class Local extends AbstractAdapter
$location = $this->applyPathPrefix($dirname);
$umask = umask(0);
$visibility = $config->get('visibility', 'public');
$return = ['path' => $dirname, 'type' => 'dir'];
if ( ! is_dir($location) && ! mkdir($location, $this->permissionMap['dir'][$visibility], true)) {
$return = false;
} else {
$return = ['path' => $dirname, 'type' => 'dir'];
if ( ! is_dir($location)) {
if (false === @mkdir($location, $this->permissionMap['dir'][$visibility], true)
|| false === is_dir($location)) {
$return = false;
}
}
umask($umask);
@@ -401,6 +415,8 @@ class Local extends AbstractAdapter
$this->deleteFileInfoObject($file);
}
unset($contents);
return rmdir($location);
}

View File

@@ -34,15 +34,17 @@ trait StreamedCopyTrait
// Required abstract method
/**
* @param string $path
* @param string $path
*
* @return resource
*/
abstract public function readStream($path);
/**
* @param string $path
* @param resource $resource
* @param Config $config
* @param string $path
* @param resource $resource
* @param Config $config
*
* @return resource
*/
abstract public function writeStream($path, $resource, Config $config);

View File

@@ -10,7 +10,7 @@ class Config
protected $settings = [];
/**
* @var Config
* @var Config|null
*/
protected $fallback;

View File

@@ -0,0 +1,9 @@
<?php
namespace League\Flysystem;
use ErrorException;
class ConnectionErrorException extends ErrorException implements FilesystemException
{
}

View File

@@ -0,0 +1,9 @@
<?php
namespace League\Flysystem;
use RuntimeException;
class ConnectionRuntimeException extends RuntimeException implements FilesystemException
{
}

View File

@@ -0,0 +1,17 @@
<?php
namespace League\Flysystem;
use LogicException;
class CorruptedPathDetected extends LogicException implements FilesystemException
{
/**
* @param string $path
* @return CorruptedPathDetected
*/
public static function forPath($path)
{
return new CorruptedPathDetected("Corrupted path detected: " . $path);
}
}

View File

@@ -2,7 +2,7 @@
namespace League\Flysystem;
class Exception extends \Exception
class Exception extends \Exception implements FilesystemException
{
//
}

View File

@@ -8,12 +8,13 @@ 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)
* @method void emptyDir(string $dirname)
* @method array|false getWithMetadata(string $path, string[] $metadata)
* @method bool forceCopy(string $path, string $newpath)
* @method bool forceRename(string $path, string $newpath)
* @method array listFiles(string $path = '', boolean $recursive = false)
* @method string[] listPaths(string $path = '', boolean $recursive = false)
* @method array listWith(string[] $keys = [], $directory = '', $recursive = false)
*/
class Filesystem implements FilesystemInterface
{
@@ -74,7 +75,7 @@ class Filesystem implements FilesystemInterface
*/
public function writeStream($path, $resource, array $config = [])
{
if ( ! is_resource($resource)) {
if ( ! is_resource($resource) || get_resource_type($resource) !== 'stream') {
throw new InvalidArgumentException(__METHOD__ . ' expects argument #2 to be a valid resource.');
}
@@ -107,7 +108,7 @@ class Filesystem implements FilesystemInterface
*/
public function putStream($path, $resource, array $config = [])
{
if ( ! is_resource($resource)) {
if ( ! is_resource($resource) || get_resource_type($resource) !== 'stream') {
throw new InvalidArgumentException(__METHOD__ . ' expects argument #2 to be a valid resource.');
}
@@ -115,7 +116,7 @@ class Filesystem implements FilesystemInterface
$config = $this->prepareConfig($config);
Util::rewindStream($resource);
if ( ! $this->getAdapter() instanceof CanOverwriteFiles &&$this->has($path)) {
if ( ! $this->getAdapter() instanceof CanOverwriteFiles && $this->has($path)) {
return (bool) $this->getAdapter()->updateStream($path, $resource, $config);
}
@@ -158,7 +159,7 @@ class Filesystem implements FilesystemInterface
*/
public function updateStream($path, $resource, array $config = [])
{
if ( ! is_resource($resource)) {
if ( ! is_resource($resource) || get_resource_type($resource) !== 'stream') {
throw new InvalidArgumentException(__METHOD__ . ' expects argument #2 to be a valid resource.');
}
@@ -270,7 +271,8 @@ class Filesystem implements FilesystemInterface
$directory = Util::normalizePath($directory);
$contents = $this->getAdapter()->listContents($directory, $recursive);
return (new ContentListingFormatter($directory, $recursive))->formatListing($contents);
return (new ContentListingFormatter($directory, $recursive, $this->config->get('case_sensitive', true)))
->formatListing($contents);
}
/**
@@ -300,7 +302,7 @@ class Filesystem implements FilesystemInterface
return false;
}
return $object['timestamp'];
return (int) $object['timestamp'];
}
/**
@@ -364,7 +366,7 @@ class Filesystem implements FilesystemInterface
if ( ! $handler) {
$metadata = $this->getMetadata($path);
$handler = $metadata['type'] === 'file' ? new File($this, $path) : new Directory($this, $path);
$handler = ($metadata && $metadata['type'] === 'file') ? new File($this, $path) : new Directory($this, $path);
}
$handler->setPath($path);

View File

@@ -0,0 +1,7 @@
<?php
namespace League\Flysystem;
interface FilesystemException
{
}

View File

@@ -87,7 +87,7 @@ interface FilesystemInterface
*
* @throws FileNotFoundException
*
* @return string|false The timestamp or false on failure.
* @return int|false The timestamp or false on failure.
*/
public function getTimestamp($path);

View File

@@ -7,6 +7,6 @@ use LogicException;
/**
* Thrown when the MountManager cannot find a filesystem.
*/
class FilesystemNotFoundException extends LogicException
class FilesystemNotFoundException extends LogicException implements FilesystemException
{
}

View File

@@ -60,7 +60,7 @@ abstract class Handler
{
$metadata = $this->filesystem->getMetadata($this->path);
return $metadata['type'];
return $metadata ? $metadata['type'] : 'dir';
}
/**

View File

@@ -0,0 +1,9 @@
<?php
namespace League\Flysystem;
use RuntimeException;
class InvalidRootException extends RuntimeException implements FilesystemException
{
}

View File

@@ -5,7 +5,7 @@ namespace League\Flysystem;
use RuntimeException;
use SplFileInfo;
class NotSupportedException extends RuntimeException
class NotSupportedException extends RuntimeException implements FilesystemException
{
/**
* Create a new exception for a link.

View File

@@ -20,8 +20,8 @@ class GetWithMetadata extends AbstractPlugin
/**
* Get metadata for an object with required metadata.
*
* @param string $path path to file
* @param array $metadata metadata keys
* @param string $path path to file
* @param string[] $metadata metadata keys
*
* @throws InvalidArgumentException
* @throws FileNotFoundException

View File

@@ -20,7 +20,7 @@ class ListPaths extends AbstractPlugin
* @param string $directory
* @param bool $recursive
*
* @return array paths
* @return string[] paths
*/
public function handle($directory = '', $recursive = false)
{

View File

@@ -17,9 +17,9 @@ class ListWith extends AbstractPlugin
/**
* List contents with metadata.
*
* @param array $keys
* @param string $directory
* @param bool $recursive
* @param string[] $keys
* @param string $directory
* @param bool $recursive
*
* @return array listing with metadata
*/

View File

@@ -4,7 +4,7 @@ namespace League\Flysystem;
use LogicException;
class RootViolationException extends LogicException
class RootViolationException extends LogicException implements FilesystemException
{
//
}

View File

@@ -5,6 +5,8 @@ namespace League\Flysystem;
use League\Flysystem\Util\MimeType;
use LogicException;
use function strcmp;
class Util
{
/**
@@ -102,8 +104,7 @@ class Util
public static function normalizeRelativePath($path)
{
$path = str_replace('\\', '/', $path);
$path = static::removeFunkyWhiteSpace($path);
$path = static::removeFunkyWhiteSpace($path);
$parts = [];
foreach (explode('/', $path) as $part) {
@@ -127,21 +128,22 @@ class Util
}
}
return implode('/', $parts);
$path = implode('/', $parts);
return $path;
}
/**
* Removes unprintable characters and invalid unicode characters.
* Rejects 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);
protected static function removeFunkyWhiteSpace($path)
{
if (preg_match('#\p{C}+#u', $path)) {
throw CorruptedPathDetected::forPath($path);
}
return $path;
@@ -175,7 +177,7 @@ class Util
/**
* Guess MIME Type based on the path of the file and it's content.
*
* @param string $path
* @param string $path
* @param string|resource $content
*
* @return string|null MIME Type or NULL if no extension detected
@@ -204,7 +206,7 @@ class Util
$listedDirectories = [];
foreach ($listing as $object) {
list($directories, $listedDirectories) = static::emulateObjectDirectories($object, $directories, $listedDirectories);
[$directories, $listedDirectories] = static::emulateObjectDirectories($object, $directories, $listedDirectories);
}
$directories = array_diff(array_unique($directories), array_unique($listedDirectories));
@@ -266,12 +268,16 @@ class Util
*
* @param resource $resource
*
* @return int stream size
* @return int|null stream size
*/
public static function getStreamSize($resource)
{
$stat = fstat($resource);
if ( ! is_array($stat) || ! isset($stat['size'])) {
return null;
}
return $stat['size'];
}
@@ -290,13 +296,13 @@ class Util
$listedDirectories[] = $object['path'];
}
if (empty($object['dirname'])) {
if ( ! isset($object['dirname']) || trim($object['dirname']) === '') {
return [$directories, $listedDirectories];
}
$parent = $object['dirname'];
while ( ! empty($parent) && ! in_array($parent, $directories)) {
while (isset($parent) && trim($parent) !== '' && ! in_array($parent, $directories)) {
$directories[] = $parent;
$parent = static::dirname($parent);
}

View File

@@ -13,19 +13,26 @@ class ContentListingFormatter
* @var string
*/
private $directory;
/**
* @var bool
*/
private $recursive;
/**
* @var bool
*/
private $caseSensitive;
/**
* @param string $directory
* @param bool $recursive
*/
public function __construct($directory, $recursive)
public function __construct($directory, $recursive, $caseSensitive = true)
{
$this->directory = $directory;
$this->directory = rtrim($directory, '/');
$this->recursive = $recursive;
$this->caseSensitive = $caseSensitive;
}
/**
@@ -37,14 +44,9 @@ class ContentListingFormatter
*/
public function formatListing(array $listing)
{
$listing = array_values(
array_map(
[$this, 'addPathInfo'],
array_filter($listing, [$this, 'isEntryOutOfScope'])
)
);
$listing = array_filter(array_map([$this, 'addPathInfo'], $listing), [$this, 'isEntryOutOfScope']);
return $this->sortListing($listing);
return $this->sortListing(array_values($listing));
}
private function addPathInfo(array $entry)
@@ -85,7 +87,9 @@ class ContentListingFormatter
return true;
}
return strpos($entry['path'], $this->directory . '/') === 0;
return $this->caseSensitive
? strpos($entry['path'], $this->directory . '/') === 0
: stripos($entry['path'], $this->directory . '/') === 0;
}
/**
@@ -97,7 +101,9 @@ class ContentListingFormatter
*/
private function isDirectChild(array $entry)
{
return Util::dirname($entry['path']) === $this->directory;
return $this->caseSensitive
? $entry['dirname'] === $this->directory
: strcasecmp($this->directory, $entry['dirname']) === 0;
}
/**

View File

@@ -2,220 +2,72 @@
namespace League\Flysystem\Util;
use finfo;
use ErrorException;
use League\MimeTypeDetection\FinfoMimeTypeDetector;
use League\MimeTypeDetection\GeneratedExtensionToMimeTypeMap;
use League\MimeTypeDetection\MimeTypeDetector;
/**
* @internal
*/
class MimeType
{
protected static $extensionToMimeTypeMap = [
'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',
'epub' => 'application/epub+zip',
'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',
'rdf' => 'application/rdf+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',
'dmn' => 'application/octet-stream',
'bpmn' => 'application/octet-stream',
'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',
'docm' => 'application/vnd.ms-word.template.macroEnabled.12',
'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',
'tex' => 'application/x-tex',
'latex' => 'application/x-latex',
'odt' => 'application/vnd.oasis.opendocument.text',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
'odp' => 'application/vnd.oasis.opendocument.presentation',
'odg' => 'application/vnd.oasis.opendocument.graphics',
'odc' => 'application/vnd.oasis.opendocument.chart',
'odf' => 'application/vnd.oasis.opendocument.formula',
'odi' => 'application/vnd.oasis.opendocument.image',
'odm' => 'application/vnd.oasis.opendocument.text-master',
'odb' => 'application/vnd.oasis.opendocument.database',
'ott' => 'application/vnd.oasis.opendocument.text-template',
];
protected static $extensionToMimeTypeMap = GeneratedExtensionToMimeTypeMap::MIME_TYPES_FOR_EXTENSIONS;
protected static $detector;
public static function useDetector(MimeTypeDetector $detector)
{
static::$detector = $detector;
}
/**
* @return MimeTypeDetector
*/
protected static function detector()
{
if ( ! static::$detector instanceof MimeTypeDetector) {
static::$detector = new FinfoMimeTypeDetector();
}
return static::$detector;
}
/**
* Detects MIME Type based on given content.
*
* @param mixed $content
*
* @return string|null MIME Type or NULL if no mime type detected
* @return string MIME Type
*/
public static function detectByContent($content)
{
if ( ! class_exists('finfo') || ! is_string($content)) {
return null;
if (is_string($content)) {
return static::detector()->detectMimeTypeFromBuffer($content);
}
try {
$finfo = new finfo(FILEINFO_MIME_TYPE);
return $finfo->buffer($content) ?: null;
// @codeCoverageIgnoreStart
} catch( ErrorException $e ) {
// This is caused by an array to string conversion error.
}
} // @codeCoverageIgnoreEnd
return 'text/plain';
}
/**
* Detects MIME Type based on file extension.
*
* @param string $extension
*
* @return string|null MIME Type or NULL if no extension detected
* @return string MIME Type
*/
public static function detectByFileExtension($extension)
{
return isset(static::$extensionToMimeTypeMap[$extension])
? static::$extensionToMimeTypeMap[$extension]
: 'text/plain';
return static::detector()->detectMimeTypeFromPath('artificial.' . $extension) ?: 'text/plain';
}
/**
* @param string $filename
*
* @return string|null MIME Type or NULL if no extension detected
* @return string MIME Type
*/
public static function detectByFilename($filename)
{
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
return empty($extension) ? 'text/plain' : static::detectByFileExtension($extension);
return static::detector()->detectMimeTypeFromPath($filename) ?: 'text/plain';
}
/**

View File

@@ -1,55 +0,0 @@
{
"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"
}
}
}

View File

@@ -1,373 +0,0 @@
<?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));
}
}

View File

@@ -1,163 +0,0 @@
<?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;
}
}

View File

@@ -1,48 +0,0 @@
<?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();
}

View File

@@ -1,118 +0,0 @@
<?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;
}
}

View File

@@ -1,114 +0,0 @@
<?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;
}
}

View File

@@ -1,132 +0,0 @@
<?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;
}
}

View File

@@ -1,64 +0,0 @@
<?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);
}

View File

@@ -1,108 +0,0 @@
<?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;
}
}

View File

@@ -1,132 +0,0 @@
<?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;
}
}

View File

@@ -1,163 +0,0 @@
<?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);
}
}

View File

@@ -1,114 +0,0 @@
<?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;
}
}

View File

@@ -1,24 +0,0 @@
<?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
{
//
}

View File

@@ -1,33 +0,0 @@
<?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.
}
}

View File

@@ -1,23 +0,0 @@
<?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
{
//
}

View File

@@ -1,182 +0,0 @@
<?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;
}
}

View File

@@ -1,54 +0,0 @@
<?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);
}

View File

@@ -1,531 +0,0 @@
<?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();
}
}

View File

@@ -1,48 +0,0 @@
<?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;
}
}

View File

@@ -1,39 +0,0 @@
<?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);
}

View File

@@ -1,136 +0,0 @@
<?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];
}
}

View File

@@ -1,51 +0,0 @@
<?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' => []];
}
}

View File

@@ -1,593 +0,0 @@
<?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];
}
}

View File

@@ -1,102 +0,0 @@
<?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);
}

View File

@@ -1,140 +0,0 @@
<?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 [];
}
}

View File

@@ -1,302 +0,0 @@
<?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();
}
}

View File

@@ -0,0 +1,31 @@
# Changelog
## 1.10.0 - 2022-04-11
### Fixed
- Added Flysystem v1 inconclusive mime-types and made it configurable as a constructor parameter.
## 1.9.0 - 2021-11-21
### Updated
- Updated lookup
## 1.8.0 - 2021-09-25
### Added
- Added the decorator `OverridingExtensionToMimeTypeMap` which allows you to override values.
## 1.7.0 - 2021-01-18
### Added
- Added a `bufferSampleSize` parameter to the `FinfoMimeTypeDetector` class that allows you to send a reduced content sample which costs less memory.
## 1.6.0 - 2021-01-18
### Changes
- Updated generated mime-type map

View File

@@ -1,16 +1,14 @@
The MIT License (MIT)
Copyright (c) 2013 Phil Sturgeon <me@philsturgeon.uk>
Copyright (c) 2013-2022 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:
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 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,

View File

@@ -0,0 +1,34 @@
{
"name": "league/mime-type-detection",
"description": "Mime-type detection for Flysystem",
"license": "MIT",
"authors": [
{
"name": "Frank de Jonge",
"email": "info@frankdejonge.nl"
}
],
"scripts": {
"test": "vendor/bin/phpunit",
"phpstan": "vendor/bin/phpstan analyse -l 6 src"
},
"require": {
"php": "^7.2 || ^8.0",
"ext-fileinfo": "*"
},
"require-dev": {
"phpunit/phpunit": "^8.5.8 || ^9.3",
"phpstan/phpstan": "^0.12.68",
"friendsofphp/php-cs-fixer": "^3.2"
},
"autoload": {
"psr-4": {
"League\\MimeTypeDetection\\": "src"
}
},
"config": {
"platform": {
"php": "7.2.0"
}
}
}

View File

@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace League\MimeTypeDetection;
class EmptyExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
{
public function lookupMimeType(string $extension): ?string
{
return null;
}
}

View File

@@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace League\MimeTypeDetection;
use const PATHINFO_EXTENSION;
class ExtensionMimeTypeDetector implements MimeTypeDetector
{
/**
* @var ExtensionToMimeTypeMap
*/
private $extensions;
public function __construct(ExtensionToMimeTypeMap $extensions = null)
{
$this->extensions = $extensions ?: new GeneratedExtensionToMimeTypeMap();
}
public function detectMimeType(string $path, $contents): ?string
{
return $this->detectMimeTypeFromPath($path);
}
public function detectMimeTypeFromPath(string $path): ?string
{
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
return $this->extensions->lookupMimeType($extension);
}
public function detectMimeTypeFromFile(string $path): ?string
{
return $this->detectMimeTypeFromPath($path);
}
public function detectMimeTypeFromBuffer(string $contents): ?string
{
return null;
}
}

View File

@@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace League\MimeTypeDetection;
interface ExtensionToMimeTypeMap
{
public function lookupMimeType(string $extension): ?string;
}

View File

@@ -0,0 +1,92 @@
<?php
declare(strict_types=1);
namespace League\MimeTypeDetection;
use const FILEINFO_MIME_TYPE;
use const PATHINFO_EXTENSION;
use finfo;
class FinfoMimeTypeDetector implements MimeTypeDetector
{
private const INCONCLUSIVE_MIME_TYPES = [
'application/x-empty',
'text/plain',
'text/x-asm',
'application/octet-stream',
'inode/x-empty',
];
/**
* @var finfo
*/
private $finfo;
/**
* @var ExtensionToMimeTypeMap
*/
private $extensionMap;
/**
* @var int|null
*/
private $bufferSampleSize;
/**
* @var array<string>
*/
private $inconclusiveMimetypes;
public function __construct(
string $magicFile = '',
ExtensionToMimeTypeMap $extensionMap = null,
?int $bufferSampleSize = null,
array $inconclusiveMimetypes = self::INCONCLUSIVE_MIME_TYPES
) {
$this->finfo = new finfo(FILEINFO_MIME_TYPE, $magicFile);
$this->extensionMap = $extensionMap ?: new GeneratedExtensionToMimeTypeMap();
$this->bufferSampleSize = $bufferSampleSize;
$this->inconclusiveMimetypes = $inconclusiveMimetypes;
}
public function detectMimeType(string $path, $contents): ?string
{
$mimeType = is_string($contents)
? (@$this->finfo->buffer($this->takeSample($contents)) ?: null)
: null;
if ($mimeType !== null && ! in_array($mimeType, $this->inconclusiveMimetypes)) {
return $mimeType;
}
return $this->detectMimeTypeFromPath($path);
}
public function detectMimeTypeFromPath(string $path): ?string
{
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
return $this->extensionMap->lookupMimeType($extension);
}
public function detectMimeTypeFromFile(string $path): ?string
{
return @$this->finfo->file($path) ?: null;
}
public function detectMimeTypeFromBuffer(string $contents): ?string
{
return @$this->finfo->buffer($this->takeSample($contents)) ?: null;
}
private function takeSample(string $contents): string
{
if ($this->bufferSampleSize === null) {
return $contents;
}
return (string) substr($contents, 0, $this->bufferSampleSize);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace League\MimeTypeDetection;
interface MimeTypeDetector
{
/**
* @param string|resource $contents
*/
public function detectMimeType(string $path, $contents): ?string;
public function detectMimeTypeFromBuffer(string $contents): ?string;
public function detectMimeTypeFromPath(string $path): ?string;
public function detectMimeTypeFromFile(string $path): ?string;
}

View File

@@ -0,0 +1,30 @@
<?php
namespace League\MimeTypeDetection;
class OverridingExtensionToMimeTypeMap implements ExtensionToMimeTypeMap
{
/**
* @var ExtensionToMimeTypeMap
*/
private $innerMap;
/**
* @var string[]
*/
private $overrides;
/**
* @param array<string, string> $overrides
*/
public function __construct(ExtensionToMimeTypeMap $innerMap, array $overrides)
{
$this->innerMap = $innerMap;
$this->overrides = $overrides;
}
public function lookupMimeType(string $extension): ?string
{
return $this->overrides[$extension] ?? $this->innerMap->lookupMimeType($extension);
}
}

View File

@@ -1,4 +1,7 @@
/build
/vendor
/composer.lock
/coverage.xml
/.phpunit.result.cache
/.php_cs.cache
.DS_Store

View File

@@ -0,0 +1,29 @@
<?php
$finder = PhpCsFixer\Finder::create()->in([__DIR__ . '/src', __DIR__ . '/tests']);
return PhpCsFixer\Config::create()
->setRules([
'@PSR2' => true,
'array_syntax' => ['syntax' => 'short'],
'binary_operator_spaces' => true,
'blank_line_before_return' => true,
'cast_spaces' => true,
'concat_space' => ['spacing' => 'one'],
'no_singleline_whitespace_before_semicolons' => true,
'not_operator_with_space' => true,
'ordered_imports' => true,
'phpdoc_align' => true,
'phpdoc_indent' => true,
'phpdoc_no_access' => true,
'phpdoc_no_alias_tag' => true,
'phpdoc_no_package' => true,
'phpdoc_scalar' => true,
'phpdoc_separation' => true,
'phpdoc_summary' => true,
'phpdoc_to_comment' => true,
'phpdoc_trim' => true,
'single_blank_line_at_eof' => true,
'ternary_operator_spaces' => true,
])
->setFinder($finder);

View File

@@ -1,22 +1,57 @@
language: php
php:
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
matrix:
include:
- php: 7.1
dist: bionic
env: COMPOSER_OPTS=""
- php: 7.1
dist: bionic
env: COMPOSER_OPTS="--prefer-lowest"
- php: 7.2
dist: bionic
env: COMPOSER_OPTS=""
- php: 7.2
dist: bionic
env: COMPOSER_OPTS="--prefer-lowest"
- php: 7.3
dist: bionic
env: COMPOSER_OPTS=""
- php: 7.3
dist: bionic
env: COMPOSER_OPTS="--prefer-lowest"
- php: 7.4
dist: bionic
env: COMPOSER_OPTS=""
- php: 7.4
dist: bionic
env: COMPOSER_OPTS="--prefer-lowest"
- php: 8.0
dist: bionic
env: COMPOSER_OPTS=""
- php: 8.0
dist: bionic
env: COMPOSER_OPTS="--prefer-lowest"
- php: nightly
dist: bionic
env: COMPOSER_OPTS="--ignore-platform-reqs"
- php: nightly
dist: bionic
env: COMPOSER_OPTS="--ignore-platform-reqs --prefer-lowest"
allow_failures:
- php: nightly
env: COMPOSER_OPTS="--ignore-platform-reqs"
- php: nightly
env: COMPOSER_OPTS="--ignore-platform-reqs --prefer-lowest"
before_script:
- travis_retry composer self-update
- travis_retry composer install --no-interaction --prefer-source --dev
- travis_retry phpenv rehash
install:
- travis_retry composer update --prefer-dist $COMPOSER_OPTS
script:
- ./vendor/bin/phpcs --standard=psr2 src/
- ./vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover
- composer php-cs-fixer:lint
- composer test:unit
- composer analyze
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
- wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover coverage.xml

View File

@@ -0,0 +1,40 @@
# Changelog
## v1.10.1
- Fix deprecation error (#147)
## v1.10.0
- Adds customizable application scope (setting the `x_auth_access_type` query parameter when fetching temporary credentials) on the Twitter provider - thanks to @Diegslapasteque
## v1.9.3
- Reverts bug in `v1.9.1` and will reintroduce `x_auth_access_type` to Twitter provider in `v1.10.0`.
## v1.9.2
- Adds `x_auth_access_type` to Twitter provider - thanks to @Diegslapasteque
## v1.9.1
- Remove deprecated Guzzle function call.
## v1.9.0
- Adds support for PHP 8.0.
- Allows optional authorization URL parameters to be passed.
## v1.8.2
- Fixes an issue where the base string used to generate signatures did not account for non-standard ports.
## v1.8.1
- Reverts the public API changes introduced in v1.8.0 where language level type declarations and return types that were introduced caused inheritence to break.
- Fixes a Composer warning with relation to autoloading test files.
## v1.8.0
- We allow installation with Guzzle 6 **or** Guzzle 7.
- The minimum PHP version has been bumped from PHP 5.6 to 7.1.

View File

@@ -12,12 +12,14 @@ OAuth 1 Client is an OAuth [RFC 5849 standards-compliant](http://tools.ietf.org/
It has built in support for:
- Bitbucket
- Magento
- Trello
- Tumblr
- Twitter
- Uservoice
- Xing
Adding support for other providers is trivial. The library requires PHP 5.3+ and is PSR-2 compatible.
Adding support for other providers is trivial. The library requires PHP 7.1+ and is PSR-2 compatible.
### Third-Party Providers
@@ -31,6 +33,8 @@ so please help them out with a pull request if you notice this.
- [500px](https://packagist.org/packages/mechant/oauth1-500px)
- [Etsy](https://packagist.org/packages/y0lk/oauth1-etsy)
- [Xero](https://packagist.org/packages/Invoiced/oauth1-xero)
- [Garmin](https://packagist.org/packages/techgyani/garmin-wellness)
- [Goodreads](https://packagist.org/packages/netgalley/oauth1-goodreads)
#### Terminology (as per the RFC 5849 specification):
@@ -96,54 +100,55 @@ $ composer require league/oauth1-client
### Bitbucket
```php
$server = new League\OAuth1\Client\Server\Bitbucket(array(
$server = new League\OAuth1\Client\Server\Bitbucket([
'identifier' => 'your-identifier',
'secret' => 'your-secret',
'callback_uri' => "http://your-callback-uri/",
));
]);
```
### Trello
```php
$server = new League\OAuth1\Client\Server\Trello(array(
$server = new League\OAuth1\Client\Server\Trello([
'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(
$server = new League\OAuth1\Client\Server\Tumblr([
'identifier' => 'your-identifier',
'secret' => 'your-secret',
'callback_uri' => "http://your-callback-uri/",
));
]);
```
### Twitter
```php
$server = new League\OAuth1\Client\Server\Twitter(array(
$server = new League\OAuth1\Client\Server\Twitter([
'identifier' => 'your-identifier',
'secret' => 'your-secret',
'callback_uri' => "http://your-callback-uri/",
));
'scope' => 'your-application-scope' // optional ('read', 'write'), empty by default
]);
```
### Xing
```php
$server = new League\OAuth1\Client\Server\Xing(array(
$server = new League\OAuth1\Client\Server\Xing([
'identifier' => 'your-consumer-key',
'secret' => 'your-consumer-secret',
'callback_uri' => "http://your-callback-uri/",
));
]);
```
### Showing a Login Button

View File

@@ -3,13 +3,27 @@
"description": "OAuth 1.0 Client Library",
"license": "MIT",
"require": {
"php": ">=5.5.0",
"guzzlehttp/guzzle": "^6.0"
"php": ">=7.1||>=8.0",
"ext-json": "*",
"ext-openssl": "*",
"guzzlehttp/guzzle": "^6.0|^7.0",
"guzzlehttp/psr7": "^1.7|^2.0"
},
"require-dev": {
"phpunit/phpunit": "^4.0",
"mockery/mockery": "^0.9",
"squizlabs/php_codesniffer": "^2.0"
"ext-simplexml": "*",
"phpunit/phpunit": "^7.5||9.5",
"mockery/mockery": "^1.3.3",
"phpstan/phpstan": "^0.12.42",
"friendsofphp/php-cs-fixer": "^2.17"
},
"scripts": {
"analyze": "vendor/bin/phpstan analyse -l 6 src/",
"php-cs-fixer:lint": "PHP_CS_FIXER_IGNORE_ENV=1 vendor/bin/php-cs-fixer fix --verbose --dry-run",
"php-cs-fixer:format": "PHP_CS_FIXER_IGNORE_ENV=1 vendor/bin/php-cs-fixer fix",
"test:unit": "vendor/bin/phpunit --coverage-text --coverage-clover coverage.xml"
},
"suggest": {
"ext-simplexml": "For decoding XML-based responses."
},
"keywords": [
"oauth",
@@ -35,12 +49,18 @@
],
"autoload": {
"psr-4": {
"League\\OAuth1\\": "src/"
"League\\OAuth1\\Client\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"League\\OAuth1\\Client\\Tests\\": "tests/"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.0-dev"
"dev-master": "1.0-dev",
"dev-develop": "2.0-dev"
}
}
}

View File

@@ -0,0 +1,11 @@
parameters:
checkMissingIterableValueType: false
checkGenericClassInNonGenericObjectType: false
ignoreErrors:
-
message: '#has unknown class OpenSSLAsymmetricKey as its type#'
path: src/Credentials/RsaClientCredentials.php
-
message: '#invalid type OpenSSLAsymmetricKey#'
path: src/Credentials/RsaClientCredentials.php
reportUnmatchedIgnoredErrors: false

View File

@@ -0,0 +1,5 @@
<?php
include __DIR__ . '/vendor/autoload.php';
date_default_timezone_set('UTC');

View File

@@ -1,28 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true"
stopOnFailure="false"
bootstrap="./vendor/autoload.php"
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="./phpunit.php"
colors="true"
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>
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
verbose="true"
>
<testsuites>
<testsuite name="common">
<directory suffix="Test.php">tests</directory>
<testsuite name="oauth1-client/tests">
<directory suffix="Test.php">./tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./src/</directory>
<directory suffix=".php">./src</directory>
</whitelist>
</filter>
<logging>
<log type="coverage-text" target="php://stdout" showUncoveredFiles="true"/>
<log type="coverage-clover" target="coverage.xml" showUncoveredFiles="true"/>
</logging>
</phpunit>

View File

@@ -1,100 +0,0 @@
<?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'];
}
}

View File

@@ -1,125 +0,0 @@
<?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);
}
}

View File

@@ -12,7 +12,7 @@ class ClientCredentials extends Credentials implements ClientCredentialsInterfac
protected $callbackUri;
/**
* {@inheritDoc}
* @inheritDoc
*/
public function getCallbackUri()
{
@@ -20,7 +20,7 @@ class ClientCredentials extends Credentials implements ClientCredentialsInterfac
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function setCallbackUri($callbackUri)
{

View File

@@ -14,7 +14,9 @@ interface ClientCredentialsInterface extends CredentialsInterface
/**
* Set the credentials callback URI.
*
* @return string
* @param string $callbackUri
*
* @return void
*/
public function setCallbackUri($callbackUri);
}

View File

@@ -19,7 +19,7 @@ abstract class Credentials implements CredentialsInterface
protected $secret;
/**
* {@inheritDoc}
* @inheritDoc
*/
public function getIdentifier()
{
@@ -27,7 +27,7 @@ abstract class Credentials implements CredentialsInterface
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function setIdentifier($identifier)
{
@@ -35,7 +35,7 @@ abstract class Credentials implements CredentialsInterface
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function getSecret()
{
@@ -43,7 +43,7 @@ abstract class Credentials implements CredentialsInterface
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function setSecret($secret)
{

View File

@@ -15,6 +15,8 @@ interface CredentialsInterface
* Set the credentials identifier.
*
* @param string $identifier
*
* @return void
*/
public function setIdentifier($identifier);
@@ -29,6 +31,8 @@ interface CredentialsInterface
* Set the credentials secret.
*
* @param string $secret
*
* @return void
*/
public function setSecret($secret);
}

View File

@@ -0,0 +1,110 @@
<?php
namespace League\OAuth1\Client\Credentials;
use OpenSSLAsymmetricKey;
class RsaClientCredentials extends ClientCredentials
{
/**
* @var string
*/
protected $rsaPublicKeyFile;
/**
* @var string
*/
protected $rsaPrivateKeyFile;
/**
* @var resource|OpenSSLAsymmetricKey|null
*/
protected $rsaPublicKey;
/**
* @var resource|OpenSSLAsymmetricKey|null
*/
protected $rsaPrivateKey;
/**
* Sets the path to the RSA public key.
*
* @param string $filename
*
* @return self
*/
public function setRsaPublicKey($filename)
{
$this->rsaPublicKeyFile = $filename;
$this->rsaPublicKey = null;
return $this;
}
/**
* Sets the path to the RSA private key.
*
* @param string $filename
*
* @return self
*/
public function setRsaPrivateKey($filename)
{
$this->rsaPrivateKeyFile = $filename;
$this->rsaPrivateKey = null;
return $this;
}
/**
* Gets the RSA public key.
*
* @throws CredentialsException when the key could not be loaded.
*
* @return resource|OpenSSLAsymmetricKey
*/
public function getRsaPublicKey()
{
if ($this->rsaPublicKey) {
return $this->rsaPublicKey;
}
if ( ! file_exists($this->rsaPublicKeyFile)) {
throw new CredentialsException('Could not read the public key file.');
}
$this->rsaPublicKey = openssl_get_publickey(file_get_contents($this->rsaPublicKeyFile));
if ( ! $this->rsaPublicKey) {
throw new CredentialsException('Cannot access public key for signing');
}
return $this->rsaPublicKey;
}
/**
* Gets the RSA private key.
*
* @throws CredentialsException when the key could not be loaded.
*
* @return resource|OpenSSLAsymmetricKey
*/
public function getRsaPrivateKey()
{
if ($this->rsaPrivateKey) {
return $this->rsaPrivateKey;
}
if ( ! file_exists($this->rsaPrivateKeyFile)) {
throw new CredentialsException('Could not read the private key file.');
}
$this->rsaPrivateKey = openssl_pkey_get_private(file_get_contents($this->rsaPrivateKeyFile));
if ( ! $this->rsaPrivateKey) {
throw new CredentialsException('Cannot access private key for signing');
}
return $this->rsaPrivateKey;
}
}

View File

@@ -7,7 +7,7 @@ use League\OAuth1\Client\Credentials\TokenCredentials;
class Bitbucket extends Server
{
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlTemporaryCredentials()
{
@@ -15,7 +15,7 @@ class Bitbucket extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlAuthorization()
{
@@ -23,7 +23,7 @@ class Bitbucket extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlTokenCredentials()
{
@@ -31,7 +31,7 @@ class Bitbucket extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlUserDetails()
{
@@ -39,7 +39,7 @@ class Bitbucket extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function userDetails($data, TokenCredentials $tokenCredentials)
{
@@ -52,11 +52,11 @@ class Bitbucket extends Server
$user->lastName = $data['user']['last_name'];
$user->imageUrl = $data['user']['avatar'];
$used = array('username', 'display_name', 'avatar');
$used = ['username', 'display_name', 'avatar'];
foreach ($data as $key => $value) {
if (strpos($key, 'url') !== false) {
if (!in_array($key, $used)) {
if ( ! in_array($key, $used)) {
$used[] = $key;
}
@@ -71,7 +71,7 @@ class Bitbucket extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function userUid($data, TokenCredentials $tokenCredentials)
{
@@ -79,15 +79,15 @@ class Bitbucket extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function userEmail($data, TokenCredentials $tokenCredentials)
{
return;
return null;
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function userScreenName($data, TokenCredentials $tokenCredentials)
{

View File

@@ -4,6 +4,7 @@ namespace League\OAuth1\Client\Server;
use League\OAuth1\Client\Credentials\TemporaryCredentials;
use League\OAuth1\Client\Credentials\TokenCredentials;
use League\OAuth1\Client\Signature\SignatureInterface;
/**
* Magento OAuth 1.0a.
@@ -48,7 +49,7 @@ class Magento extends Server
private $verifier;
/**
* {@inheritDoc}
* @inheritDoc
*/
public function __construct($clientCredentials, SignatureInterface $signature = null)
{
@@ -59,45 +60,45 @@ class Magento extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlTemporaryCredentials()
{
return $this->baseUri.'/oauth/initiate';
return $this->baseUri . '/oauth/initiate';
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlAuthorization()
{
return $this->isAdmin
? $this->adminUrl
: $this->baseUri.'/oauth/authorize';
: $this->baseUri . '/oauth/authorize';
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlTokenCredentials()
{
return $this->baseUri.'/oauth/token';
return $this->baseUri . '/oauth/token';
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlUserDetails()
{
return $this->baseUri.'/api/rest/customers';
return $this->baseUri . '/api/rest/customers';
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function userDetails($data, TokenCredentials $tokenCredentials)
{
if (!is_array($data) || !count($data)) {
if ( ! is_array($data) || ! count($data)) {
throw new \Exception('Not possible to get user info');
}
@@ -107,13 +108,13 @@ class Magento extends Server
$user = new User();
$user->uid = $id;
$mapping = array(
$mapping = [
'email' => 'email',
'firstName' => 'firstname',
'lastName' => 'lastname',
);
'lastName' => 'lastname',
];
foreach ($mapping as $userKey => $dataKey) {
if (!isset($data[$dataKey])) {
if ( ! isset($data[$dataKey])) {
continue;
}
$user->{$userKey} = $data[$dataKey];
@@ -125,7 +126,7 @@ class Magento extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function userUid($data, TokenCredentials $tokenCredentials)
{
@@ -133,27 +134,29 @@ class Magento extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function userEmail($data, TokenCredentials $tokenCredentials)
{
$data = current($data);
if (!isset($data['email'])) {
return;
if ( ! isset($data['email'])) {
return null;
}
return $data['email'];
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function userScreenName($data, TokenCredentials $tokenCredentials)
{
return;
return null;
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function getTokenCredentials(TemporaryCredentials $temporaryCredentials, $temporaryIdentifier, $verifier)
{
@@ -163,13 +166,13 @@ class Magento extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
protected function additionalProtocolParameters()
{
return array(
return [
'oauth_verifier' => $this->verifier,
);
];
}
protected function getHttpClientDefaultHeaders()
@@ -185,28 +188,31 @@ class Magento extends Server
* Parse configuration array to set attributes.
*
* @param array $configuration
*
* @return void
*
* @throws \Exception
*/
private function parseConfigurationArray(array $configuration = array())
private function parseConfigurationArray(array $configuration = [])
{
if (!isset($configuration['host'])) {
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'];
$this->baseUri .= ':' . $url['port'];
}
if (isset($url['path'])) {
$this->baseUri .= '/'.trim($url['path'], '/');
$this->baseUri .= '/' . trim($url['path'], '/');
}
$this->isAdmin = !empty($configuration['admin']);
if (!empty($configuration['adminUrl'])) {
$this->adminUrl = $configuration['adminUrl'].'/oauth_authorize';
$this->isAdmin = ! empty($configuration['admin']);
if ( ! empty($configuration['adminUrl'])) {
$this->adminUrl = $configuration['adminUrl'] . '/oauth_authorize';
} else {
$this->adminUrl = $this->baseUri.'/admin/oauth_authorize';
$this->adminUrl = $this->baseUri . '/admin/oauth_authorize';
}
}
}

View File

@@ -4,21 +4,25 @@ 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\ClientCredentialsInterface;
use League\OAuth1\Client\Credentials\CredentialsException;
use League\OAuth1\Client\Credentials\CredentialsInterface;
use League\OAuth1\Client\Credentials\RsaClientCredentials;
use League\OAuth1\Client\Credentials\TemporaryCredentials;
use League\OAuth1\Client\Credentials\TokenCredentials;
use League\OAuth1\Client\Signature\HmacSha1Signature;
use League\OAuth1\Client\Signature\RsaSha1Signature;
use League\OAuth1\Client\Signature\SignatureInterface;
use SimpleXMLElement;
use Throwable;
abstract class Server
{
/**
* Client credentials.
*
* @var ClientCredentials
* @var ClientCredentialsInterface
*/
protected $clientCredentials;
@@ -39,7 +43,7 @@ abstract class Server
/**
* Cached user details response.
*
* @var unknown
* @var array|SimpleXMLElement
*/
protected $cachedUserDetailsResponse;
@@ -61,11 +65,15 @@ abstract class Server
// Pass through an array or client credentials, we don't care
if (is_array($clientCredentials)) {
$clientCredentials = $this->createClientCredentials($clientCredentials);
} elseif (!$clientCredentials instanceof ClientCredentialsInterface) {
} elseif ( ! $clientCredentials instanceof ClientCredentialsInterface) {
throw new \InvalidArgumentException('Client credentials must be an array or valid object.');
}
$this->clientCredentials = $clientCredentials;
if ( ! $signature && $clientCredentials instanceof RsaClientCredentials) {
$signature = new RsaSha1Signature($clientCredentials);
}
$this->signature = $signature ?: new HmacSha1Signature($clientCredentials);
}
@@ -74,6 +82,8 @@ abstract class Server
* the server.
*
* @return TemporaryCredentials
*
* @throws CredentialsException
*/
public function getTemporaryCredentials()
{
@@ -82,18 +92,20 @@ abstract class Server
$client = $this->createHttpClient();
$header = $this->temporaryCredentialsProtocolHeader($uri);
$authorizationHeader = array('Authorization' => $header);
$authorizationHeader = ['Authorization' => $header];
$headers = $this->buildHttpClientHeaders($authorizationHeader);
try {
$response = $client->post($uri, [
'headers' => $headers,
]);
return $this->createTemporaryCredentials((string) $response->getBody());
} catch (BadResponseException $e) {
return $this->handleTemporaryCredentialsBadResponse($e);
$this->handleTemporaryCredentialsBadResponse($e);
}
return $this->createTemporaryCredentials((string) $response->getBody());
throw new CredentialsException('Failed to get temporary credentials');
}
/**
@@ -101,10 +113,11 @@ abstract class Server
* identifier or an object instance.
*
* @param TemporaryCredentials|string $temporaryIdentifier
* @param array $options
*
* @return string
*/
public function getAuthorizationUrl($temporaryIdentifier)
public function getAuthorizationUrl($temporaryIdentifier, array $options = [])
{
// Somebody can pass through an instance of temporary
// credentials and we'll extract the identifier from there.
@@ -112,7 +125,7 @@ abstract class Server
$temporaryIdentifier = $temporaryIdentifier->getIdentifier();
}
$parameters = array('oauth_token' => $temporaryIdentifier);
$parameters = array_merge($options, ['oauth_token' => $temporaryIdentifier]);
$url = $this->urlAuthorization();
$queryString = http_build_query($parameters);
@@ -124,14 +137,14 @@ abstract class Server
* Redirect the client to the authorization URL.
*
* @param TemporaryCredentials|string $temporaryIdentifier
*
* @return void
*/
public function authorize($temporaryIdentifier)
{
$url = $this->getAuthorizationUrl($temporaryIdentifier);
header('Location: '.$url);
return;
header('Location: ' . $url);
}
/**
@@ -144,6 +157,8 @@ abstract class Server
* @param string $verifier
*
* @return TokenCredentials
*
* @throws CredentialsException
*/
public function getTokenCredentials(TemporaryCredentials $temporaryCredentials, $temporaryIdentifier, $verifier)
{
@@ -155,7 +170,7 @@ abstract class Server
}
$uri = $this->urlTokenCredentials();
$bodyParameters = array('oauth_verifier' => $verifier);
$bodyParameters = ['oauth_verifier' => $verifier];
$client = $this->createHttpClient();
@@ -166,11 +181,13 @@ abstract class Server
'headers' => $headers,
'form_params' => $bodyParameters,
]);
return $this->createTokenCredentials((string) $response->getBody());
} catch (BadResponseException $e) {
return $this->handleTokenCredentialsBadResponse($e);
$this->handleTokenCredentialsBadResponse($e);
}
return $this->createTokenCredentials((string) $response->getBody());
throw new CredentialsException('Failed to get token credentials.');
}
/**
@@ -243,7 +260,7 @@ abstract class Server
*/
protected function fetchUserDetails(TokenCredentials $tokenCredentials, $force = true)
{
if (!$this->cachedUserDetailsResponse || $force) {
if ( ! $this->cachedUserDetailsResponse || $force) {
$url = $this->urlUserDetails();
$client = $this->createHttpClient();
@@ -338,10 +355,10 @@ abstract class Server
*
* @return array
*/
public function getHeaders(CredentialsInterface $credentials, $method, $url, array $bodyParameters = array())
public function getHeaders(CredentialsInterface $credentials, $method, $url, array $bodyParameters = [])
{
$header = $this->protocolHeader(strtoupper($method), $url, $credentials, $bodyParameters);
$authorizationHeader = array('Authorization' => $header);
$authorizationHeader = ['Authorization' => $header];
$headers = $this->buildHttpClientHeaders($authorizationHeader);
return $headers;
@@ -354,8 +371,8 @@ abstract class Server
*/
protected function getHttpClientDefaultHeaders()
{
$defaultHeaders = array();
if (!empty($this->userAgent)) {
$defaultHeaders = [];
if ( ! empty($this->userAgent)) {
$defaultHeaders['User-Agent'] = $this->userAgent;
}
@@ -365,9 +382,11 @@ abstract class Server
/**
* Build Guzzle HTTP client headers.
*
* @param array $headers
*
* @return array
*/
protected function buildHttpClientHeaders($headers = array())
protected function buildHttpClientHeaders($headers = [])
{
$defaultHeaders = $this->getHttpClientDefaultHeaders();
@@ -383,15 +402,22 @@ abstract class Server
*/
protected function createClientCredentials(array $clientCredentials)
{
$keys = array('identifier', 'secret');
$keys = ['identifier', 'secret'];
foreach ($keys as $key) {
if (!isset($clientCredentials[$key])) {
if ( ! isset($clientCredentials[$key])) {
throw new \InvalidArgumentException("Missing client credentials key [$key] from options.");
}
}
$_clientCredentials = new ClientCredentials();
if (isset($clientCredentials['rsa_private_key']) && isset($clientCredentials['rsa_public_key'])) {
$_clientCredentials = new RsaClientCredentials();
$_clientCredentials->setRsaPrivateKey($clientCredentials['rsa_private_key']);
$_clientCredentials->setRsaPublicKey($clientCredentials['rsa_public_key']);
} else {
$_clientCredentials = new ClientCredentials();
}
$_clientCredentials->setIdentifier($clientCredentials['identifier']);
$_clientCredentials->setSecret($clientCredentials['secret']);
@@ -407,6 +433,8 @@ abstract class Server
*
* @param BadResponseException $e
*
* @return void
*
* @throws CredentialsException
*/
protected function handleTemporaryCredentialsBadResponse(BadResponseException $e)
@@ -431,11 +459,11 @@ abstract class Server
{
parse_str($body, $data);
if (!$data || !is_array($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') {
if ( ! isset($data['oauth_callback_confirmed']) || $data['oauth_callback_confirmed'] != 'true') {
throw new CredentialsException('Error in retrieving temporary credentials.');
}
@@ -451,6 +479,8 @@ abstract class Server
*
* @param BadResponseException $e
*
* @return void
*
* @throws CredentialsException
*/
protected function handleTokenCredentialsBadResponse(BadResponseException $e)
@@ -475,7 +505,7 @@ abstract class Server
{
parse_str($body, $data);
if (!$data || !is_array($data)) {
if ( ! $data || ! is_array($data)) {
throw new CredentialsException('Unable to parse token credentials response.');
}
@@ -502,13 +532,13 @@ abstract class Server
{
$dateTime = new \DateTime();
return array(
return [
'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',
);
];
}
/**
@@ -519,7 +549,7 @@ abstract class Server
*/
protected function additionalProtocolParameters()
{
return array();
return [];
}
/**
@@ -532,9 +562,9 @@ abstract class Server
*/
protected function temporaryCredentialsProtocolHeader($uri)
{
$parameters = array_merge($this->baseProtocolParameters(), array(
$parameters = array_merge($this->baseProtocolParameters(), [
'oauth_callback' => $this->clientCredentials->getCallbackUri(),
));
]);
$parameters['oauth_signature'] = $this->signature->sign($uri, $parameters, 'POST');
@@ -553,14 +583,14 @@ abstract class Server
*
* @return string
*/
protected function protocolHeader($method, $uri, CredentialsInterface $credentials, array $bodyParameters = array())
protected function protocolHeader($method, $uri, CredentialsInterface $credentials, array $bodyParameters = [])
{
$parameters = array_merge(
$this->baseProtocolParameters(),
$this->additionalProtocolParameters(),
array(
[
'oauth_token' => $credentials->getIdentifier(),
)
]
);
$this->signature->setCredentials($credentials);
@@ -585,10 +615,10 @@ abstract class Server
protected function normalizeProtocolParameters(array $parameters)
{
array_walk($parameters, function (&$value, $key) {
$value = rawurlencode($key).'="'.rawurlencode($value).'"';
$value = rawurlencode($key) . '="' . rawurlencode($value) . '"';
});
return 'OAuth '.implode(', ', $parameters);
return 'OAuth ' . implode(', ', $parameters);
}
/**
@@ -618,7 +648,7 @@ abstract class Server
*/
protected function buildUrl($host, $queryString)
{
return $host.(strpos($host, '?') !== false ? '&' : '?').$queryString;
return $host . (strpos($host, '?') !== false ? '&' : '?') . $queryString;
}
/**
@@ -678,7 +708,7 @@ abstract class Server
* @param mixed $data
* @param TokenCredentials $tokenCredentials
*
* @return string
* @return string|null
*/
abstract public function userEmail($data, TokenCredentials $tokenCredentials);
@@ -689,7 +719,7 @@ abstract class Server
* @param mixed $data
* @param TokenCredentials $tokenCredentials
*
* @return string
* @return string|null
*/
abstract public function userScreenName($data, TokenCredentials $tokenCredentials);
}

View File

@@ -3,6 +3,7 @@
namespace League\OAuth1\Client\Server;
use League\OAuth1\Client\Credentials\TokenCredentials;
use League\OAuth1\Client\Signature\SignatureInterface;
class Trello extends Server
{
@@ -42,7 +43,7 @@ class Trello extends Server
protected $applicationScope;
/**
* {@inheritDoc}
* @inheritDoc
*/
public function __construct($clientCredentials, SignatureInterface $signature = null)
{
@@ -140,7 +141,7 @@ class Trello extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlTemporaryCredentials()
{
@@ -148,16 +149,16 @@ class Trello extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlAuthorization()
{
return 'https://trello.com/1/OAuthAuthorizeToken?'.
return 'https://trello.com/1/OAuthAuthorizeToken?' .
$this->buildAuthorizationQueryParameters();
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlTokenCredentials()
{
@@ -165,15 +166,15 @@ class Trello extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlUserDetails()
{
return 'https://trello.com/1/members/me?key='.$this->applicationKey.'&token='.$this->accessToken;
return 'https://trello.com/1/members/me?key=' . $this->applicationKey . '&token=' . $this->accessToken;
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function userDetails($data, TokenCredentials $tokenCredentials)
{
@@ -181,7 +182,6 @@ class Trello extends Server
$user->nickname = $data['username'];
$user->name = $data['fullName'];
$user->imageUrl = null;
$user->extra = (array) $data;
@@ -189,7 +189,7 @@ class Trello extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function userUid($data, TokenCredentials $tokenCredentials)
{
@@ -197,15 +197,15 @@ class Trello extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function userEmail($data, TokenCredentials $tokenCredentials)
{
return;
return null;
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function userScreenName($data, TokenCredentials $tokenCredentials)
{
@@ -219,12 +219,12 @@ class Trello extends Server
*/
private function buildAuthorizationQueryParameters()
{
$params = array(
$params = [
'response_type' => 'fragment',
'scope' => $this->getApplicationScope(),
'expiration' => $this->getApplicationExpiration(),
'name' => $this->getApplicationName(),
);
];
return http_build_query($params);
}
@@ -233,15 +233,17 @@ class Trello extends Server
* Parse configuration array to set attributes.
*
* @param array $configuration
*
* @return void
*/
private function parseConfiguration(array $configuration = array())
private function parseConfiguration(array $configuration = [])
{
$configToPropertyMap = array(
$configToPropertyMap = [
'identifier' => 'applicationKey',
'expiration' => 'applicationExpiration',
'name' => 'applicationName',
'scope' => 'applicationScope',
);
];
foreach ($configToPropertyMap as $config => $property) {
if (isset($configuration[$config])) {

View File

@@ -3,11 +3,13 @@
namespace League\OAuth1\Client\Server;
use League\OAuth1\Client\Credentials\TokenCredentials;
use LogicException;
use RuntimeException;
class Tumblr extends Server
{
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlTemporaryCredentials()
{
@@ -15,7 +17,7 @@ class Tumblr extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlAuthorization()
{
@@ -23,7 +25,7 @@ class Tumblr extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlTokenCredentials()
{
@@ -31,7 +33,7 @@ class Tumblr extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlUserDetails()
{
@@ -39,13 +41,13 @@ class Tumblr extends Server
}
/**
* {@inheritDoc}
* @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;
if ( ! isset($data['response']['user']) || ! is_array($data['response']['user'])) {
throw new LogicException('Not possible to get user info');
}
$data = $data['response']['user'];
@@ -55,19 +57,19 @@ class Tumblr extends Server
$user->nickname = $data['name'];
// Save all extra data
$used = array('name');
$used = ['name'];
$user->extra = array_diff_key($data, array_flip($used));
return $user;
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function userUid($data, TokenCredentials $tokenCredentials)
{
if (!isset($data['response']['user']) || !is_array($data['response']['user'])) {
return;
if ( ! isset($data['response']['user']) || ! is_array($data['response']['user'])) {
throw new LogicException('Not possible to get user UUID');
}
$data = $data['response']['user'];
@@ -76,20 +78,20 @@ class Tumblr extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function userEmail($data, TokenCredentials $tokenCredentials)
{
return;
return null;
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function userScreenName($data, TokenCredentials $tokenCredentials)
{
if (!isset($data['response']['user']) || !is_array($data['response']['user'])) {
return;
if ( ! isset($data['response']['user']) || ! is_array($data['response']['user'])) {
throw new LogicException('Not possible to get user screen name');
}
$data = $data['response']['user'];

View File

@@ -0,0 +1,182 @@
<?php
namespace League\OAuth1\Client\Server;
use League\OAuth1\Client\Credentials\TokenCredentials;
class Twitter extends Server
{
/**
* Application scope.
*
* @var ?string
*/
protected $applicationScope = null;
/**
* @inheritDoc
*/
public function __construct($clientCredentials, SignatureInterface $signature = null)
{
parent::__construct($clientCredentials, $signature);
if (is_array($clientCredentials)) {
$this->parseConfiguration($clientCredentials);
}
}
/**
* Set the application scope.
*
* @param ?string $applicationScope
*
* @return Twitter
*/
public function setApplicationScope($applicationScope)
{
$this->applicationScope = $applicationScope;
return $this;
}
/**
* Get application scope.
*
* @return ?string
*/
public function getApplicationScope()
{
return $this->applicationScope;
}
/**
* @inheritDoc
*/
public function urlTemporaryCredentials()
{
$url = 'https://api.twitter.com/oauth/request_token';
$queryParams = $this->temporaryCredentialsQueryParameters();
return empty($queryParams) ? $url : $url . '?' . $queryParams;
}
/**
* @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'];
if (isset($data['email'])) {
$user->email = $data['email'];
}
$used = ['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 null;
}
/**
* @inheritDoc
*/
public function userScreenName($data, TokenCredentials $tokenCredentials)
{
return $data['name'];
}
/**
* Query parameters for a Twitter OAuth request to get temporary credentials.
*
* @return string
*/
protected function temporaryCredentialsQueryParameters()
{
$queryParams = [];
if ($scope = $this->getApplicationScope()) {
$queryParams['x_auth_access_type'] = $scope;
}
return http_build_query($queryParams);
}
/**
* Parse configuration array to set attributes.
*
* @param array $configuration
*
* @return void
*/
private function parseConfiguration(array $configuration = [])
{
$configToPropertyMap = [
'scope' => 'applicationScope',
];
foreach ($configToPropertyMap as $config => $property) {
if (isset($configuration[$config])) {
$this->$property = $configuration[$config];
}
}
}
}

View File

@@ -2,6 +2,8 @@
namespace League\OAuth1\Client\Server;
use ArrayIterator;
class User implements \IteratorAggregate
{
/**
@@ -9,83 +11,85 @@ class User implements \IteratorAggregate
*
* @var mixed
*/
public $uid = null;
public $uid;
/**
* The user's nickname (screen name, username etc).
*
* @var mixed
*/
public $nickname = null;
public $nickname;
/**
* The user's name.
*
* @var mixed
*/
public $name = null;
public $name;
/**
* The user's first name.
*
* @var string
*/
public $firstName = null;
public $firstName;
/**
* The user's last name.
*
* @var string
*/
public $lastName = null;
public $lastName;
/**
* The user's email.
*
* @var string
*/
public $email = null;
public $email;
/**
* The user's location.
*
* @var string|array
*/
public $location = null;
public $location;
/**
* The user's description.
*
* @var string
*/
public $description = null;
public $description;
/**
* The user's image URL.
*
* @var string
*/
public $imageUrl = null;
public $imageUrl;
/**
* The users' URLs.
*
* @var string|array
*/
public $urls = array();
public $urls = [];
/**
* Any extra data.
*
* @var array
*/
public $extra = array();
public $extra = [];
/**
* Set a property on the user.
*
* @param string $key
* @param mixed $value
*
* @return void
*/
public function __set($key, $value)
{
@@ -94,6 +98,18 @@ class User implements \IteratorAggregate
}
}
/**
* Tells if a property is set.
*
* @param string $key
*
* @return bool
*/
public function __isset($key)
{
return isset($this->{$key});
}
/**
* Get a property from the user.
*
@@ -109,10 +125,11 @@ class User implements \IteratorAggregate
}
/**
* {@inheritDoc}
* @inheritDoc
*/
#[\ReturnTypeWillChange]
public function getIterator()
{
return new \ArrayIterator($this);
return new ArrayIterator(get_object_vars($this));
}
}

View File

@@ -16,7 +16,7 @@ class Uservoice extends Server
protected $base;
/**
* {@inheritDoc}
* @inheritDoc
*/
public function __construct($clientCredentials, SignatureInterface $signature = null)
{
@@ -28,39 +28,39 @@ class Uservoice extends Server
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlTemporaryCredentials()
{
return $this->base.'/oauth/request_token';
return $this->base . '/oauth/request_token';
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlAuthorization()
{
return $this->base.'/oauth/authorize';
return $this->base . '/oauth/authorize';
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function urlTokenCredentials()
{
return $this->base.'/oauth/access_token';
return $this->base . '/oauth/access_token';
}
/**
* {@inheritdoc}
* @inheritDoc
*/
public function urlUserDetails()
{
return $this->base.'/api/v1/users/current.json';
return $this->base . '/api/v1/users/current.json';
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function userDetails($data, TokenCredentials $tokenCredentials)
{
@@ -72,13 +72,11 @@ class Uservoice extends Server
$user->email = $data['user']['email'];
if ($data['user']['name']) {
$parts = explode(' ', $data['user']['name']);
$parts = explode(' ', $data['user']['name'], 2);
if (count($parts) > 0) {
$user->firstName = $parts[0];
}
$user->firstName = $parts[0];
if (count($parts) > 1) {
if (2 === count($parts)) {
$user->lastName = $parts[1];
}
}
@@ -89,7 +87,7 @@ class Uservoice extends Server
}
/**
* {@inheritdoc}
* @inheritDoc
*/
public function userUid($data, TokenCredentials $tokenCredentials)
{
@@ -97,7 +95,7 @@ class Uservoice extends Server
}
/**
* {@inheritdoc}
* @inheritDoc
*/
public function userEmail($data, TokenCredentials $tokenCredentials)
{
@@ -105,7 +103,7 @@ class Uservoice extends Server
}
/**
* {@inheritdoc}
* @inheritDoc
*/
public function userScreenName($data, TokenCredentials $tokenCredentials)
{
@@ -117,9 +115,11 @@ class Uservoice extends Server
*
* @param array $configuration
*
* @return void
*
* @throws InvalidArgumentException
*/
private function parseConfigurationArray(array $configuration = array())
private function parseConfigurationArray(array $configuration = [])
{
if (isset($configuration['host'])) {
throw new InvalidArgumentException('Missing host');

View File

@@ -9,39 +9,39 @@ class Xing extends Server
const XING_API_ENDPOINT = 'https://api.xing.com';
/**
* {@inheritDoc}
*/
* @inheritDoc
*/
public function urlTemporaryCredentials()
{
return self::XING_API_ENDPOINT . '/v1/request_token';
}
/**
* {@inheritDoc}
*/
* @inheritDoc
*/
public function urlAuthorization()
{
return self::XING_API_ENDPOINT . '/v1/authorize';
}
/**
* {@inheritDoc}
*/
* @inheritDoc
*/
public function urlTokenCredentials()
{
return self::XING_API_ENDPOINT . '/v1/access_token';
}
/**
* {@inheritDoc}
*/
* @inheritDoc
*/
public function urlUserDetails()
{
return self::XING_API_ENDPOINT . '/v1/users/me';
}
/**
* {@inheritDoc}
*/
* @inheritDoc
*/
public function userDetails($data, TokenCredentials $tokenCredentials)
{
if (!isset($data['users'][0])) {
if ( ! isset($data['users'][0])) {
throw new \Exception('Not possible to get user info');
}
$data = $data['users'][0];
@@ -66,27 +66,30 @@ class Xing extends Server
return $user;
}
/**
* {@inheritDoc}
*/
* @inheritDoc
*/
public function userUid($data, TokenCredentials $tokenCredentials)
{
$data = $data['users'][0];
return $data['id'];
}
/**
* {@inheritDoc}
*/
* @inheritDoc
*/
public function userEmail($data, TokenCredentials $tokenCredentials)
{
$data = $data['users'][0];
return $data['active_email'];
}
/**
* {@inheritDoc}
*/
* @inheritDoc
*/
public function userScreenName($data, TokenCredentials $tokenCredentials)
{
$data = $data['users'][0];
return $data['display_name'];
}
}

View File

@@ -0,0 +1,115 @@
<?php
namespace League\OAuth1\Client\Signature;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Uri;
use Psr\Http\Message\UriInterface;
trait EncodesUrl
{
/**
* Create a Guzzle url for the given URI.
*
* @param string $uri
*
* @return UriInterface
*/
protected function createUrl($uri)
{
return Psr7\Utils::uriFor($uri);
}
/**
* Generate a base string for a RSA-SHA1 signature
* based on the given a url, method, and any parameters.
*
* @param UriInterface $url
* @param string $method
* @param array $parameters
*
* @return string
*/
protected function baseString(UriInterface $url, $method = 'POST', array $parameters = [])
{
$baseString = rawurlencode($method) . '&';
$schemeHostPath = Uri::fromParts([
'scheme' => $url->getScheme(),
'host' => $url->getHost(),
'port' => $url->getPort(),
'path' => $url->getPath(),
]);
$baseString .= rawurlencode($schemeHostPath) . '&';
parse_str($url->getQuery(), $query);
$data = array_merge($query, $parameters);
// normalize data key/values
$data = $this->normalizeArray($data);
ksort($data);
$baseString .= $this->queryStringFromData($data);
return $baseString;
}
/**
* Return a copy of the given array with all keys and values rawurlencoded.
*
* @param array $array Array to normalize
*
* @return array Normalized array
*/
protected function normalizeArray(array $array = [])
{
$normalizedArray = [];
foreach ($array as $key => $value) {
$key = rawurlencode(rawurldecode($key));
if (is_array($value)) {
$normalizedArray[$key] = $this->normalizeArray($value);
} else {
$normalizedArray[$key] = rawurlencode(rawurldecode($value));
}
}
return $normalizedArray;
}
/**
* Creates an array of rawurlencoded strings out of each array key/value pair
* Handles multi-dimensional arrays recursively.
*
* @param array $data Array of parameters to convert.
* @param array|null $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 = null, $prevKey = '')
{
if ($initial = (null === $queryParams)) {
$queryParams = [];
}
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;
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace League\OAuth1\Client\Signature;
class HmacSha1Signature extends Signature implements SignatureInterface
{
use EncodesUrl;
/**
* @inheritDoc
*/
public function method()
{
return 'HMAC-SHA1';
}
/**
* @inheritDoc
*/
public function sign($uri, array $parameters = [], $method = 'POST')
{
$url = $this->createUrl($uri);
$baseString = $this->baseString($url, $method, $parameters);
return base64_encode($this->hash($baseString));
}
/**
* 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);
}
}

View File

@@ -5,7 +5,7 @@ namespace League\OAuth1\Client\Signature;
class PlainTextSignature extends Signature implements SignatureInterface
{
/**
* {@inheritDoc}
* @inheritDoc
*/
public function method()
{
@@ -13,9 +13,9 @@ class PlainTextSignature extends Signature implements SignatureInterface
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function sign($uri, array $parameters = array(), $method = 'POST')
public function sign($uri, array $parameters = [], $method = 'POST')
{
return $this->key();
}

View File

@@ -0,0 +1,35 @@
<?php
namespace League\OAuth1\Client\Signature;
use League\OAuth1\Client\Credentials\RsaClientCredentials;
class RsaSha1Signature extends Signature implements SignatureInterface
{
use EncodesUrl;
/**
* @inheritDoc
*/
public function method()
{
return 'RSA-SHA1';
}
/**
* @inheritDoc
*/
public function sign($uri, array $parameters = [], $method = 'POST')
{
$url = $this->createUrl($uri);
$baseString = $this->baseString($url, $method, $parameters);
/** @var RsaClientCredentials $clientCredentials */
$clientCredentials = $this->clientCredentials;
$privateKey = $clientCredentials->getRsaPrivateKey();
openssl_sign($baseString, $signature, $privateKey);
return base64_encode($signature);
}
}

View File

@@ -22,7 +22,7 @@ abstract class Signature implements SignatureInterface
protected $credentials;
/**
* {@inheritDoc}
* @inheritDoc
*/
public function __construct(ClientCredentialsInterface $clientCredentials)
{
@@ -30,7 +30,7 @@ abstract class Signature implements SignatureInterface
}
/**
* {@inheritDoc}
* @inheritDoc
*/
public function setCredentials(CredentialsInterface $credentials)
{
@@ -44,7 +44,7 @@ abstract class Signature implements SignatureInterface
*/
protected function key()
{
$key = rawurlencode($this->clientCredentials->getSecret()).'&';
$key = rawurlencode($this->clientCredentials->getSecret()) . '&';
if ($this->credentials !== null) {
$key .= rawurlencode($this->credentials->getSecret());

Some files were not shown because too many files have changed in this diff Show More