Commit d2e2cfe7 by 杨树贤

添加vendor和.env到git

parent 0f81938f
Showing with 4793 additions and 2 deletions

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

APP_ENV=local
APP_DEBUG=true
APP_KEY=0uUBt7t4fFIttyqkyLxDhLC7gn9361Yt
APP_TIMEZONE=UTC
DB_CONNECTION=mysql
DB_HOST=192.168.10.10
DB_PORT=3306
DB_DATABASE=ic_welfare
DB_USERNAME=homestead
DB_PASSWORD=secret
DB_PREFIX=ic_
CACHE_DRIVER=file
QUEUE_DRIVER=sync
//系统编码,用于生成错误码
SYSTEM_CODE=09
//系统名称,用于告警识别
SYSTEM_NAME=IC业务助手福利中心服务
//laravels监听IP和端口
LARAVELS_LISTEN_IP=0.0.0.0
LARAVELS_LISTEN_PORT=61009
\ No newline at end of file
......@@ -3,14 +3,12 @@
/public/storage
/storage/*.key
/storage
/vendor
/.idea
/.vagrant
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
.env
_ide_helper.php
_ide_helper_models.php
.phpstorm.meta.php
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit9c11833987e743dc3f50bdb92ca1aa2a::getLoader();
#!/usr/bin/env sh
dir=$(cd "${0%[/\\]*}" > /dev/null; cd '../hhxsv5/laravel-s/bin' && pwd)
if [ -d /proc/cygdrive ] && [[ $(which php) == $(readlink -n /proc/cygdrive)/* ]]; then
# We are in Cgywin using Windows php, so the path must be translated
dir=$(cygpath -m "$dir");
fi
"${dir}/fswatch" "$@"
#!/usr/bin/env sh
dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../phpunit/phpunit" && pwd)
if [ -d /proc/cygdrive ]; then
case $(which php) in
$(readlink -n /proc/cygdrive)/*)
# We are in Cygwin using Windows php, so the path must be translated
dir=$(cygpath -m "$dir");
;;
esac
fi
"${dir}/phpunit" "$@"
@ECHO OFF
setlocal DISABLEDELAYEDEXPANSION
SET BIN_TARGET=%~dp0/../phpunit/phpunit/phpunit
php "%BIN_TARGET%" %*
#!/usr/bin/env sh
dir=$(cd "${0%[/\\]*}" > /dev/null; cd "../nesbot/carbon/bin" && pwd)
if [ -d /proc/cygdrive ]; then
case $(which php) in
$(readlink -n /proc/cygdrive)/*)
# We are in Cygwin using Windows php, so the path must be translated
dir=$(cygpath -m "$dir");
;;
esac
fi
"${dir}/upgrade-carbon" "$@"
@ECHO OFF
setlocal DISABLEDELAYEDEXPANSION
SET BIN_TARGET=%~dp0/../nesbot/carbon/bin/upgrade-carbon
php "%BIN_TARGET%" %*
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php',
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
'72579e7bd17821bb1321b87411366eae' => $vendorDir . '/illuminate/support/helpers.php',
'1d1b89d124cc9cb8219922c9d5569199' => $vendorDir . '/hamcrest/hamcrest-php/hamcrest/Hamcrest.php',
'253c157292f75eb38082b5acb06f3f01' => $vendorDir . '/nikic/fast-route/src/functions.php',
'bee9632da3ca00a99623b9c35d0c4f8b' => $vendorDir . '/laravel/lumen-framework/src/helpers.php',
'6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
'f4e9c7747917193061b46ee142399603' => $vendorDir . '/dingo/api/src/helpers.php',
'7039eb1b48735a2a2905d88c22d84a48' => $baseDir . '/common/function.php',
'c71e5a39a1f58c05a11e8537318d6d3d' => $baseDir . '/common/LogReport.php',
'83c5b4042a3162a51b89dd77dc1632db' => $baseDir . '/app/helpers.php',
);
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'UpdateHelper\\' => array($vendorDir . '/kylekatarnls/update-helper/src'),
'Mockery' => array($vendorDir . '/mockery/mockery/library'),
);
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'phpDocumentor\\Reflection\\' => array($vendorDir . '/phpdocumentor/reflection-common/src', $vendorDir . '/phpdocumentor/reflection-docblock/src', $vendorDir . '/phpdocumentor/type-resolver/src'),
'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'),
'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'),
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
'Symfony\\Contracts\\Translation\\' => array($vendorDir . '/symfony/translation-contracts'),
'Symfony\\Contracts\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher-contracts'),
'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
'Symfony\\Component\\Routing\\' => array($vendorDir . '/symfony/routing'),
'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'),
'Symfony\\Component\\HttpKernel\\' => array($vendorDir . '/symfony/http-kernel'),
'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'),
'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'),
'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
'Symfony\\Component\\Debug\\' => array($vendorDir . '/symfony/debug'),
'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
'Swoole\\IDEHelper\\' => array($vendorDir . '/swoole/ide-helper/src'),
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/Psr/Log'),
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
'Prophecy\\' => array($vendorDir . '/phpspec/prophecy/src/Prophecy'),
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
'League\\Fractal\\' => array($vendorDir . '/league/fractal/src'),
'Laravel\\Lumen\\' => array($vendorDir . '/laravel/lumen-framework/src'),
'Illuminate\\View\\' => array($vendorDir . '/illuminate/view'),
'Illuminate\\Validation\\' => array($vendorDir . '/illuminate/validation'),
'Illuminate\\Translation\\' => array($vendorDir . '/illuminate/translation'),
'Illuminate\\Support\\' => array($vendorDir . '/illuminate/support'),
'Illuminate\\Session\\' => array($vendorDir . '/illuminate/session'),
'Illuminate\\Routing\\' => array($vendorDir . '/illuminate/routing'),
'Illuminate\\Queue\\' => array($vendorDir . '/illuminate/queue'),
'Illuminate\\Pipeline\\' => array($vendorDir . '/illuminate/pipeline'),
'Illuminate\\Pagination\\' => array($vendorDir . '/illuminate/pagination'),
'Illuminate\\Http\\' => array($vendorDir . '/illuminate/http'),
'Illuminate\\Hashing\\' => array($vendorDir . '/illuminate/hashing'),
'Illuminate\\Filesystem\\' => array($vendorDir . '/illuminate/filesystem'),
'Illuminate\\Events\\' => array($vendorDir . '/illuminate/events'),
'Illuminate\\Encryption\\' => array($vendorDir . '/illuminate/encryption'),
'Illuminate\\Database\\' => array($vendorDir . '/illuminate/database'),
'Illuminate\\Contracts\\' => array($vendorDir . '/illuminate/contracts'),
'Illuminate\\Container\\' => array($vendorDir . '/illuminate/container'),
'Illuminate\\Console\\' => array($vendorDir . '/illuminate/console'),
'Illuminate\\Config\\' => array($vendorDir . '/illuminate/config'),
'Illuminate\\Cache\\' => array($vendorDir . '/illuminate/cache'),
'Illuminate\\Bus\\' => array($vendorDir . '/illuminate/bus'),
'Illuminate\\Broadcasting\\' => array($vendorDir . '/illuminate/broadcasting'),
'Illuminate\\Auth\\' => array($vendorDir . '/illuminate/auth'),
'Hhxsv5\\LaravelS\\' => array($vendorDir . '/hhxsv5/laravel-s/src'),
'FastRoute\\' => array($vendorDir . '/nikic/fast-route/src'),
'Faker\\' => array($vendorDir . '/fzaninotto/faker/src/Faker'),
'Dotenv\\' => array($vendorDir . '/vlucas/phpdotenv/src'),
'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'),
'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib/Doctrine/Common/Lexer'),
'Doctrine\\Common\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Common/Inflector'),
'Doctrine\\Common\\Annotations\\' => array($vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations'),
'Dingo\\Blueprint\\' => array($vendorDir . '/dingo/blueprint/src'),
'Dingo\\Api\\' => array($vendorDir . '/dingo/api/src'),
'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'),
'Cron\\' => array($vendorDir . '/mtdowling/cron-expression/src/Cron'),
'Common\\' => array($baseDir . '/common'),
'App\\' => array($baseDir . '/app'),
'' => array($vendorDir . '/nesbot/carbon/src'),
);
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit9c11833987e743dc3f50bdb92ca1aa2a
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit9c11833987e743dc3f50bdb92ca1aa2a', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit9c11833987e743dc3f50bdb92ca1aa2a', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit9c11833987e743dc3f50bdb92ca1aa2a::getInitializer($loader));
} else {
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
$loader->set($namespace, $path);
}
$map = require __DIR__ . '/autoload_psr4.php';
foreach ($map as $namespace => $path) {
$loader->setPsr4($namespace, $path);
}
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
$loader->addClassMap($classMap);
}
}
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit9c11833987e743dc3f50bdb92ca1aa2a::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire9c11833987e743dc3f50bdb92ca1aa2a($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequire9c11833987e743dc3f50bdb92ca1aa2a($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}
This diff could not be displayed because it is too large.
| Q | A
| ----------------- | ---
| Bug? | no|yes
| New Feature? | no|yes
| Framework | Laravel|Lumen
| Framework version | 5.x.y
| Package version | 1.x.y
| PHP version | 5.x.y|7.x.y
#### Actual Behaviour
Describe the behaviour you're experiencing. Do not just copy and paste a random error message and expect help.
#### Expected Behaviour
Describe the behaviour you're expecting.
#### Steps to Reproduce
List all the steps needed to reproduce the issue you're having. Make sure to include code (affected models, configurations),
any screenshots and/or other resources that may help us understand what's going on.
#### Possible Solutions
If you have any ideas on how to solve the issue, add them here, otherwise you can omit this part.
preset: laravel
enabled:
- phpdoc_order
- phpdoc_separation
- unalign_double_arrow
Copyright (c) 2014-2015, Jason Lewis
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Dingo API nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
{
"name": "dingo/api",
"description": "A RESTful API package for the Laravel and Lumen frameworks.",
"keywords": [
"api",
"dingo",
"laravel",
"restful"
],
"license": "BSD-3-Clause",
"authors": [{
"name": "Jason Lewis",
"email": "jason.lewis1991@gmail.com"
}],
"require": {
"php": "^7.1",
"dingo/blueprint": "^0.2",
"illuminate/routing": "^5.5",
"illuminate/support": "^5.5",
"league/fractal": "^0.17"
},
"require-dev": {
"phpdocumentor/reflection-docblock": "3.3.2",
"friendsofphp/php-cs-fixer": "~2",
"illuminate/auth": "^5.5",
"illuminate/cache": "^5.5",
"illuminate/console": "^5.5",
"illuminate/database": "^5.5",
"illuminate/events": "^5.5",
"illuminate/filesystem": "^5.5",
"illuminate/log": "^5.5",
"illuminate/pagination": "^5.5",
"laravel/lumen-framework": "^5.5",
"mockery/mockery": "~1.0",
"phpunit/phpunit": "^4.8.35 || ^5.4.3 || ^6.5",
"squizlabs/php_codesniffer": "~2.0",
"tymon/jwt-auth": "1.0.*"
},
"suggest": {
"tymon/jwt-auth": "Protect your API with JSON Web Tokens."
},
"autoload": {
"psr-4": {
"Dingo\\Api\\": "src/"
},
"files": [
"src/helpers.php"
]
},
"autoload-dev": {
"psr-4": {
"Dingo\\Api\\Tests\\": "tests/"
}
},
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
},
"laravel": {
"providers": [
"Dingo\\Api\\Provider\\LaravelServiceProvider"
],
"aliases": {
"API": "Dingo\\Api\\Facade\\API"
}
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true
}
<?php
return [
/*
|--------------------------------------------------------------------------
| Standards Tree
|--------------------------------------------------------------------------
|
| Versioning an API with Dingo revolves around content negotiation and
| custom MIME types. A custom type will belong to one of three
| standards trees, the Vendor tree (vnd), the Personal tree
| (prs), and the Unregistered tree (x).
|
| By default the Unregistered tree (x) is used, however, should you wish
| to you can register your type with the IANA. For more details:
| https://tools.ietf.org/html/rfc6838
|
*/
'standardsTree' => env('API_STANDARDS_TREE', 'x'),
/*
|--------------------------------------------------------------------------
| API Subtype
|--------------------------------------------------------------------------
|
| Your subtype will follow the standards tree you use when used in the
| "Accept" header to negotiate the content type and version.
|
| For example: Accept: application/x.SUBTYPE.v1+json
|
*/
'subtype' => env('API_SUBTYPE', ''),
/*
|--------------------------------------------------------------------------
| Default API Version
|--------------------------------------------------------------------------
|
| This is the default version when strict mode is disabled and your API
| is accessed via a web browser. It's also used as the default version
| when generating your APIs documentation.
|
*/
'version' => env('API_VERSION', 'v1'),
/*
|--------------------------------------------------------------------------
| Default API Prefix
|--------------------------------------------------------------------------
|
| A default prefix to use for your API routes so you don't have to
| specify it for each group.
|
*/
'prefix' => env('API_PREFIX', null),
/*
|--------------------------------------------------------------------------
| Default API Domain
|--------------------------------------------------------------------------
|
| A default domain to use for your API routes so you don't have to
| specify it for each group.
|
*/
'domain' => env('API_DOMAIN', null),
/*
|--------------------------------------------------------------------------
| Name
|--------------------------------------------------------------------------
|
| When documenting your API using the API Blueprint syntax you can
| configure a default name to avoid having to manually specify
| one when using the command.
|
*/
'name' => env('API_NAME', null),
/*
|--------------------------------------------------------------------------
| Conditional Requests
|--------------------------------------------------------------------------
|
| Globally enable conditional requests so that an ETag header is added to
| any successful response. Subsequent requests will perform a check and
| will return a 304 Not Modified. This can also be enabled or disabled
| on certain groups or routes.
|
*/
'conditionalRequest' => env('API_CONDITIONAL_REQUEST', true),
/*
|--------------------------------------------------------------------------
| Strict Mode
|--------------------------------------------------------------------------
|
| Enabling strict mode will require clients to send a valid Accept header
| with every request. This also voids the default API version, meaning
| your API will not be browsable via a web browser.
|
*/
'strict' => env('API_STRICT', false),
/*
|--------------------------------------------------------------------------
| Debug Mode
|--------------------------------------------------------------------------
|
| Enabling debug mode will result in error responses caused by thrown
| exceptions to have a "debug" key that will be populated with
| more detailed information on the exception.
|
*/
'debug' => env('API_DEBUG', false),
/*
|--------------------------------------------------------------------------
| Generic Error Format
|--------------------------------------------------------------------------
|
| When some HTTP exceptions are not caught and dealt with the API will
| generate a generic error response in the format provided. Any
| keys that aren't replaced with corresponding values will be
| removed from the final response.
|
*/
'errorFormat' => [
'message' => ':message',
'errors' => ':errors',
'code' => ':code',
'status_code' => ':status_code',
'debug' => ':debug',
],
/*
|--------------------------------------------------------------------------
| API Middleware
|--------------------------------------------------------------------------
|
| Middleware that will be applied globally to all API requests.
|
*/
'middleware' => [
],
/*
|--------------------------------------------------------------------------
| Authentication Providers
|--------------------------------------------------------------------------
|
| The authentication providers that should be used when attempting to
| authenticate an incoming API request.
|
*/
'auth' => [
],
/*
|--------------------------------------------------------------------------
| Throttling / Rate Limiting
|--------------------------------------------------------------------------
|
| Consumers of your API can be limited to the amount of requests they can
| make. You can create your own throttles or simply change the default
| throttles.
|
*/
'throttling' => [
],
/*
|--------------------------------------------------------------------------
| Response Transformer
|--------------------------------------------------------------------------
|
| Responses can be transformed so that they are easier to format. By
| default a Fractal transformer will be used to transform any
| responses prior to formatting. You can easily replace
| this with your own transformer.
|
*/
'transformer' => env('API_TRANSFORMER', Dingo\Api\Transformer\Adapter\Fractal::class),
/*
|--------------------------------------------------------------------------
| Response Formats
|--------------------------------------------------------------------------
|
| Responses can be returned in multiple formats by registering different
| response formatters. You can also customize an existing response
| formatter with a number of options to configure its output.
|
*/
'defaultFormat' => env('API_DEFAULT_FORMAT', 'json'),
'formats' => [
'json' => Dingo\Api\Http\Response\Format\Json::class,
],
'formatsOptions' => [
'json' => [
'pretty_print' => env('API_JSON_FORMAT_PRETTY_PRINT_ENABLED', false),
'indent_style' => env('API_JSON_FORMAT_INDENT_STYLE', 'space'),
'indent_size' => env('API_JSON_FORMAT_INDENT_SIZE', 2),
],
],
];
![](https://cloud.githubusercontent.com/assets/829059/9216039/82be51cc-40f6-11e5-88f5-f0cbd07bcc39.png)
The Dingo API package is meant to provide you, the developer, with a set of tools to help you easily and quickly build your own API. While the goal of this package is to remain as flexible as possible it still won't cover all situations and solve all problems.
[![Build Status](https://img.shields.io/travis/dingo/api/master.svg?style=flat-square)](https://travis-ci.org/dingo/api)
[![License](https://img.shields.io/packagist/l/dingo/api.svg?style=flat-square)](LICENSE)
[![Development Version](https://img.shields.io/packagist/vpre/dingo/api.svg?style=flat-square)](https://packagist.org/packages/dingo/api)
[![Monthly Installs](https://img.shields.io/packagist/dm/dingo/api.svg?style=flat-square)](https://packagist.org/packages/dingo/api)
[![StyleCI](https://styleci.io/repos/18673522/shield)](https://styleci.io/repos/18673522)
## Features
This package provides tools for the following, and more:
- Content Negotiation
- Multiple Authentication Adapters
- API Versioning
- Rate Limiting
- Response Transformers and Formatters
- Error and Exception Handling
- Internal Requests
- API Blueprint Documentation
## Documentation
Please refer to our extensive [Wiki documentation](https://github.com/dingo/api/wiki) for more information.
## API Boilerplate
If you are looking to start a new project from scratch, consider using the [Laravel 5 API Boilerplate](https://github.com/specialtactics/laravel5-api-boilerplate), which builds on top of the dingo-api package, and adds a lot of great features.
## Support
For answers you may not find in the Wiki, avoid posting issues. Feel free to ask for support on the dedicated [Slack](https://larachat.slack.com/messages/api/) room. Make sure to mention **specialtactics** / **@jasonlewis** so he is notified.
## License
This package is licensed under the [BSD 3-Clause license](http://opensource.org/licenses/BSD-3-Clause).
<?php
namespace Dingo\Api\Auth;
use Exception;
use Dingo\Api\Routing\Router;
use Illuminate\Container\Container;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
class Auth
{
/**
* Router instance.
*
* @var \Dingo\Api\Routing\Router
*/
protected $router;
/**
* Illuminate container instance.
*
* @var \Illuminate\Container\Container
*/
protected $container;
/**
* Array of available authentication providers.
*
* @var array
*/
protected $providers;
/**
* The provider used for authentication.
*
* @var \Dingo\Api\Contract\Auth\Provider
*/
protected $providerUsed;
/**
* Authenticated user instance.
*
* @var \Illuminate\Auth\GenericUser|\Illuminate\Database\Eloquent\Model
*/
protected $user;
/**
* Create a new auth instance.
*
* @param \Dingo\Api\Routing\Router $router
* @param \Illuminate\Container\Container $container
* @param array $providers
*
* @return void
*/
public function __construct(Router $router, Container $container, array $providers)
{
$this->router = $router;
$this->container = $container;
$this->providers = $providers;
}
/**
* Authenticate the current request.
*
* @param array $providers
*
* @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
*
* @return mixed
*/
public function authenticate(array $providers = [])
{
$exceptionStack = [];
// Spin through each of the registered authentication providers and attempt to
// authenticate through one of them. This allows a developer to implement
// and allow a number of different authentication mechanisms.
foreach ($this->filterProviders($providers) as $provider) {
try {
$user = $provider->authenticate($this->router->getCurrentRequest(), $this->router->getCurrentRoute());
$this->providerUsed = $provider;
return $this->user = $user;
} catch (UnauthorizedHttpException $exception) {
$exceptionStack[] = $exception;
} catch (BadRequestHttpException $exception) {
// We won't add this exception to the stack as it's thrown when the provider
// is unable to authenticate due to the correct authorization header not
// being set. We will throw an exception for this below.
}
}
$this->throwUnauthorizedException($exceptionStack);
}
/**
* Throw the first exception from the exception stack.
*
* @param array $exceptionStack
*
* @throws \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException
*
* @return void
*/
protected function throwUnauthorizedException(array $exceptionStack)
{
$exception = array_shift($exceptionStack);
if ($exception === null) {
$exception = new UnauthorizedHttpException('dingo', 'Failed to authenticate because of bad credentials or an invalid authorization header.');
}
throw $exception;
}
/**
* Filter the requested providers from the available providers.
*
* @param array $providers
*
* @return array
*/
protected function filterProviders(array $providers)
{
if (empty($providers)) {
return $this->providers;
}
return array_intersect_key($this->providers, array_flip($providers));
}
/**
* Get the authenticated user.
*
* @param bool $authenticate
*
* @return \Illuminate\Auth\GenericUser|\Illuminate\Database\Eloquent\Model|null
*/
public function getUser($authenticate = true)
{
if ($this->user) {
return $this->user;
} elseif (! $authenticate) {
return;
}
try {
return $this->user = $this->authenticate();
} catch (Exception $exception) {
return;
}
}
/**
* Alias for getUser.
*
* @param bool $authenticate
*
* @return \Illuminate\Auth\GenericUser|\Illuminate\Database\Eloquent\Model
*/
public function user($authenticate = true)
{
return $this->getUser($authenticate);
}
/**
* Set the authenticated user.
*
* @param \Illuminate\Auth\GenericUser|\Illuminate\Database\Eloquent\Model $user
*
* @return \Dingo\Api\Auth\Auth
*/
public function setUser($user)
{
$this->user = $user;
return $this;
}
/**
* Check if a user has authenticated with the API.
*
* @param bool $authenticate
*
* @return bool
*/
public function check($authenticate = false)
{
return ! is_null($this->user($authenticate));
}
/**
* Get the provider used for authentication.
*
* @return \Dingo\Api\Contract\Auth\Provider
*/
public function getProviderUsed()
{
return $this->providerUsed;
}
/**
* Extend the authentication layer with a custom provider.
*
* @param string $key
* @param object|callable $provider
*
* @return void
*/
public function extend($key, $provider)
{
if (is_callable($provider)) {
$provider = call_user_func($provider, $this->container);
}
$this->providers[$key] = $provider;
}
}
<?php
namespace Dingo\Api\Auth\Provider;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
abstract class Authorization implements \Dingo\Api\Contract\Auth\Provider
{
/**
* Array of provider specific options.
*
* @var array
*/
protected $options = [];
/**
* Validate the requests authorization header for the provider.
*
* @param \Illuminate\Http\Request $request
*
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
*
* @return bool
*/
public function validateAuthorizationHeader(Request $request)
{
if (Str::startsWith(strtolower($request->headers->get('authorization')), $this->getAuthorizationMethod())) {
return true;
}
throw new BadRequestHttpException;
}
/**
* Get the providers authorization method.
*
* @return string
*/
abstract public function getAuthorizationMethod();
}
<?php
namespace Dingo\Api\Auth\Provider;
use Dingo\Api\Routing\Route;
use Illuminate\Http\Request;
use Illuminate\Auth\AuthManager;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
class Basic extends Authorization
{
/**
* Illuminate authentication manager.
*
* @var \Illuminate\Auth\AuthManager
*/
protected $auth;
/**
* Basic auth identifier.
*
* @var string
*/
protected $identifier;
/**
* Create a new basic provider instance.
*
* @param \Illuminate\Auth\AuthManager $auth
* @param string $identifier
*
* @return void
*/
public function __construct(AuthManager $auth, $identifier = 'email')
{
$this->auth = $auth;
$this->identifier = $identifier;
}
/**
* Authenticate request with Basic.
*
* @param \Illuminate\Http\Request $request
* @param \Dingo\Api\Routing\Route $route
*
* @return mixed
*/
public function authenticate(Request $request, Route $route)
{
$this->validateAuthorizationHeader($request);
if (($response = $this->auth->onceBasic($this->identifier)) && $response->getStatusCode() === 401) {
throw new UnauthorizedHttpException('Basic', 'Invalid authentication credentials.');
}
return $this->auth->user();
}
/**
* Get the providers authorization method.
*
* @return string
*/
public function getAuthorizationMethod()
{
return 'basic';
}
}
<?php
namespace Dingo\Api\Auth\Provider;
use Exception;
use Tymon\JWTAuth\JWTAuth;
use Dingo\Api\Routing\Route;
use Illuminate\Http\Request;
use Tymon\JWTAuth\Exceptions\JWTException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
class JWT extends Authorization
{
/**
* The JWTAuth instance.
*
* @var \Tymon\JWTAuth\JWTAuth
*/
protected $auth;
/**
* Create a new JWT provider instance.
*
* @param \Tymon\JWTAuth\JWTAuth $auth
*
* @return void
*/
public function __construct(JWTAuth $auth)
{
$this->auth = $auth;
}
/**
* Authenticate request with a JWT.
*
* @param \Illuminate\Http\Request $request
* @param \Dingo\Api\Routing\Route $route
*
* @return mixed
*/
public function authenticate(Request $request, Route $route)
{
$token = $this->getToken($request);
try {
if (! $user = $this->auth->setToken($token)->authenticate()) {
throw new UnauthorizedHttpException('JWTAuth', 'Unable to authenticate with invalid token.');
}
} catch (JWTException $exception) {
throw new UnauthorizedHttpException('JWTAuth', $exception->getMessage(), $exception);
}
return $user;
}
/**
* Get the JWT from the request.
*
* @param \Illuminate\Http\Request $request
*
* @throws \Exception
*
* @return string
*/
protected function getToken(Request $request)
{
try {
$this->validateAuthorizationHeader($request);
$token = $this->parseAuthorizationHeader($request);
} catch (Exception $exception) {
if (! $token = $request->query('token', false)) {
throw $exception;
}
}
return $token;
}
/**
* Parse JWT from the authorization header.
*
* @param \Illuminate\Http\Request $request
*
* @return string
*/
protected function parseAuthorizationHeader(Request $request)
{
return trim(str_ireplace($this->getAuthorizationMethod(), '', $request->header('authorization')));
}
/**
* Get the providers authorization method.
*
* @return string
*/
public function getAuthorizationMethod()
{
return 'bearer';
}
}
<?php
namespace Dingo\Api\Console\Command;
use Dingo\Api\Routing\Router;
use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
use Dingo\Api\Contract\Routing\Adapter;
use Illuminate\Contracts\Console\Kernel;
class Cache extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
public $signature = 'api:cache';
/**
* The console command description.
*
* @var string
*/
public $description = 'Create a route cache file for faster route registration';
/**
* Filesystem instance.
*
* @var \Illuminate\Filesystem\Filesystem
*/
protected $files;
/**
* Router instance.
*
* @var \Dingo\Api\Routing\Router
*/
private $router;
/**
* Adapter instance.
*
* @var \Dingo\Api\Contract\Routing\Adapter
*/
private $adapter;
/**
* Create a new cache command instance.
*
* @param \Illuminate\Filesystem\Filesystem $files
* @param \Dingo\Api\Routing\Router $router
* @param \Dingo\Api\Contract\Routing\Adapter $adapter
*
* @return void
*/
public function __construct(Filesystem $files, Router $router, Adapter $adapter)
{
$this->files = $files;
$this->router = $router;
$this->adapter = $adapter;
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$this->callSilent('route:clear');
$app = $this->getFreshApplication();
$this->call('route:cache');
$routes = $app['api.router']->getAdapterRoutes();
foreach ($routes as $collection) {
foreach ($collection as $route) {
$app['api.router.adapter']->prepareRouteForSerialization($route);
}
}
$stub = "app('api.router')->setAdapterRoutes(unserialize(base64_decode('{{routes}}')));";
$path = $this->laravel->getCachedRoutesPath();
if (! $this->files->exists($path)) {
$stub = "<?php\n\n$stub";
}
$this->files->append(
$path,
str_replace('{{routes}}', base64_encode(serialize($routes)), $stub)
);
}
/**
* Get a fresh application instance.
*
* @return \Illuminate\Contracts\Container\Container
*/
protected function getFreshApplication()
{
if (method_exists($this->laravel, 'bootstrapPath')) {
$app = require $this->laravel->bootstrapPath().'/app.php';
} else {
$app = require $this->laravel->basePath().'/bootstrap/app.php';
}
$app->make(Kernel::class)->bootstrap();
return $app;
}
}
<?php
namespace Dingo\Api\Console\Command;
use ReflectionClass;
use Dingo\Blueprint\Writer;
use Illuminate\Support\Arr;
use Dingo\Api\Routing\Router;
use Dingo\Blueprint\Blueprint;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
class Docs extends Command
{
/**
* Router instance.
*
* @var \Dingo\Api\Routing\Router
*/
protected $router;
/**
* The blueprint instance.
*
* @var \Dingo\Blueprint\Blueprint
*/
protected $blueprint;
/**
* Blueprint instance.
*
* @var \Dingo\Blueprint\Blueprint
*/
protected $docs;
/**
* Writer instance.
*
* @var \Dingo\Blueprint\Writer
*/
protected $writer;
/**
* Default documentation name.
*
* @var string
*/
protected $name;
/**
* Default documentation version.
*
* @var string
*/
protected $version;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'api:docs {--name= : Name of the generated documentation}
{--use-version= : Version of the documentation to be generated}
{--output-file= : Output the generated documentation to a file}
{--include-path= : Path where included documentation files are located}
{--use-controller= : Specify a controller where to generate documentation for}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate API documentation from annotated controllers';
/**
* Create a new docs command instance.
*
* @param \Dingo\Api\Routing\Router $router
* @param \Dingo\Blueprint\Blueprint $blueprint
* @param \Dingo\Blueprint\Writer $writer
* @param string $name
* @param string $version
*
* @return void
*/
public function __construct(Router $router, Blueprint $blueprint, Writer $writer, $name, $version)
{
parent::__construct();
$this->router = $router;
$this->blueprint = $blueprint;
$this->writer = $writer;
$this->name = $name;
$this->version = $version;
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$contents = $this->blueprint->generate($this->getControllers(), $this->getDocName(), $this->getVersion(), $this->getIncludePath());
if ($file = $this->option('output-file')) {
$this->writer->write($contents, $file);
return $this->info('Documentation was generated successfully.');
}
return $this->line($contents);
}
/**
* Get the documentation name.
*
* @return string
*/
protected function getDocName()
{
$name = $this->option('name') ?: $this->name;
if (! $name) {
$this->comment('A name for the documentation was not supplied. Use the --name option or set a default in the configuration.');
exit;
}
return $name;
}
/**
* Get the include path for documentation files.
*
* @return string
*/
protected function getIncludePath()
{
return base_path($this->option('include-path'));
}
/**
* Get the documentation version.
*
* @return string
*/
protected function getVersion()
{
$version = $this->option('use-version') ?: $this->version;
if (! $version) {
$this->comment('A version for the documentation was not supplied. Use the --use-version option or set a default in the configuration.');
exit;
}
return $version;
}
/**
* Get all the controller instances.
*
* @return array
*/
protected function getControllers()
{
$controllers = new Collection;
if ($controller = $this->option('use-controller')) {
$this->addControllerIfNotExists($controllers, app($controller));
return $controllers;
}
foreach ($this->router->getRoutes() as $collections) {
foreach ($collections as $route) {
if ($controller = $route->getControllerInstance()) {
$this->addControllerIfNotExists($controllers, $controller);
}
}
}
return $controllers;
}
/**
* Add a controller to the collection if it does not exist. If the
* controller implements an interface suffixed with "Docs" it
* will be used instead of the controller.
*
* @param \Illuminate\Support\Collection $controllers
* @param object $controller
*
* @return void
*/
protected function addControllerIfNotExists(Collection $controllers, $controller)
{
$class = get_class($controller);
if ($controllers->has($class)) {
return;
}
$reflection = new ReflectionClass($controller);
$interface = Arr::first($reflection->getInterfaces(), function ($key, $value) {
return ends_with($key, 'Docs');
});
if ($interface) {
$controller = $interface;
}
$controllers->put($class, $controller);
}
}
<?php
namespace Dingo\Api\Console\Command;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Dingo\Api\Routing\Router;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Illuminate\Foundation\Console\RouteListCommand;
class Routes extends RouteListCommand
{
/**
* Dingo router instance.
*
* @var \Dingo\Api\Routing\Router
*/
protected $router;
/**
* Array of route collections.
*
* @var array
*/
protected $routes;
/**
* The console command name.
*
* @var string
*/
protected $name = 'api:routes';
/**
* The console command description.
*
* @var string
*/
protected $description = 'List all registered API routes';
/**
* The table headers for the command.
*
* @var array
*/
protected $headers = ['Host', 'Method', 'URI', 'Name', 'Action', 'Protected', 'Version(s)', 'Scope(s)', 'Rate Limit'];
/**
* Create a new routes command instance.
*
* @param \Dingo\Api\Routing\Router $router
*
* @return void
*/
public function __construct(Router $router)
{
// Ugly, but we need to bypass the constructor and directly target the
// constructor on the command class.
Command::__construct();
$this->router = $router;
}
/**
* Execute the console command.
*
* @return void
*/
public function fire()
{
$this->routes = $this->router->getRoutes();
parent::fire();
}
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$this->routes = $this->router->getRoutes();
parent::handle();
}
/**
* Compile the routes into a displayable format.
*
* @return array
*/
protected function getRoutes()
{
$routes = [];
foreach ($this->router->getRoutes() as $collection) {
foreach ($collection->getRoutes() as $route) {
$routes[] = $this->filterRoute([
'host' => $route->domain(),
'method' => implode('|', $route->methods()),
'uri' => $route->uri(),
'name' => $route->getName(),
'action' => $route->getActionName(),
'protected' => $route->isProtected() ? 'Yes' : 'No',
'versions' => implode(', ', $route->versions()),
'scopes' => implode(', ', $route->scopes()),
'rate' => $this->routeRateLimit($route),
]);
}
}
if ($sort = $this->option('sort')) {
$routes = Arr::sort($routes, function ($value) use ($sort) {
return $value[$sort];
});
}
if ($this->option('reverse')) {
$routes = array_reverse($routes);
}
if ($this->option('short')) {
$this->headers = ['Method', 'URI', 'Name', 'Version(s)'];
$routes = array_map(function ($item) {
return array_only($item, ['method', 'uri', 'name', 'versions']);
}, $routes);
}
return array_filter(array_unique($routes, SORT_REGULAR));
}
/**
* Display the routes rate limiting requests per second. This takes the limit
* and divides it by the expiration time in seconds to give you a rough
* idea of how many requests you'd be able to fire off per second
* on the route.
*
* @param \Dingo\Api\Routing\Route $route
*
* @return null|string
*/
protected function routeRateLimit($route)
{
[$limit, $expires] = [$route->getRateLimit(), $route->getRateLimitExpiration()];
if ($limit && $expires) {
return sprintf('%s req/s', round($limit / ($expires * 60), 2));
}
}
/**
* Filter the route by URI, Version, Scopes and / or name.
*
* @param array $route
*
* @return array|null
*/
protected function filterRoute(array $route)
{
$filters = ['name', 'path', 'protected', 'unprotected', 'versions', 'scopes'];
foreach ($filters as $filter) {
if ($this->option($filter) && ! $this->{'filterBy'.ucfirst($filter)}($route)) {
return;
}
}
return $route;
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
$options = parent::getOptions();
foreach ($options as $key => $option) {
if ($option[0] == 'sort') {
unset($options[$key]);
}
}
return array_merge(
$options,
[
['sort', null, InputOption::VALUE_OPTIONAL, 'The column (domain, method, uri, name, action) to sort by'],
['versions', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Filter the routes by version'],
['scopes', 'S', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'Filter the routes by scopes'],
['protected', null, InputOption::VALUE_NONE, 'Filter the protected routes'],
['unprotected', null, InputOption::VALUE_NONE, 'Filter the unprotected routes'],
['short', null, InputOption::VALUE_NONE, 'Get an abridged version of the routes'],
]
);
}
/**
* Filter the route by its path.
*
* @param array $route
*
* @return bool
*/
protected function filterByPath(array $route)
{
return Str::contains($route['uri'], $this->option('path'));
}
/**
* Filter the route by whether or not it is protected.
*
* @param array $route
*
* @return bool
*/
protected function filterByProtected(array $route)
{
return $this->option('protected') && $route['protected'] == 'Yes';
}
/**
* Filter the route by whether or not it is unprotected.
*
* @param array $route
*
* @return bool
*/
protected function filterByUnprotected(array $route)
{
return $this->option('unprotected') && $route['protected'] == 'No';
}
/**
* Filter the route by its versions.
*
* @param array $route
*
* @return bool
*/
protected function filterByVersions(array $route)
{
foreach ($this->option('versions') as $version) {
if (Str::contains($route['versions'], $version)) {
return true;
}
}
return false;
}
/**
* Filter the route by its name.
*
* @param array $route
*
* @return bool
*/
protected function filterByName(array $route)
{
return Str::contains($route['name'], $this->option('name'));
}
/**
* Filter the route by its scopes.
*
* @param array $route
*
* @return bool
*/
protected function filterByScopes(array $route)
{
foreach ($this->option('scopes') as $scope) {
if (Str::contains($route['scopes'], $scope)) {
return true;
}
}
return false;
}
}
<?php
namespace Dingo\Api\Contract\Auth;
use Dingo\Api\Routing\Route;
use Illuminate\Http\Request;
interface Provider
{
/**
* Authenticate the request and return the authenticated user instance.
*
* @param \Illuminate\Http\Request $request
* @param \Dingo\Api\Routing\Route $route
*
* @return mixed
*/
public function authenticate(Request $request, Route $route);
}
<?php
namespace Dingo\Api\Contract\Debug;
use Exception;
interface ExceptionHandler
{
/**
* Handle an exception.
*
* @param \Exception $exception
*
* @return \Illuminate\Http\Response
*/
public function handle(Exception $exception);
}
<?php
namespace Dingo\Api\Contract\Debug;
interface MessageBagErrors
{
/**
* Get the errors message bag.
*
* @return \Illuminate\Support\MessageBag
*/
public function getErrors();
/**
* Determine if message bag has any errors.
*
* @return bool
*/
public function hasErrors();
}
<?php
namespace Dingo\Api\Contract\Http;
use Illuminate\Http\Request as IlluminateRequest;
interface Parser
{
/**
* Parse an incoming request.
*
* @param \Illuminate\Http\Request $request
*
* @return mixed
*/
public function parse(IlluminateRequest $request);
}
<?php
namespace Dingo\Api\Contract\Http\RateLimit;
use Dingo\Api\Http\Request;
use Illuminate\Container\Container;
interface HasRateLimiter
{
/**
* Get rate limiter callable.
*
* @param \Illuminate\Container\Container $app
* @param \Dingo\Api\Http\Request $request
*
* @return string
*/
public function getRateLimiter(Container $app, Request $request);
}
<?php
namespace Dingo\Api\Contract\Http\RateLimit;
use Illuminate\Container\Container;
interface Throttle
{
/**
* Attempt to match the throttle against a given condition.
*
* @param \Illuminate\Container\Container $container
*
* @return bool
*/
public function match(Container $container);
/**
* Get the time in minutes that the throttles request limit will expire.
*
* @return int
*/
public function getExpires();
/**
* Get the throttles request limit.
*
* @return int
*/
public function getLimit();
}
<?php
namespace Dingo\Api\Contract\Http;
use Illuminate\Http\Request as IlluminateRequest;
interface Request
{
/**
* Create a new Dingo request instance from an Illuminate request instance.
*
* @param \Illuminate\Http\Request $old
*
* @return \Dingo\Api\Http\Request
*/
public function createFromIlluminate(IlluminateRequest $old);
}
<?php
namespace Dingo\Api\Contract\Http;
use Illuminate\Http\Request as IlluminateRequest;
interface Validator
{
/**
* Validate a request.
*
* @param \Illuminate\Http\Request $request
*
* @return bool
*/
public function validate(IlluminateRequest $request);
}
<?php
namespace Dingo\Api\Contract\Routing;
use Illuminate\Http\Request;
interface Adapter
{
/**
* Dispatch a request.
*
* @param \Illuminate\Http\Request $request
* @param string $version
*
* @return mixed
*/
public function dispatch(Request $request, $version);
/**
* Get the URI, methods, and action from the route.
*
* @param mixed $route
* @param \Illuminate\Http\Request $request
*
* @return array
*/
public function getRouteProperties($route, Request $request);
/**
* Add a route to the appropriate route collection.
*
* @param array $methods
* @param array $versions
* @param string $uri
* @param mixed $action
*
* @return void
*/
public function addRoute(array $methods, array $versions, $uri, $action);
/**
* Get all routes or only for a specific version.
*
* @param string $version
*
* @return mixed
*/
public function getRoutes($version = null);
/**
* Get a normalized iterable set of routes. Top level key must be a version with each
* version containing iterable routes that can be consumed by the adapter.
*
* @param string $version
*
* @return mixed
*/
public function getIterableRoutes($version = null);
/**
* Set the routes on the adapter.
*
* @param array $routes
*
* @return void
*/
public function setRoutes(array $routes);
/**
* Prepare a route for serialization.
*
* @param mixed $route
*
* @return mixed
*/
public function prepareRouteForSerialization($route);
}
<?php
namespace Dingo\Api\Contract\Transformer;
use Dingo\Api\Http\Request;
use Dingo\Api\Transformer\Binding;
interface Adapter
{
/**
* Transform a response with a transformer.
*
* @param mixed $response
* @param object $transformer
* @param \Dingo\Api\Transformer\Binding $binding
* @param \Dingo\Api\Http\Request $request
*
* @return array
*/
public function transform($response, $transformer, Binding $binding, Request $request);
}
<?php
namespace Dingo\Api\Event;
use Dingo\Api\Http\Request;
use Illuminate\Contracts\Container\Container;
class RequestWasMatched
{
/**
* Request instance.
*
* @var \Dingo\Api\Http\Request
*/
public $request;
/**
* Application instance.
*
* @var \Illuminate\Contracts\Container\Container
*/
public $app;
/**
* Create a new request was matched event.
*
* @param \Dingo\Api\Http\Request $request
* @param \Illuminate\Contracts\Container\Container $app
*
* @return void
*/
public function __construct(Request $request, Container $app)
{
$this->request = $request;
$this->app = $app;
}
}
<?php
namespace Dingo\Api\Event;
use Dingo\Api\Http\Response;
class ResponseIsMorphing
{
/**
* Response instance.
*
* @var \Dingo\Api\Http\Response
*/
public $response;
/**
* Response content.
*
* @var string
*/
public $content;
/**
* Create a new response is morphing event. Content is passed by reference
* so that multiple listeners can modify content.
*
* @param \Dingo\Api\Http\Response $response
* @param string $content
*
* @return void
*/
public function __construct(Response $response, &$content)
{
$this->response = $response;
$this->content = &$content;
}
}
<?php
namespace Dingo\Api\Event;
class ResponseWasMorphed extends ResponseIsMorphing
{
//
}
<?php
namespace Dingo\Api\Exception;
class DeleteResourceFailedException extends ResourceException
{
//
}
<?php
namespace Dingo\Api\Exception;
use Exception;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;
class InternalHttpException extends HttpException
{
/**
* The response.
*
* @var \Illuminate\Http\Response
*/
protected $response;
/**
* Create a new internal HTTP exception instance.
*
* @param \Symfony\Component\HttpFoundation\Response $response
* @param string $message
* @param \Exception $previous
* @param array $headers
* @param int $code
*
* @return void
*/
public function __construct(Response $response, $message = null, Exception $previous = null, array $headers = [], $code = 0)
{
$this->response = $response;
parent::__construct($response->getStatusCode(), $message, $previous, $headers, $code);
}
/**
* Get the response of the internal request.
*
* @return \Illuminate\Http\Response
*/
public function getResponse()
{
return $this->response;
}
}
<?php
namespace Dingo\Api\Exception;
use Exception;
use Symfony\Component\HttpKernel\Exception\HttpException;
class RateLimitExceededException extends HttpException
{
/**
* Create a new rate limit exceeded exception instance.
*
* @param string $message
* @param \Exception $previous
* @param array $headers
* @param int $code
*
* @return void
*/
public function __construct($message = null, Exception $previous = null, $headers = [], $code = 0)
{
if (array_key_exists('X-RateLimit-Reset', $headers)) {
$headers['Retry-After'] = $headers['X-RateLimit-Reset'] - time();
}
parent::__construct(429, $message ?: 'You have exceeded your rate limit.', $previous, $headers, $code);
}
}
<?php
namespace Dingo\Api\Exception;
use Exception;
use Illuminate\Support\MessageBag;
use Dingo\Api\Contract\Debug\MessageBagErrors;
use Symfony\Component\HttpKernel\Exception\HttpException;
class ResourceException extends HttpException implements MessageBagErrors
{
/**
* MessageBag errors.
*
* @var \Illuminate\Support\MessageBag
*/
protected $errors;
/**
* Create a new resource exception instance.
*
* @param string $message
* @param \Illuminate\Support\MessageBag|array $errors
* @param \Exception $previous
* @param array $headers
* @param int $code
*
* @return void
*/
public function __construct($message = null, $errors = null, Exception $previous = null, $headers = [], $code = 0)
{
if (is_null($errors)) {
$this->errors = new MessageBag;
} else {
$this->errors = is_array($errors) ? new MessageBag($errors) : $errors;
}
parent::__construct(422, $message, $previous, $headers, $code);
}
/**
* Get the errors message bag.
*
* @return \Illuminate\Support\MessageBag
*/
public function getErrors()
{
return $this->errors;
}
/**
* Determine if message bag has any errors.
*
* @return bool
*/
public function hasErrors()
{
return ! $this->errors->isEmpty();
}
}
<?php
namespace Dingo\Api\Exception;
class StoreResourceFailedException extends ResourceException
{
//
}
<?php
namespace Dingo\Api\Exception;
use Exception;
use Symfony\Component\HttpKernel\Exception\HttpException;
class UnknownVersionException extends HttpException
{
/**
* Create a new unknown version exception instance.
*
* @param string $message
* @param \Exception $previous
* @param int $code
*
* @return void
*/
public function __construct($message = null, Exception $previous = null, $code = 0)
{
parent::__construct(400, $message ?: 'The version given was unknown or has no registered routes.', $previous, [], $code);
}
}
<?php
namespace Dingo\Api\Exception;
class UpdateResourceFailedException extends ResourceException
{
//
}
<?php
namespace Dingo\Api\Exception;
use Exception;
class ValidationHttpException extends ResourceException
{
/**
* Create a new validation HTTP exception instance.
*
* @param \Illuminate\Support\MessageBag|array $errors
* @param \Exception $previous
* @param array $headers
* @param int $code
*
* @return void
*/
public function __construct($errors = null, Exception $previous = null, $headers = [], $code = 0)
{
parent::__construct(null, $errors, $previous, $headers, $code);
}
}
<?php
namespace Dingo\Api\Facade;
use Dingo\Api\Http\InternalRequest;
use Illuminate\Support\Facades\Facade;
class API extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'api.dispatcher';
}
/**
* Bind an exception handler.
*
* @param callable $callback
*
* @return void
*/
public static function error(callable $callback)
{
return static::$app['api.exception']->register($callback);
}
/**
* Register a class transformer.
*
* @param string $class
* @param string|\Closure $transformer
*
* @return \Dingo\Api\Transformer\Binding
*/
public static function transform($class, $transformer)
{
return static::$app['api.transformer']->register($class, $transformer);
}
/**
* Get the authenticator.
*
* @return \Dingo\Api\Auth\Auth
*/
public static function auth()
{
return static::$app['api.auth'];
}
/**
* Get the authenticated user.
*
* @return \Illuminate\Auth\GenericUser|\Illuminate\Database\Eloquent\Model
*/
public static function user()
{
return static::$app['api.auth']->user();
}
/**
* Determine if a request is internal.
*
* @return bool
*/
public static function internal()
{
return static::$app['api.router']->getCurrentRequest() instanceof InternalRequest;
}
/**
* Get the response factory to begin building a response.
*
* @return \Dingo\Api\Http\Response\Factory
*/
public static function response()
{
return static::$app['api.http.response'];
}
/**
* Get the API router instance.
*
* @return \Dingo\Api\Routing\Router
*/
public static function router()
{
return static::$app['api.router'];
}
/**
* Get the API route of the given name, and optionally specify the API version.
*
* @param string $routeName
* @param string $apiVersion
*
* @return string
*/
public static function route($routeName, $apiVersion = 'v1')
{
return static::$app['api.url']->version($apiVersion)->route($routeName);
}
}
<?php
namespace Dingo\Api\Facade;
use Illuminate\Support\Facades\Facade;
class Route extends Facade
{
/**
* Get the registered name of the component.
*
* @return string
*/
protected static function getFacadeAccessor()
{
return 'api.router';
}
}
<?php
namespace Dingo\Api\Http;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\Validation\Validator;
use Dingo\Api\Exception\ValidationHttpException;
use Illuminate\Validation\ValidatesWhenResolvedTrait;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Illuminate\Contracts\Validation\ValidatesWhenResolved;
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class FormRequest extends Request implements ValidatesWhenResolved
{
use ValidatesWhenResolvedTrait;
/**
* The container instance.
*
* @var \Illuminate\Contracts\Container\Container
*/
protected $container;
/**
* The redirector instance.
*
* @var \Illuminate\Routing\Redirector
*/
protected $redirector;
/**
* The URI to redirect to if validation fails.
*
* @var string
*/
protected $redirect;
/**
* The route to redirect to if validation fails.
*
* @var string
*/
protected $redirectRoute;
/**
* The controller action to redirect to if validation fails.
*
* @var string
*/
protected $redirectAction;
/**
* The key to be used for the view error bag.
*
* @var string
*/
protected $errorBag = 'default';
/**
* The input keys that should not be flashed on redirect.
*
* @var array
*/
protected $dontFlash = [
'password',
'password_confirmation',
];
/**
* Validate the request.
*/
public function validate()
{
if ($this->authorize() === false) {
throw new AccessDeniedHttpException();
}
$validator = app('validator')->make($this->all(), $this->rules(), $this->messages());
if ($validator->fails()) {
throw new ValidationHttpException($validator->errors());
}
}
/**
* Get the validator instance for the request.
*
* @return \Illuminate\Contracts\Validation\Validator
*
* @SuppressWarnings(PHPMD.ElseExpression)
*/
protected function getValidatorInstance()
{
$factory = $this->container->make(ValidationFactory::class);
if (method_exists($this, 'validator')) {
$validator = $this->container->call([$this, 'validator'], compact('factory'));
} else {
$validator = $this->createDefaultValidator($factory);
}
if (method_exists($this, 'withValidator')) {
$this->withValidator($validator);
}
return $validator;
}
/**
* Create the default validator instance.
*
* @param \Illuminate\Contracts\Validation\Factory $factory
*
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function createDefaultValidator(ValidationFactory $factory)
{
return $factory->make(
$this->validationData(),
$this->container->call([$this, 'rules']),
$this->messages(),
$this->attributes()
);
}
/**
* Get data to be validated from the request.
*
* @return array
*/
protected function validationData()
{
return $this->all();
}
/**
* Handle a failed validation attempt.
*
* @param \Illuminate\Contracts\Validation\Validator $validator
*
* @return void
*/
protected function failedValidation(Validator $validator)
{
if ($this->container['request'] instanceof Request) {
throw new ValidationHttpException($validator->errors());
}
parent::failedValidation($validator);
}
/**
* Get the proper failed validation response for the request.
*
* @param array $errors
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function response(array $errors)
{
if ($this->expectsJson()) {
return new JsonResponse($errors, 422);
}
return $this->redirector->to($this->getRedirectUrl())
->withInput($this->except($this->dontFlash))
->withErrors($errors, $this->errorBag);
}
/**
* Format the errors from the given Validator instance.
*
* @param \Illuminate\Contracts\Validation\Validator $validator
*
* @return array
*/
protected function formatErrors(Validator $validator)
{
return $validator->getMessageBag()->toArray();
}
/**
* Get the URL to redirect to on a validation error.
*
* @return string
*/
protected function getRedirectUrl()
{
$url = $this->redirector->getUrlGenerator();
if ($this->redirect) {
return $url->to($this->redirect);
} elseif ($this->redirectRoute) {
return $url->route($this->redirectRoute);
} elseif ($this->redirectAction) {
return $url->action($this->redirectAction);
}
return $url->previous();
}
/**
* Determine if the request passes the authorization check.
*
* @return bool
*/
protected function passesAuthorization()
{
if (method_exists($this, 'authorize')) {
return $this->container->call([$this, 'authorize']);
}
return false;
}
/**
* Handle a failed authorization attempt.
*
* @return void
*/
protected function failedAuthorization()
{
if ($this->container['request'] instanceof Request) {
throw new HttpException(403);
}
parent::failedAuthorization();
}
/**
* Get custom messages for validator errors.
*
* @return array
*/
public function messages()
{
return [];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes()
{
return [];
}
/**
* Set the Redirector instance.
*
* @param \Laravel\Lumen\Http\Redirector|\Illuminate\Routing\Redirector $redirector
*
* @return $this
*/
public function setRedirector($redirector)
{
$this->redirector = $redirector;
return $this;
}
/**
* Set the container implementation.
*
* @param \Illuminate\Contracts\Container\Container $container
*
* @return $this
*/
public function setContainer(Container $container)
{
$this->container = $container;
return $this;
}
}
<?php
namespace Dingo\Api\Http;
class InternalRequest extends Request
{
public function __construct(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
{
parent::__construct($query, $request, $attributes, $cookies, $files, $server, $content);
// Pass parameters inside internal request into Laravel's JSON ParameterBag,
// so that they can be accessed using $request->input()
if ($this->isJson() && isset($this->request)) {
$this->setJson($this->request);
}
}
}
<?php
namespace Dingo\Api\Http\Middleware;
use Closure;
use Dingo\Api\Routing\Router;
use Dingo\Api\Auth\Auth as Authentication;
class Auth
{
/**
* Router instance.
*
* @var \Dingo\Api\Routing\Router
*/
protected $router;
/**
* Authenticator instance.
*
* @var \Dingo\Api\Auth\Auth
*/
protected $auth;
/**
* Create a new auth middleware instance.
*
* @param \Dingo\Api\Routing\Router $router
* @param \Dingo\Api\Auth\Auth $auth
*
* @return void
*/
public function __construct(Router $router, Authentication $auth)
{
$this->router = $router;
$this->auth = $auth;
}
/**
* Perform authentication before a request is executed.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle($request, Closure $next)
{
$route = $this->router->getCurrentRoute();
if (! $this->auth->check(false)) {
$this->auth->authenticate($route->getAuthenticationProviders());
}
return $next($request);
}
}
<?php
namespace Dingo\Api\Http\Middleware;
use Closure;
use Dingo\Api\Routing\Router;
class PrepareController
{
/**
* Dingo router instance.
*
* @var \Dingo\Api\Routing\Router
*/
protected $router;
/**
* Create a new prepare controller instance.
*
* @param \Dingo\Api\Routing\Router $router
*
* @return void
*/
public function __construct(Router $router)
{
$this->router = $router;
}
/**
* Handle the request.
*
* @param \Dingo\Api\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle($request, Closure $next)
{
// To prepare the controller all we need to do is call the current method on the router to fetch
// the current route. This will create a new Dingo\Api\Routing\Route instance and prepare the
// controller by binding it as a singleton in the container. This will result in the
// controller only be instantiated once per request.
$this->router->current();
return $next($request);
}
}
<?php
namespace Dingo\Api\Http\Middleware;
use Closure;
use Dingo\Api\Http\Response;
use Dingo\Api\Routing\Router;
use Dingo\Api\Http\InternalRequest;
use Dingo\Api\Http\RateLimit\Handler;
use Dingo\Api\Exception\RateLimitExceededException;
class RateLimit
{
/**
* Router instance.
*
* @var \Dingo\Api\Routing\Router
*/
protected $router;
/**
* Rate limit handler instance.
*
* @var \Dingo\Api\Http\RateLimit\Handler
*/
protected $handler;
/**
* Create a new rate limit middleware instance.
*
* @param \Dingo\Api\Routing\Router $router
* @param \Dingo\Api\Http\RateLimit\Handler $handler
*
* @return void
*/
public function __construct(Router $router, Handler $handler)
{
$this->router = $router;
$this->handler = $handler;
}
/**
* Perform rate limiting before a request is executed.
*
* @param \Dingo\Api\Http\Request $request
* @param \Closure $next
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
*
* @return mixed
*/
public function handle($request, Closure $next)
{
if ($request instanceof InternalRequest) {
return $next($request);
}
$route = $this->router->getCurrentRoute();
if ($route->hasThrottle()) {
$this->handler->setThrottle($route->getThrottle());
}
$this->handler->rateLimitRequest($request, $route->getRateLimit(), $route->getRateLimitExpiration());
if ($this->handler->exceededRateLimit()) {
throw new RateLimitExceededException('You have exceeded your rate limit.', null, $this->getHeaders());
}
$response = $next($request);
if ($this->handler->requestWasRateLimited()) {
return $this->responseWithHeaders($response);
}
return $response;
}
/**
* Send the response with the rate limit headers.
*
* @param \Dingo\Api\Http\Response $response
*
* @return \Dingo\Api\Http\Response
*/
protected function responseWithHeaders($response)
{
foreach ($this->getHeaders() as $key => $value) {
$response->headers->set($key, $value);
}
return $response;
}
/**
* Get the headers for the response.
*
* @return array
*/
protected function getHeaders()
{
return [
'X-RateLimit-Limit' => $this->handler->getThrottleLimit(),
'X-RateLimit-Remaining' => $this->handler->getRemainingLimit(),
'X-RateLimit-Reset' => $this->handler->getRateLimitReset(),
];
}
}
<?php
namespace Dingo\Api\Http\Middleware;
use Closure;
use Exception;
use Dingo\Api\Routing\Router;
use Laravel\Lumen\Application;
use Illuminate\Pipeline\Pipeline;
use Dingo\Api\Http\RequestValidator;
use Dingo\Api\Event\RequestWasMatched;
use Dingo\Api\Http\Request as HttpRequest;
use Illuminate\Contracts\Container\Container;
use Dingo\Api\Contract\Debug\ExceptionHandler;
use Dingo\Api\Contract\Http\Request as RequestContract;
use Illuminate\Contracts\Events\Dispatcher as EventDispatcher;
use Illuminate\Contracts\Debug\ExceptionHandler as LaravelExceptionHandler;
class Request
{
/**
* Application instance.
*
* @var \Illuminate\Contracts\Foundation\Application
*/
protected $app;
/**
* Exception handler instance.
*
* @var \Dingo\Api\Contract\Debug\ExceptionHandler
*/
protected $exception;
/**
* Router instance.
*
* @var \Dingo\Api\Routing\Router
*/
protected $router;
/**
* HTTP validator instance.
*
* @var \Dingo\Api\Http\Validator
*/
protected $validator;
/**
* Event dispatcher instance.
*
* @var \Illuminate\Contracts\Events\Dispatcher
*/
protected $events;
/**
* Array of middleware.
*
* @var array
*/
protected $middleware = [];
/**
* Create a new request middleware instance.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @param \Dingo\Api\Contract\Debug\ExceptionHandler $exception
* @param \Dingo\Api\Routing\Router $router
* @param \Dingo\Api\Http\RequestValidator $validator
* @param \Illuminate\Contracts\Events\Dispatcher $events
*
* @return void
*/
public function __construct(Container $app, ExceptionHandler $exception, Router $router, RequestValidator $validator, EventDispatcher $events)
{
$this->app = $app;
$this->exception = $exception;
$this->router = $router;
$this->validator = $validator;
$this->events = $events;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
*
* @return mixed
*/
public function handle($request, Closure $next)
{
try {
if ($this->validator->validateRequest($request)) {
$this->app->singleton(LaravelExceptionHandler::class, function ($app) {
return $app[ExceptionHandler::class];
});
$request = $this->app->make(RequestContract::class)->createFromIlluminate($request);
$this->events->dispatch(new RequestWasMatched($request, $this->app));
return $this->sendRequestThroughRouter($request);
}
} catch (Exception $exception) {
$this->exception->report($exception);
return $this->exception->handle($exception);
}
return $next($request);
}
/**
* Send the request through the Dingo router.
*
* @param \Dingo\Api\Http\Request $request
*
* @return \Dingo\Api\Http\Response
*/
protected function sendRequestThroughRouter(HttpRequest $request)
{
$this->app->instance('request', $request);
return (new Pipeline($this->app))->send($request)->through($this->middleware)->then(function ($request) {
return $this->router->dispatch($request);
});
}
/**
* Call the terminate method on middlewares.
*
* @return void
*/
public function terminate($request, $response)
{
if (! ($request = $this->app['request']) instanceof HttpRequest) {
return;
}
// Laravel's route middlewares can be terminated just like application
// middleware, so we'll gather all the route middleware here.
// On Lumen this will simply be an empty array as it does
// not implement terminable route middleware.
$middlewares = $this->gatherRouteMiddlewares($request);
// Because of how middleware is executed on Lumen we'll need to merge in the
// application middlewares now so that we can terminate them. Laravel does
// not need this as it handles things a little more gracefully so it
// can terminate the application ones itself.
if (class_exists(Application::class, false)) {
$middlewares = array_merge($middlewares, $this->middleware);
}
foreach ($middlewares as $middleware) {
if ($middleware instanceof Closure) {
continue;
}
[$name, $parameters] = $this->parseMiddleware($middleware);
$instance = $this->app->make($name);
if (method_exists($instance, 'terminate')) {
$instance->terminate($request, $response);
}
}
}
/**
* Parse a middleware string to get the name and parameters.
*
* @author Taylor Otwell
*
* @param string $middleware
*
* @return array
*/
protected function parseMiddleware($middleware)
{
[$name, $parameters] = array_pad(explode(':', $middleware, 2), 2, []);
if (is_string($parameters)) {
$parameters = explode(',', $parameters);
}
return [$name, $parameters];
}
/**
* Gather the middlewares for the route.
*
* @param \Dingo\Api\Http\Request $request
*
* @return array
*/
protected function gatherRouteMiddlewares($request)
{
if ($route = $request->route()) {
return $this->router->gatherRouteMiddlewares($route);
}
return [];
}
/**
* Set the middlewares.
*
* @param array $middleware
*
* @return void
*/
public function setMiddlewares(array $middleware)
{
$this->middleware = $middleware;
}
/**
* Merge new middlewares onto the existing middlewares.
*
* @param array $middleware
*
* @return void
*/
public function mergeMiddlewares(array $middleware)
{
$this->middleware = array_merge($this->middleware, $middleware);
}
}
<?php
namespace Dingo\Api\Http\Parser;
use Illuminate\Http\Request;
use Dingo\Api\Contract\Http\Parser;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
class Accept implements Parser
{
/**
* Standards tree.
*
* @var string
*/
protected $standardsTree;
/**
* API subtype.
*
* @var string
*/
protected $subtype;
/**
* Default version.
*
* @var string
*/
protected $version;
/**
* Default format.
*
* @var string
*/
protected $format;
/**
* Create a new accept parser instance.
*
* @param string $standardsTree
* @param string $subtype
* @param string $version
* @param string $format
*
* @return void
*/
public function __construct($standardsTree, $subtype, $version, $format)
{
$this->standardsTree = $standardsTree;
$this->subtype = $subtype;
$this->version = $version;
$this->format = $format;
}
/**
* Parse the accept header on the incoming request. If strict is enabled
* then the accept header must be available and must be a valid match.
*
* @param \Illuminate\Http\Request $request
* @param bool $strict
*
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
*
* @return array
*/
public function parse(Request $request, $strict = false)
{
$pattern = '/application\/'.$this->standardsTree.'\.('.$this->subtype.')\.([\w\d\.\-]+)\+([\w]+)/';
if (! preg_match($pattern, $request->header('accept'), $matches)) {
if ($strict) {
throw new BadRequestHttpException('Accept header could not be properly parsed because of a strict matching process.');
}
$default = 'application/'.$this->standardsTree.'.'.$this->subtype.'.'.$this->version.'+'.$this->format;
preg_match($pattern, $default, $matches);
}
return array_combine(['subtype', 'version', 'format'], array_slice($matches, 1));
}
}
<?php
namespace Dingo\Api\Http\RateLimit;
use Dingo\Api\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Cache\CacheManager;
use Illuminate\Support\Collection;
use Illuminate\Container\Container;
use Dingo\Api\Http\RateLimit\Throttle\Route;
use Dingo\Api\Contract\Http\RateLimit\Throttle;
use Dingo\Api\Contract\Http\RateLimit\HasRateLimiter;
class Handler
{
/**
* Container instance.
*
* @var \Illuminate\Container\Container
*/
protected $container;
/**
* Cache instance.
*
* @var \Illuminate\Cache\CacheManager
*/
protected $cache;
/**
* Registered throttles.
*
* @var \Illuminate\Support\Collection
*/
protected $throttles;
/**
* Throttle used for rate limiting.
*
* @var \Dingo\Api\Contract\Http\RateLimit\Throttle
*/
protected $throttle;
/**
* Request instance being throttled.
*
* @var \Dingo\Api\Http\Request
*/
protected $request;
/**
* The key prefix used when throttling route specific requests.
*
* @var string
*/
protected $keyPrefix;
/**
* A callback used to define the limiter.
*
* @var callable
*/
protected $limiter;
/**
* Create a new rate limit handler instance.
*
* @param \Illuminate\Container\Container $container
* @param \Illuminate\Cache\CacheManager $cache
* @param array $throttles
*
* @return void
*/
public function __construct(Container $container, CacheManager $cache, array $throttles)
{
$this->cache = $cache;
$this->container = $container;
$this->throttles = new Collection($throttles);
}
/**
* Execute the rate limiting for the given request.
*
* @param \Dingo\Api\Http\Request $request
* @param int $limit
* @param int $expires
*
* @return void
*/
public function rateLimitRequest(Request $request, $limit = 0, $expires = 0)
{
$this->request = $request;
// If the throttle instance is already set then we'll just carry on as
// per usual.
if ($this->throttle instanceof Throttle) {
// If the developer specified a certain amount of requests or expiration
// time on a specific route then we'll always use the route specific
// throttle with the given values.
} elseif ($limit > 0 || $expires > 0) {
$this->throttle = new Route(['limit' => $limit, 'expires' => $expires]);
$this->keyPrefix = sha1($request->path());
// Otherwise we'll use the throttle that gives the consumer the largest
// amount of requests. If no matching throttle is found then rate
// limiting will not be imposed for the request.
} else {
$this->throttle = $this->getMatchingThrottles()->sort(function ($a, $b) {
return $a->getLimit() < $b->getLimit();
})->first();
}
if (is_null($this->throttle)) {
return;
}
if ($this->throttle instanceof HasRateLimiter) {
$this->setRateLimiter([$this->throttle, 'getRateLimiter']);
}
$this->prepareCacheStore();
$this->cache('requests', 0, $this->throttle->getExpires());
$this->cache('expires', $this->throttle->getExpires(), $this->throttle->getExpires());
$this->cache('reset', time() + ($this->throttle->getExpires() * 60), $this->throttle->getExpires());
$this->increment('requests');
}
/**
* Prepare the cache store.
*
* @return void
*/
protected function prepareCacheStore()
{
if ($this->retrieve('expires') != $this->throttle->getExpires()) {
$this->forget('requests');
$this->forget('expires');
$this->forget('reset');
}
}
/**
* Determine if the rate limit has been exceeded.
*
* @return bool
*/
public function exceededRateLimit()
{
return $this->requestWasRateLimited() ? $this->retrieve('requests') > $this->throttle->getLimit() : false;
}
/**
* Get matching throttles after executing the condition of each throttle.
*
* @return \Illuminate\Support\Collection
*/
protected function getMatchingThrottles()
{
return $this->throttles->filter(function ($throttle) {
return $throttle->match($this->container);
});
}
/**
* Namespace a cache key.
*
* @param string $key
*
* @return string
*/
protected function key($key)
{
return sprintf('dingo.api.%s.%s', $key, $this->getRateLimiter());
}
/**
* Cache a value under a given key for a certain amount of minutes.
*
* @param string $key
* @param mixed $value
* @param int $minutes
*
* @return void
*/
protected function cache($key, $value, $minutes)
{
$this->cache->add($this->key($key), $value, Carbon::now()->addMinutes($minutes));
}
/**
* Retrieve a value from the cache store.
*
* @param string $key
*
* @return mixed
*/
protected function retrieve($key)
{
return $this->cache->get($this->key($key));
}
/**
* Increment a key in the cache.
*
* @param string $key
*
* @return void
*/
protected function increment($key)
{
$this->cache->increment($this->key($key));
}
/**
* Forget a key in the cache.
*
* @param string $key
*
* @return void
*/
protected function forget($key)
{
$this->cache->forget($this->key($key));
}
/**
* Determine if the request was rate limited.
*
* @return bool
*/
public function requestWasRateLimited()
{
return ! is_null($this->throttle);
}
/**
* Get the rate limiter.
*
* @return string
*/
public function getRateLimiter()
{
return call_user_func($this->limiter ?: function ($container, $request) {
return $request->getClientIp();
}, $this->container, $this->request);
}
/**
* Set the rate limiter.
*
* @param callable $limiter
*
* @return void
*/
public function setRateLimiter(callable $limiter)
{
$this->limiter = $limiter;
}
/**
* Set the throttle to use for rate limiting.
*
* @param string|\Dingo\Api\Contract\Http\RateLimit\Throttle $throttle
*
* @return void
*/
public function setThrottle($throttle)
{
if (is_string($throttle)) {
$throttle = $this->container->make($throttle);
}
$this->throttle = $throttle;
}
/**
* Get the throttle used to rate limit the request.
*
* @return \Dingo\Api\Contract\Http\RateLimit\Throttle
*/
public function getThrottle()
{
return $this->throttle;
}
/**
* Get the limit of the throttle used.
*
* @return int
*/
public function getThrottleLimit()
{
return $this->throttle->getLimit();
}
/**
* Get the remaining limit before the consumer is rate limited.
*
* @return int
*/
public function getRemainingLimit()
{
$remaining = $this->throttle->getLimit() - $this->retrieve('requests');
return $remaining > 0 ? $remaining : 0;
}
/**
* Get the timestamp for when the current rate limiting will expire.
*
* @return int
*/
public function getRateLimitReset()
{
return $this->retrieve('reset');
}
/**
* Extend the rate limiter by adding a new throttle.
*
* @param callable|\Dingo\Api\Http\RateLimit\Throttle $throttle
*
* @return void
*/
public function extend($throttle)
{
if (is_callable($throttle)) {
$throttle = call_user_func($throttle, $this->container);
}
$this->throttles->push($throttle);
}
}
<?php
namespace Dingo\Api\Http\RateLimit\Throttle;
use Illuminate\Container\Container;
class Authenticated extends Throttle
{
/**
* Authenticated throttle will be matched when request is authenticated.
*
* @param \Illuminate\Container\Container $container
*
* @return bool
*/
public function match(Container $container)
{
return $container['api.auth']->check();
}
}
<?php
namespace Dingo\Api\Http\RateLimit\Throttle;
use Illuminate\Container\Container;
class Route extends Throttle
{
/**
* Route specific throttles always match.
*
* @param \Illuminate\Container\Container $container
*
* @return bool
*/
public function match(Container $container)
{
return true;
}
}
<?php
namespace Dingo\Api\Http\RateLimit\Throttle;
use Dingo\Api\Contract\Http\RateLimit\Throttle as RateLimitThrottle;
abstract class Throttle implements RateLimitThrottle
{
/**
* Array of throttle options.
*
* @var array
*/
protected $options = ['limit' => 60, 'expires' => 60];
/**
* Create a new throttle instance.
*
* @param array $options
*
* @return void
*/
public function __construct(array $options = [])
{
$this->options = array_merge($this->options, $options);
}
/**
* Get the throttles request limit.
*
* @return int
*/
public function getLimit()
{
return $this->options['limit'];
}
/**
* Get the time in minutes that the throttles request limit will expire.
*
* @return int
*/
public function getExpires()
{
return $this->options['expires'];
}
}
<?php
namespace Dingo\Api\Http\RateLimit\Throttle;
use Illuminate\Container\Container;
class Unauthenticated extends Throttle
{
/**
* Unauthenticated throttle will be matched when request is not authenticated.
*
* @param \Illuminate\Container\Container $container
*
* @return bool
*/
public function match(Container $container)
{
return ! $container['api.auth']->check();
}
}
<?php
namespace Dingo\Api\Http;
use Dingo\Api\Http\Parser\Accept;
use Illuminate\Http\Request as IlluminateRequest;
use Dingo\Api\Contract\Http\Request as RequestInterface;
class Request extends IlluminateRequest implements RequestInterface
{
/**
* Accept parser instance.
*
* @var \Dingo\Api\Http\Parser\Accept
*/
protected static $acceptParser;
/**
* Parsed accept header for the request.
*
* @var array
*/
protected $accept;
/**
* Create a new Dingo request instance from an Illuminate request instance.
*
* @param \Illuminate\Http\Request $old
*
* @return \Dingo\Api\Http\Request
*/
public function createFromIlluminate(IlluminateRequest $old)
{
$new = new static(
$old->query->all(), $old->request->all(), $old->attributes->all(),
$old->cookies->all(), $old->files->all(), $old->server->all(), $old->content
);
if ($session = $old->getSession()) {
$new->setLaravelSession($old->getSession());
}
$new->setRouteResolver($old->getRouteResolver());
$new->setUserResolver($old->getUserResolver());
return $new;
}
/**
* Get the defined version.
*
* @return string
*/
public function version()
{
$this->parseAcceptHeader();
return $this->accept['version'];
}
/**
* Get the defined subtype.
*
* @return string
*/
public function subtype()
{
$this->parseAcceptHeader();
return $this->accept['subtype'];
}
/**
* Get the expected format type.
*
* @return string
*/
public function format($default = 'html')
{
$this->parseAcceptHeader();
return $this->accept['format'] ?: parent::format($default);
}
/**
* Parse the accept header.
*
* @return void
*/
protected function parseAcceptHeader()
{
if ($this->accept) {
return;
}
$this->accept = static::$acceptParser->parse($this);
}
/**
* Set the accept parser instance.
*
* @param \Dingo\Api\Http\Parser\Accept $acceptParser
*
* @return void
*/
public static function setAcceptParser(Accept $acceptParser)
{
static::$acceptParser = $acceptParser;
}
/**
* Get the accept parser instance.
*
* @return \Dingo\Api\Http\Parser\Accept
*/
public static function getAcceptParser()
{
return static::$acceptParser;
}
}
<?php
namespace Dingo\Api\Http;
use Illuminate\Container\Container;
use Dingo\Api\Http\Validation\Accept;
use Dingo\Api\Http\Validation\Domain;
use Dingo\Api\Http\Validation\Prefix;
use Dingo\Api\Contract\Http\Validator;
use Illuminate\Http\Request as IlluminateRequest;
class RequestValidator
{
/**
* Container instance.
*
* @var \Illuminate\Container\Container
*/
protected $container;
/**
* Array of request validators.
*
* @var array
*/
protected $validators = [
Domain::class,
Prefix::class,
];
/**
* Create a new request validator instance.
*
* @param \Illuminate\Container\Container $container
*
* @return void
*/
public function __construct(Container $container)
{
$this->container = $container;
}
/**
* Replace the validators.
*
* @param array $validators
*
* @return void
*/
public function replace(array $validators)
{
$this->validators = $validators;
}
/**
* Merge an array of validators.
*
* @param array $validators
*
* @return void
*/
public function merge(array $validators)
{
$this->validators = array_merge($this->validators, $validators);
}
/**
* Extend the validators.
*
* @param string|\Dingo\Api\Http\Validator $validator
*
* @return void
*/
public function extend($validator)
{
$this->validators[] = $validator;
}
/**
* Validate a request.
*
* @param \Illuminate\Http\Request $request
*
* @return bool
*/
public function validateRequest(IlluminateRequest $request)
{
$passed = false;
foreach ($this->validators as $validator) {
$validator = $this->container->make($validator);
if ($validator instanceof Validator && $validator->validate($request)) {
$passed = true;
}
}
// The accept validator will always be run once any of the previous validators have
// been run. This ensures that we only run the accept validator once we know we
// have a request that is targeting the API.
if ($passed) {
$this->container->make(Accept::class)->validate($request);
}
return $passed;
}
}
<?php
namespace Dingo\Api\Http\Response;
use Closure;
use ErrorException;
use Illuminate\Support\Str;
use Dingo\Api\Http\Response;
use Illuminate\Support\Collection;
use Illuminate\Contracts\Pagination\Paginator;
use Dingo\Api\Transformer\Factory as TransformerFactory;
use Symfony\Component\HttpKernel\Exception\HttpException;
class Factory
{
/**
* Transformer factory instance.
*
* @var \Dingo\Api\Transformer\Factory
*/
protected $transformer;
/**
* Create a new response factory instance.
*
* @param \Dingo\Api\Transformer\Factory $transformer
*
* @return void
*/
public function __construct(TransformerFactory $transformer)
{
$this->transformer = $transformer;
}
/**
* Respond with a created response and associate a location if provided.
*
* @param null|string $location
*
* @return \Dingo\Api\Http\Response
*/
public function created($location = null, $content = null)
{
$response = new Response($content);
$response->setStatusCode(201);
if (! is_null($location)) {
$response->header('Location', $location);
}
return $response;
}
/**
* Respond with an accepted response and associate a location and/or content if provided.
*
* @param null|string $location
* @param mixed $content
*
* @return \Dingo\Api\Http\Response
*/
public function accepted($location = null, $content = null)
{
$response = new Response($content);
$response->setStatusCode(202);
if (! is_null($location)) {
$response->header('Location', $location);
}
return $response;
}
/**
* Respond with a no content response.
*
* @return \Dingo\Api\Http\Response
*/
public function noContent()
{
$response = new Response(null);
return $response->setStatusCode(204);
}
/**
* Bind a collection to a transformer and start building a response.
*
* @param \Illuminate\Support\Collection $collection
* @param string|callable|object $transformer
* @param array|\Closure $parameters
* @param \Closure|null $after
*
* @return \Dingo\Api\Http\Response
*/
public function collection(Collection $collection, $transformer, $parameters = [], Closure $after = null)
{
if ($collection->isEmpty()) {
$class = get_class($collection);
} else {
$class = get_class($collection->first());
}
if ($parameters instanceof \Closure) {
$after = $parameters;
$parameters = [];
}
$binding = $this->transformer->register($class, $transformer, $parameters, $after);
return new Response($collection, 200, [], $binding);
}
/**
* Bind an item to a transformer and start building a response.
*
* @param object $item
* @param string|callable|object $transformer
* @param array $parameters
* @param \Closure $after
*
* @return \Dingo\Api\Http\Response
*/
public function item($item, $transformer, $parameters = [], Closure $after = null)
{
$class = get_class($item);
if ($parameters instanceof \Closure) {
$after = $parameters;
$parameters = [];
}
$binding = $this->transformer->register($class, $transformer, $parameters, $after);
return new Response($item, 200, [], $binding);
}
/**
* Bind a paginator to a transformer and start building a response.
*
* @param \Illuminate\Contracts\Pagination\Paginator $paginator
* @param string|callable|object $transformer
* @param array $parameters
* @param \Closure $after
*
* @return \Dingo\Api\Http\Response
*/
public function paginator(Paginator $paginator, $transformer, array $parameters = [], Closure $after = null)
{
if ($paginator->isEmpty()) {
$class = get_class($paginator);
} else {
$class = get_class($paginator->first());
}
$binding = $this->transformer->register($class, $transformer, $parameters, $after);
return new Response($paginator, 200, [], $binding);
}
/**
* Return an error response.
*
* @param string $message
* @param int $statusCode
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
*
* @return void
*/
public function error($message, $statusCode)
{
throw new HttpException($statusCode, $message);
}
/**
* Return a 404 not found error.
*
* @param string $message
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
*
* @return void
*/
public function errorNotFound($message = 'Not Found')
{
$this->error($message, 404);
}
/**
* Return a 400 bad request error.
*
* @param string $message
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
*
* @return void
*/
public function errorBadRequest($message = 'Bad Request')
{
$this->error($message, 400);
}
/**
* Return a 403 forbidden error.
*
* @param string $message
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
*
* @return void
*/
public function errorForbidden($message = 'Forbidden')
{
$this->error($message, 403);
}
/**
* Return a 500 internal server error.
*
* @param string $message
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
*
* @return void
*/
public function errorInternal($message = 'Internal Error')
{
$this->error($message, 500);
}
/**
* Return a 401 unauthorized error.
*
* @param string $message
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
*
* @return void
*/
public function errorUnauthorized($message = 'Unauthorized')
{
$this->error($message, 401);
}
/**
* Return a 405 method not allowed error.
*
* @param string $message
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
*
* @return void
*/
public function errorMethodNotAllowed($message = 'Method Not Allowed')
{
$this->error($message, 405);
}
/**
* Call magic methods beginning with "with".
*
* @param string $method
* @param array $parameters
*
* @throws \ErrorException
*
* @return mixed
*/
public function __call($method, $parameters)
{
if (Str::startsWith($method, 'with')) {
return call_user_func_array([$this, Str::camel(substr($method, 4))], $parameters);
// Because PHP won't let us name the method "array" we'll simply watch for it
// in here and return the new binding. Gross. This is now DEPRECATED and
// should not be used. Just return an array or a new response instance.
} elseif ($method == 'array') {
return new Response($parameters[0]);
}
throw new ErrorException('Undefined method '.get_class($this).'::'.$method);
}
}
<?php
namespace Dingo\Api\Http\Response\Format;
abstract class Format
{
/**
* Illuminate request instance.
*
* @var \Illuminate\Http\Request
*/
protected $request;
/**
* Illuminate response instance.
*
* @var \Illuminate\Http\Response
*/
protected $response;
/*
* Array of formats' options.
*
* @var array
*/
protected $options;
/**
* Set the request instance.
*
* @param \Illuminate\Http\Request $request
*
* @return \Dingo\Api\Http\Response\Format\Format
*/
public function setRequest($request)
{
$this->request = $request;
return $this;
}
/**
* Set the response instance.
*
* @param \Illuminate\Http\Response $response
*
* @return \Dingo\Api\Http\Response\Format\Format
*/
public function setResponse($response)
{
$this->response = $response;
return $this;
}
/**
* Set the formats' options.
*
* @param array $options
*
* @return \Dingo\Api\Http\Response\Format\Format
*/
public function setOptions(array $options)
{
$this->options = $options;
return $this;
}
/**
* Format an Eloquent model.
*
* @param \Illuminate\Database\Eloquent\Model $model
*
* @return string
*/
abstract public function formatEloquentModel($model);
/**
* Format an Eloquent collection.
*
* @param \Illuminate\Database\Eloquent\Collection $collection
*
* @return string
*/
abstract public function formatEloquentCollection($collection);
/**
* Format an array or instance implementing Arrayable.
*
* @param array|\Illuminate\Contracts\Support\Arrayable $content
*
* @return string
*/
abstract public function formatArray($content);
/**
* Get the response content type.
*
* @return string
*/
abstract public function getContentType();
}
<?php
namespace Dingo\Api\Http\Response\Format;
use Illuminate\Support\Str;
use Illuminate\Contracts\Support\Arrayable;
class Json extends Format
{
/*
* JSON format (as well as JSONP) uses JsonOptionalFormatting trait, which
* provides extra functionality for the process of encoding data to
* its JSON representation.
*/
use JsonOptionalFormatting;
/**
* Format an Eloquent model.
*
* @param \Illuminate\Database\Eloquent\Model $model
*
* @return string
*/
public function formatEloquentModel($model)
{
$key = Str::singular($model->getTable());
if (! $model::$snakeAttributes) {
$key = Str::camel($key);
}
return $this->encode([$key => $model->toArray()]);
}
/**
* Format an Eloquent collection.
*
* @param \Illuminate\Database\Eloquent\Collection $collection
*
* @return string
*/
public function formatEloquentCollection($collection)
{
if ($collection->isEmpty()) {
return $this->encode([]);
}
$model = $collection->first();
$key = Str::plural($model->getTable());
if (! $model::$snakeAttributes) {
$key = Str::camel($key);
}
return $this->encode([$key => $collection->toArray()]);
}
/**
* Format an array or instance implementing Arrayable.
*
* @param array|\Illuminate\Contracts\Support\Arrayable $content
*
* @return string
*/
public function formatArray($content)
{
$content = $this->morphToArray($content);
array_walk_recursive($content, function (&$value) {
$value = $this->morphToArray($value);
});
return $this->encode($content);
}
/**
* Get the response content type.
*
* @return string
*/
public function getContentType()
{
return 'application/json';
}
/**
* Morph a value to an array.
*
* @param array|\Illuminate\Contracts\Support\Arrayable $value
*
* @return array
*/
protected function morphToArray($value)
{
return $value instanceof Arrayable ? $value->toArray() : $value;
}
/**
* Encode the content to its JSON representation.
*
* @param mixed $content
*
* @return string
*/
protected function encode($content)
{
$jsonEncodeOptions = [];
// Here is a place, where any available JSON encoding options, that
// deal with users' requirements to JSON response formatting and
// structure, can be conveniently applied to tweak the output.
if ($this->isJsonPrettyPrintEnabled()) {
$jsonEncodeOptions[] = JSON_PRETTY_PRINT;
}
$encodedString = $this->performJsonEncoding($content, $jsonEncodeOptions);
if ($this->isCustomIndentStyleRequired()) {
$encodedString = $this->indentPrettyPrintedJson(
$encodedString,
$this->options['indent_style']
);
}
return $encodedString;
}
}
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment