);
}
export default Example;
if (document.getElementById('example')) {
const Index = ReactDOM.createRoot(document.getElementById("example"));
Index.render(
)
}
ui/src/Presets/react-stubs/app.js 0000644 00000000764 15242753746 0012775 0 ustar 00 /**
* First we will load all of this project's JavaScript dependencies which
* includes React and other helpers. It's a great starting point while
* building robust, powerful web applications using React + Laravel.
*/
import './bootstrap';
/**
* Next, we will create a fresh React component instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
import './components/Example';
ui/src/Presets/vue-stubs/vite.config.js 0000644 00000001206 15242753746 0014121 0 ustar 00 import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [
laravel({
input: [
'resources/sass/app.scss',
'resources/js/app.js',
],
refresh: true,
}),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
}),
],
resolve: {
alias: {
vue: 'vue/dist/vue.esm-bundler.js',
},
},
});
ui/src/Presets/vue-stubs/ExampleComponent.vue 0000644 00000001050 15242753746 0015344 0 ustar 00
Example Component
I'm an example component.
ui/src/Presets/vue-stubs/app.js 0000644 00000002601 15242753746 0012466 0 ustar 00 /**
* First we will load all of this project's JavaScript dependencies which
* includes Vue and other libraries. It is a great starting point when
* building robust, powerful web applications using Vue and Laravel.
*/
import './bootstrap';
import { createApp } from 'vue';
/**
* Next, we will create a fresh Vue application instance. You may then begin
* registering components with the application instance so they are ready
* to use in your application's views. An example is included for you.
*/
const app = createApp({});
import ExampleComponent from './components/ExampleComponent.vue';
app.component('example-component', ExampleComponent);
/**
* The following block of code may be used to automatically register your
* Vue components. It will recursively scan this directory for the Vue
* components and automatically register them with their "basename".
*
* Eg. ./components/ExampleComponent.vue ->
*/
// Object.entries(import.meta.glob('./**/*.vue', { eager: true })).forEach(([path, definition]) => {
// app.component(path.split('/').pop().replace(/\.\w+$/, ''), definition.default);
// });
/**
* Finally, we will attach the application instance to a HTML element with
* an "id" attribute of "app". This element is included with the "auth"
* scaffolding. Otherwise, you will need to add an element yourself.
*/
app.mount('#app');
ui/src/Presets/Preset.php 0000644 00000003137 15242753746 0011373 0 ustar 00 isDirectory($directory = resource_path('js/components'))) {
$filesystem->makeDirectory($directory, 0755, true);
}
}
/**
* Update the "package.json" file.
*
* @param bool $dev
* @return void
*/
protected static function updatePackages($dev = true)
{
if (! file_exists(base_path('package.json'))) {
return;
}
$configurationKey = $dev ? 'devDependencies' : 'dependencies';
$packages = json_decode(file_get_contents(base_path('package.json')), true);
$packages[$configurationKey] = static::updatePackageArray(
array_key_exists($configurationKey, $packages) ? $packages[$configurationKey] : [],
$configurationKey
);
ksort($packages[$configurationKey]);
file_put_contents(
base_path('package.json'),
json_encode($packages, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT).PHP_EOL
);
}
/**
* Remove the installed Node modules.
*
* @return void
*/
protected static function removeNodeModules()
{
tap(new Filesystem, function ($files) {
$files->deleteDirectory(base_path('node_modules'));
$files->delete(base_path('yarn.lock'));
});
}
}
ui/src/Presets/Bootstrap.php 0000644 00000003143 15242753746 0012103 0 ustar 00 '^5.2.3',
'@popperjs/core' => '^2.11.6',
'sass' => '^1.56.1',
] + $packages;
}
/**
* Update the Vite configuration.
*
* @return void
*/
protected static function updateViteConfiguration()
{
copy(__DIR__.'/bootstrap-stubs/vite.config.js', base_path('vite.config.js'));
}
/**
* Update the Sass files for the application.
*
* @return void
*/
protected static function updateSass()
{
(new Filesystem)->ensureDirectoryExists(resource_path('sass'));
copy(__DIR__.'/bootstrap-stubs/_variables.scss', resource_path('sass/_variables.scss'));
copy(__DIR__.'/bootstrap-stubs/app.scss', resource_path('sass/app.scss'));
}
/**
* Update the bootstrapping files.
*
* @return void
*/
protected static function updateBootstrapping()
{
copy(__DIR__.'/bootstrap-stubs/bootstrap.js', resource_path('js/bootstrap.js'));
}
}
ui/src/Presets/Vue.php 0000644 00000003320 15242753746 0010662 0 ustar 00 '^4.5.0',
'vue' => '^3.2.37',
] + Arr::except($packages, [
'@vitejs/plugin-react',
'react',
'react-dom',
]);
}
/**
* Update the Vite configuration.
*
* @return void
*/
protected static function updateViteConfiguration()
{
copy(__DIR__.'/vue-stubs/vite.config.js', base_path('vite.config.js'));
}
/**
* Update the example component.
*
* @return void
*/
protected static function updateComponent()
{
(new Filesystem)->delete(
resource_path('js/components/Example.js')
);
copy(
__DIR__.'/vue-stubs/ExampleComponent.vue',
resource_path('js/components/ExampleComponent.vue')
);
}
/**
* Update the bootstrapping files.
*
* @return void
*/
protected static function updateBootstrapping()
{
copy(__DIR__.'/vue-stubs/app.js', resource_path('js/app.js'));
}
}
ui/src/Presets/React.php 0000644 00000005040 15242753746 0011162 0 ustar 00 '^4.2.0',
'react' => '^18.2.0',
'react-dom' => '^18.2.0',
] + Arr::except($packages, [
'@vitejs/plugin-vue',
'vue'
]);
}
/**
* Update the Vite configuration.
*
* @return void
*/
protected static function updateViteConfiguration()
{
copy(__DIR__.'/react-stubs/vite.config.js', base_path('vite.config.js'));
}
/**
* Update the example component.
*
* @return void
*/
protected static function updateComponent()
{
(new Filesystem)->delete(
resource_path('js/components/ExampleComponent.vue')
);
copy(
__DIR__.'/react-stubs/Example.jsx',
resource_path('js/components/Example.jsx')
);
}
/**
* Update the bootstrapping files.
*
* @return void
*/
protected static function updateBootstrapping()
{
copy(__DIR__.'/react-stubs/app.js', resource_path('js/app.js'));
}
/**
* Add Vite's React Refresh Runtime
*
* @return void
*/
protected static function addViteReactRefreshDirective()
{
$view = static::getViewPath('layouts/app.blade.php');
if (! file_exists($view)) {
return;
}
file_put_contents(
$view,
str_replace('@vite(', '@viteReactRefresh'.PHP_EOL.' @vite(', file_get_contents($view))
);
}
/**
* Get full view path relative to the application's configured view path.
*
* @param string $path
* @return string
*/
protected static function getViewPath($path)
{
return implode(DIRECTORY_SEPARATOR, [
config('view.paths')[0] ?? resource_path('views'), $path,
]);
}
}
ui/src/Auth/bootstrap-stubs/layouts/app.stub 0000644 00000006664 15242753746 0015236 0 ustar 00
{{ config('app.name', 'Laravel') }}
@vite(['resources/sass/app.scss', 'resources/js/app.js'])
@endsection
ui/src/Auth/stubs/controllers/Controller.stub 0000644 00000000464 15242753746 0015424 0 ustar 00 middleware('auth');
}
/**
* Show the application dashboard.
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public function index()
{
return view('home');
}
}
ui/src/Auth/stubs/routes.stub 0000644 00000000154 15242753746 0012250 0 ustar 00
Auth::routes();
Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');
ui/src/AuthCommand.php 0000644 00000012437 15242753746 0010707 0 ustar 00 'auth/login.blade.php',
'auth/passwords/confirm.stub' => 'auth/passwords/confirm.blade.php',
'auth/passwords/email.stub' => 'auth/passwords/email.blade.php',
'auth/passwords/reset.stub' => 'auth/passwords/reset.blade.php',
'auth/register.stub' => 'auth/register.blade.php',
'auth/verify.stub' => 'auth/verify.blade.php',
'home.stub' => 'home.blade.php',
'layouts/app.stub' => 'layouts/app.blade.php',
];
/**
* Execute the console command.
*
* @return void
*
* @throws \InvalidArgumentException
*/
public function handle()
{
if (static::hasMacro($this->argument('type'))) {
return call_user_func(static::$macros[$this->argument('type')], $this);
}
if (! in_array($this->argument('type'), ['bootstrap'])) {
throw new InvalidArgumentException('Invalid preset.');
}
$this->ensureDirectoriesExist();
$this->exportViews();
if (! $this->option('views')) {
$this->exportBackend();
}
$this->components->info('Authentication scaffolding generated successfully.');
}
/**
* Create the directories for the files.
*
* @return void
*/
protected function ensureDirectoriesExist()
{
if (! is_dir($directory = $this->getViewPath('layouts'))) {
mkdir($directory, 0755, true);
}
if (! is_dir($directory = $this->getViewPath('auth/passwords'))) {
mkdir($directory, 0755, true);
}
}
/**
* Export the authentication views.
*
* @return void
*/
protected function exportViews()
{
foreach ($this->views as $key => $value) {
if (file_exists($view = $this->getViewPath($value)) && ! $this->option('force')) {
if (! $this->components->confirm("The [$value] view already exists. Do you want to replace it?")) {
continue;
}
}
copy(
__DIR__.'/Auth/'.$this->argument('type').'-stubs/'.$key,
$view
);
}
}
/**
* Export the authentication backend.
*
* @return void
*/
protected function exportBackend()
{
$this->callSilent('ui:controllers');
$controller = app_path('Http/Controllers/HomeController.php');
if (file_exists($controller) && ! $this->option('force')) {
if ($this->components->confirm("The [HomeController.php] file already exists. Do you want to replace it?", true)) {
file_put_contents($controller, $this->compileStub('controllers/HomeController'));
}
} else {
file_put_contents($controller, $this->compileStub('controllers/HomeController'));
}
$baseController = app_path('Http/Controllers/Controller.php');
if (file_exists($baseController) && ! $this->option('force')) {
if ($this->components->confirm("The [Controller.php] file already exists. Do you want to replace it?", true)) {
file_put_contents($baseController, $this->compileStub('controllers/Controller'));
}
} else {
file_put_contents($baseController, $this->compileStub('controllers/Controller'));
}
if (! file_exists(database_path('migrations/0001_01_01_000000_create_users_table.php'))) {
copy(
__DIR__.'/../stubs/migrations/2014_10_12_100000_create_password_resets_table.php',
base_path('database/migrations/2014_10_12_100000_create_password_resets_table.php')
);
}
file_put_contents(
base_path('routes/web.php'),
file_get_contents(__DIR__.'/Auth/stubs/routes.stub'),
FILE_APPEND
);
}
/**
* Compiles the given stub.
*
* @param string $stub
* @return string
*/
protected function compileStub($stub)
{
return str_replace(
'{{namespace}}',
$this->laravel->getNamespace(),
file_get_contents(__DIR__.'/Auth/stubs/'.$stub.'.stub')
);
}
/**
* Get full view path relative to the application's configured view path.
*
* @param string $path
* @return string
*/
protected function getViewPath($path)
{
return implode(DIRECTORY_SEPARATOR, [
config('view.paths')[0] ?? resource_path('views'), $path,
]);
}
}
ui/src/UiCommand.php 0000644 00000004577 15242753746 0010371 0 ustar 00 argument('type'))) {
return call_user_func(static::$macros[$this->argument('type')], $this);
}
if (! in_array($this->argument('type'), ['bootstrap', 'vue', 'react'])) {
throw new InvalidArgumentException('Invalid preset.');
}
if ($this->option('auth')) {
$this->call('ui:auth');
}
$this->{$this->argument('type')}();
}
/**
* Install the "bootstrap" preset.
*
* @return void
*/
protected function bootstrap()
{
Presets\Bootstrap::install();
$this->components->info('Bootstrap scaffolding installed successfully.');
$this->components->warn('Please run [npm install && npm run dev] to compile your fresh scaffolding.');
}
/**
* Install the "vue" preset.
*
* @return void
*/
protected function vue()
{
Presets\Bootstrap::install();
Presets\Vue::install();
$this->components->info('Vue scaffolding installed successfully.');
$this->components->warn('Please run [npm install && npm run dev] to compile your fresh scaffolding.');
}
/**
* Install the "react" preset.
*
* @return void
*/
protected function react()
{
Presets\Bootstrap::install();
Presets\React::install();
$this->components->info('React scaffolding installed successfully.');
$this->components->warn('Please run [npm install && npm run dev] to compile your fresh scaffolding.');
}
}
ui/src/UiServiceProvider.php 0000644 00000001236 15242753746 0012113 0 ustar 00 app->runningInConsole()) {
$this->commands([
AuthCommand::class,
ControllersCommand::class,
UiCommand::class,
]);
}
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Route::mixin(new AuthRouteMethods);
}
}
ui/src/AuthRouteMethods.php 0000644 00000006657 15242753746 0011762 0 ustar 00 prependGroupNamespace('Auth\LoginController')) ? null : 'App\Http\Controllers';
$this->group(['namespace' => $namespace], function() use($options) {
// Login Routes...
if ($options['login'] ?? true) {
$this->get('login', 'Auth\LoginController@showLoginForm')->name('login');
$this->post('login', 'Auth\LoginController@login');
}
// Logout Routes...
if ($options['logout'] ?? true) {
$this->post('logout', 'Auth\LoginController@logout')->name('logout');
}
// Registration Routes...
if ($options['register'] ?? true) {
$this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
$this->post('register', 'Auth\RegisterController@register');
}
// Password Reset Routes...
if ($options['reset'] ?? true) {
$this->resetPassword();
}
// Password Confirmation Routes...
if ($options['confirm'] ??
class_exists($this->prependGroupNamespace('Auth\ConfirmPasswordController'))) {
$this->confirmPassword();
}
// Email Verification Routes...
if ($options['verify'] ?? false) {
$this->emailVerification();
}
});
};
}
/**
* Register the typical reset password routes for an application.
*
* @return callable
*/
public function resetPassword()
{
return function () {
$this->get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
$this->post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
$this->get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
$this->post('password/reset', 'Auth\ResetPasswordController@reset')->name('password.update');
};
}
/**
* Register the typical confirm password routes for an application.
*
* @return callable
*/
public function confirmPassword()
{
return function () {
$this->get('password/confirm', 'Auth\ConfirmPasswordController@showConfirmForm')->name('password.confirm');
$this->post('password/confirm', 'Auth\ConfirmPasswordController@confirm');
};
}
/**
* Register the typical email verification routes for an application.
*
* @return callable
*/
public function emailVerification()
{
return function () {
$this->get('email/verify', 'Auth\VerificationController@show')->name('verification.notice');
$this->get('email/verify/{id}/{hash}', 'Auth\VerificationController@verify')->name('verification.verify');
$this->post('email/resend', 'Auth\VerificationController@resend')->name('verification.resend');
};
}
}
ui/src/ControllersCommand.php 0000644 00000002462 15242753746 0012311 0 ustar 00 allFiles(__DIR__.'/../stubs/Auth'))
->each(function (SplFileInfo $file) use ($filesystem) {
$filesystem->copy(
$file->getPathname(),
app_path('Http/Controllers/Auth/'.Str::replaceLast('.stub', '.php', $file->getFilename()))
);
});
$this->components->info('Authentication scaffolding generated successfully.');
}
}
ui/README.md 0000644 00000022463 15242753746 0006466 0 ustar 00 # Laravel UI
## Introduction
While Laravel does not dictate which JavaScript or CSS pre-processors you use, it does provide a basic starting point using [Bootstrap](https://getbootstrap.com/), [React](https://reactjs.org/), and / or [Vue](https://vuejs.org/) that will be helpful for many applications. By default, Laravel uses [NPM](https://www.npmjs.org/) to install both of these frontend packages.
> This legacy package is a very simple authentication scaffolding built on the Bootstrap CSS framework. While it continues to work with the latest version of Laravel, you should consider using [Laravel Breeze](https://github.com/laravel/breeze) for new projects. Or, for something more robust, consider [Laravel Jetstream](https://github.com/laravel/jetstream).
## Official Documentation
### Supported Versions
Only the latest major version of Laravel UI receives bug fixes. The table below lists compatible Laravel versions:
| Version | Laravel Version |
|---- |----|
| [1.x](https://github.com/laravel/ui/tree/1.x) | 5.8, 6.x |
| [2.x](https://github.com/laravel/ui/tree/2.x) | 7.x |
| [3.x](https://github.com/laravel/ui/tree/3.x) | 8.x, 9.x |
| [4.x](https://github.com/laravel/ui/tree/4.x) | 9.x, 10.x, 11.x |
### Installation
The Bootstrap and Vue scaffolding provided by Laravel is located in the `laravel/ui` Composer package, which may be installed using Composer:
```bash
composer require laravel/ui
```
Once the `laravel/ui` package has been installed, you may install the frontend scaffolding using the `ui` Artisan command:
```bash
// Generate basic scaffolding...
php artisan ui bootstrap
php artisan ui vue
php artisan ui react
// Generate login / registration scaffolding...
php artisan ui bootstrap --auth
php artisan ui vue --auth
php artisan ui react --auth
```
#### CSS
Laravel officially supports [Vite](https://laravel.com/docs/vite), a modern frontend build tool that provides an extremely fast development environment and bundles your code for production. Vite supports a variety of CSS preprocessor languages, including SASS and Less, which are extensions of plain CSS that add variables, mixins, and other powerful features that make working with CSS much more enjoyable. In this document, we will briefly discuss CSS compilation in general; however, you should consult the full [Vite documentation](https://laravel.com/docs/vite#working-with-stylesheets) for more information on compiling SASS or Less.
#### JavaScript
Laravel does not require you to use a specific JavaScript framework or library to build your applications. In fact, you don't have to use JavaScript at all. However, Laravel does include some basic scaffolding to make it easier to get started writing modern JavaScript using the [Vue](https://vuejs.org) library. Vue provides an expressive API for building robust JavaScript applications using components. As with CSS, we may use Vite to easily compile JavaScript components into a single, browser-ready JavaScript file.
### Writing CSS
After installing the `laravel/ui` Composer package and [generating the frontend scaffolding](#introduction), Laravel's `package.json` file will include the `bootstrap` package to help you get started prototyping your application's frontend using Bootstrap. However, feel free to add or remove packages from the `package.json` file as needed for your own application. You are not required to use the Bootstrap framework to build your Laravel application - it is provided as a good starting point for those who choose to use it.
Before compiling your CSS, install your project's frontend dependencies using the [Node package manager (NPM)](https://www.npmjs.org):
```bash
npm install
```
Once the dependencies have been installed using `npm install`, you can compile your SASS files to plain CSS using [Vite](https://laravel.com/docs/vite#working-with-stylesheets). The `npm run dev` command will process the instructions in your `vite.config.js` file. Typically, your compiled CSS will be placed in the `public/build/assets` directory:
```bash
npm run dev
```
The `vite.config.js` file included with Laravel's frontend scaffolding will compile the `resources/sass/app.scss` SASS file. This `app.scss` file imports a file of SASS variables and loads Bootstrap, which provides a good starting point for most applications. Feel free to customize the `app.scss` file however you wish or even use an entirely different pre-processor by [configuring Vite](https://laravel.com/docs/vite#working-with-stylesheets).
### Writing JavaScript
All of the JavaScript dependencies required by your application can be found in the `package.json` file in the project's root directory. This file is similar to a `composer.json` file except it specifies JavaScript dependencies instead of PHP dependencies. You can install these dependencies using the [Node package manager (NPM)](https://www.npmjs.org):
```bash
npm install
```
> By default, the Laravel `package.json` file includes a few packages such as `lodash` and `axios` to help you get started building your JavaScript application. Feel free to add or remove from the `package.json` file as needed for your own application.
Once the packages are installed, you can use the `npm run dev` command to [compile your assets](https://laravel.com/docs/vite). Vite is a module bundler for modern JavaScript applications. When you run the `npm run dev` command, Vite will execute the instructions in your `vite.config.js` file:
```bash
npm run dev
```
By default, the Laravel `vite.config.js` file compiles your SASS and the `resources/js/app.js` file. Within the `app.js` file you may register your Vue components or, if you prefer a different framework, configure your own JavaScript application. Your compiled JavaScript will typically be placed in the `public/build/assets` directory.
> The `app.js` file will load the `resources/js/bootstrap.js` file which bootstraps and configures Vue, Axios, jQuery, and all other JavaScript dependencies. If you have additional JavaScript dependencies to configure, you may do so in this file.
#### Writing Vue Components
When using the `laravel/ui` package to scaffold your frontend, an `ExampleComponent.vue` Vue component will be placed in the `resources/js/components` directory. The `ExampleComponent.vue` file is an example of a [single file Vue component](https://vuejs.org/guide/single-file-components) which defines its JavaScript and HTML template in the same file. Single file components provide a very convenient approach to building JavaScript driven applications. The example component is registered in your `app.js` file:
```javascript
import ExampleComponent from './components/ExampleComponent.vue';
Vue.component('example-component', ExampleComponent);
```
To use the component in your application, you may drop it into one of your HTML templates. For example, after running the `php artisan ui vue --auth` Artisan command to scaffold your application's authentication and registration screens, you could drop the component into the `home.blade.php` Blade template:
```blade
@extends('layouts.app')
@section('content')
@endsection
```
> Remember, you should run the `npm run dev` command each time you change a Vue component. Or, you may run the `npm run watch` command to monitor and automatically recompile your components each time they are modified.
If you are interested in learning more about writing Vue components, you should read the [Vue documentation](https://vuejs.org/guide/), which provides a thorough, easy-to-read overview of the entire Vue framework.
#### Using React
If you prefer to use React to build your JavaScript application, Laravel makes it a cinch to swap the Vue scaffolding with React scaffolding:
```bash
composer require laravel/ui
// Generate basic scaffolding...
php artisan ui react
// Generate login / registration scaffolding...
php artisan ui react --auth
````
### Adding Presets
Presets are "macroable", which allows you to add additional methods to the `UiCommand` class at runtime. For example, the following code adds a `nextjs` method to the `UiCommand` class. Typically, you should declare preset macros in a [service provider](https://laravel.com/docs/providers):
```php
use Laravel\Ui\UiCommand;
UiCommand::macro('nextjs', function (UiCommand $command) {
// Scaffold your frontend...
});
```
Then, you may call the new preset via the `ui` command:
```bash
php artisan ui nextjs
```
## Contributing
Thank you for considering contributing to UI! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
## Code of Conduct
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
## Security Vulnerabilities
Please review [our security policy](https://github.com/laravel/ui/security/policy) on how to report security vulnerabilities.
## License
Laravel UI is open-sourced software licensed under the [MIT license](LICENSE.md).
ui/composer.json 0000644 00000002217 15242753746 0007724 0 ustar 00 {
"name": "laravel/ui",
"description": "Laravel UI utilities and presets.",
"keywords": ["laravel", "ui"],
"license": "MIT",
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"require": {
"php": "^8.0",
"illuminate/console": "^9.21|^10.0|^11.0",
"illuminate/filesystem": "^9.21|^10.0|^11.0",
"illuminate/support": "^9.21|^10.0|^11.0",
"illuminate/validation": "^9.21|^10.0|^11.0",
"symfony/console": "^6.0|^7.0"
},
"require-dev": {
"orchestra/testbench": "^7.35|^8.15|^9.0",
"phpunit/phpunit": "^9.3|^10.4|^11.0"
},
"autoload": {
"psr-4": {
"Laravel\\Ui\\": "src/",
"Illuminate\\Foundation\\Auth\\": "auth-backend/"
}
},
"config": {
"sort-packages": true
},
"extra": {
"branch-alias": {
"dev-master": "4.x-dev"
},
"laravel": {
"providers": [
"Laravel\\Ui\\UiServiceProvider"
]
}
},
"minimum-stability": "dev",
"prefer-stable": true
}
ui/LICENSE.md 0000644 00000002063 15242753746 0006605 0 ustar 00 The MIT License (MIT)
Copyright (c) Taylor Otwell
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.
sanctum/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php 0000644 00000001530 15242753746 0024167 0 ustar 00 id();
$table->morphs('tokenable');
$table->string('name');
$table->string('token', 64)->unique();
$table->text('abilities')->nullable();
$table->timestamp('last_used_at')->nullable();
$table->timestamp('expires_at')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('personal_access_tokens');
}
};
sanctum/src/Events/TokenAuthenticated.php 0000644 00000000672 15242753746 0014571 0 ustar 00 token = $token;
}
}
sanctum/src/Exceptions/MissingAbilityException.php 0000644 00000001502 15242753746 0016462 0 ustar 00 abilities = Arr::wrap($abilities);
}
/**
* Get the abilities that the user did not have.
*
* @return array
*/
public function abilities()
{
return $this->abilities;
}
}
sanctum/src/Exceptions/MissingScopeException.php 0000644 00000001571 15242753746 0016144 0 ustar 00 scopes = Arr::wrap($scopes);
}
/**
* Get the scopes that the user did not have.
*
* @return array
*/
public function scopes()
{
return $this->scopes;
}
}
sanctum/src/Http/Controllers/CsrfCookieController.php 0000644 00000001147 15242753746 0017060 0 ustar 00 expectsJson()) {
return new JsonResponse(status: 204);
}
return new Response(status: 204);
}
}
sanctum/src/Http/Middleware/CheckForAnyAbility.php 0000644 00000001654 15242753746 0016211 0 ustar 00 user() || ! $request->user()->currentAccessToken()) {
throw new AuthenticationException;
}
foreach ($abilities as $ability) {
if ($request->user()->tokenCan($ability)) {
return $next($request);
}
}
throw new MissingAbilityException($abilities);
}
}
sanctum/src/Http/Middleware/CheckScopes.php 0000644 00000001513 15242753746 0014723 0 ustar 00 handle($request, $next, ...$scopes);
} catch (\Laravel\Sanctum\Exceptions\MissingAbilityException $e) {
throw new MissingScopeException($e->abilities());
}
}
}
sanctum/src/Http/Middleware/CheckForAnyScope.php 0000644 00000001530 15242753746 0015656 0 ustar 00 handle($request, $next, ...$scopes);
} catch (\Laravel\Sanctum\Exceptions\MissingAbilityException $e) {
throw new MissingScopeException($e->abilities());
}
}
}
sanctum/src/Http/Middleware/EnsureFrontendRequestsAreStateful.php 0000644 00000005266 15242753746 0021377 0 ustar 00 configureSecureCookieSessions();
return (new Pipeline(app()))->send($request)->through(
static::fromFrontend($request) ? $this->frontendMiddleware() : []
)->then(function ($request) use ($next) {
return $next($request);
});
}
/**
* Configure secure cookie sessions.
*
* @return void
*/
protected function configureSecureCookieSessions()
{
config([
'session.http_only' => true,
'session.same_site' => 'lax',
]);
}
/**
* Get the middleware that should be applied to requests from the "frontend".
*
* @return array
*/
protected function frontendMiddleware()
{
$middleware = array_values(array_filter(array_unique([
config('sanctum.middleware.encrypt_cookies', \Illuminate\Cookie\Middleware\EncryptCookies::class),
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
config('sanctum.middleware.validate_csrf_token'),
config('sanctum.middleware.verify_csrf_token', \Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class),
config('sanctum.middleware.authenticate_session'),
])));
array_unshift($middleware, function ($request, $next) {
$request->attributes->set('sanctum', true);
return $next($request);
});
return $middleware;
}
/**
* Determine if the given request is from the first-party application frontend.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
public static function fromFrontend($request)
{
$domain = $request->headers->get('referer') ?: $request->headers->get('origin');
if (is_null($domain)) {
return false;
}
$domain = Str::replaceFirst('https://', '', $domain);
$domain = Str::replaceFirst('http://', '', $domain);
$domain = Str::endsWith($domain, '/') ? $domain : "{$domain}/";
$stateful = array_filter(config('sanctum.stateful', []));
return Str::is(Collection::make($stateful)->map(function ($uri) {
return trim($uri).'/*';
})->all(), $domain);
}
}
sanctum/src/Http/Middleware/AuthenticateSession.php 0000644 00000005151 15242753746 0016515 0 ustar 00 auth = $auth;
}
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
*
* @throws \Illuminate\Auth\AuthenticationException
*/
public function handle(Request $request, Closure $next): Response
{
if (! $request->hasSession() || ! $request->user()) {
return $next($request);
}
$guards = Collection::make(Arr::wrap(config('sanctum.guard')))
->mapWithKeys(fn ($guard) => [$guard => $this->auth->guard($guard)])
->filter(fn ($guard) => $guard instanceof SessionGuard);
$shouldLogout = $guards->filter(
fn ($guard, $driver) => $request->session()->has('password_hash_'.$driver)
)->filter(
fn ($guard, $driver) => $request->session()->get('password_hash_'.$driver) !==
$request->user()->getAuthPassword()
);
if ($shouldLogout->isNotEmpty()) {
$shouldLogout->each->logoutCurrentDevice();
$request->session()->flush();
throw new AuthenticationException('Unauthenticated.', [...$shouldLogout->keys()->all(), 'sanctum']);
}
return tap($next($request), function () use ($request, $guards) {
if (! is_null($request->user())) {
$this->storePasswordHashInSession($request, $guards->keys()->first());
}
});
}
/**
* Store the user's current password hash in the session.
*
* @param \Illuminate\Http\Request $request
* @param string $guard
* @return void
*/
protected function storePasswordHashInSession($request, string $guard)
{
if (! $request->user()) {
return;
}
$request->session()->put([
"password_hash_{$guard}" => $request->user()->getAuthPassword(),
]);
}
}
sanctum/src/Http/Middleware/CheckAbilities.php 0000644 00000001650 15242753746 0015376 0 ustar 00 user() || ! $request->user()->currentAccessToken()) {
throw new AuthenticationException;
}
foreach ($abilities as $ability) {
if (! $request->user()->tokenCan($ability)) {
throw new MissingAbilityException($ability);
}
}
return $next($request);
}
}
sanctum/src/Console/Commands/PruneExpired.php 0000644 00000002760 15242753746 0015317 0 ustar 00 option('hours');
$this->components->task(
'Pruning tokens with expired expires_at timestamps',
fn () => $model::where('expires_at', '<', now()->subHours($hours))->delete()
);
if ($expiration = config('sanctum.expiration')) {
$this->components->task(
'Pruning tokens with expired expiration value based on configuration file',
fn () => $model::where('created_at', '<', now()->subMinutes($expiration + ($hours * 60)))->delete()
);
} else {
$this->components->warn('Expiration value not specified in configuration file.');
}
$this->components->info("Tokens expired for more than [$hours hours] pruned successfully.");
return 0;
}
}
sanctum/src/Contracts/HasAbilities.php 0000644 00000000623 15242753746 0014037 0 ustar 00 'json',
'last_used_at' => 'datetime',
'expires_at' => 'datetime',
];
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
'token',
'abilities',
'expires_at',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array
*/
protected $hidden = [
'token',
];
/**
* Get the tokenable model that the access token belongs to.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphTo
*/
public function tokenable()
{
return $this->morphTo('tokenable');
}
/**
* Find the token instance matching the given token.
*
* @param string $token
* @return static|null
*/
public static function findToken($token)
{
if (strpos($token, '|') === false) {
return static::where('token', hash('sha256', $token))->first();
}
[$id, $token] = explode('|', $token, 2);
if ($instance = static::find($id)) {
return hash_equals($instance->token, hash('sha256', $token)) ? $instance : null;
}
}
/**
* Determine if the token has a given ability.
*
* @param string $ability
* @return bool
*/
public function can($ability)
{
return in_array('*', $this->abilities) ||
array_key_exists($ability, array_flip($this->abilities));
}
/**
* Determine if the token is missing a given ability.
*
* @param string $ability
* @return bool
*/
public function cant($ability)
{
return ! $this->can($ability);
}
}
sanctum/src/Sanctum.php 0000644 00000007166 15242753746 0011161 0 ustar 00 shouldIgnoreMissing(false);
if (in_array('*', $abilities)) {
$token->shouldReceive('can')->withAnyArgs()->andReturn(true);
} else {
foreach ($abilities as $ability) {
$token->shouldReceive('can')->with($ability)->andReturn(true);
}
}
$user->withAccessToken($token);
if (isset($user->wasRecentlyCreated) && $user->wasRecentlyCreated) {
$user->wasRecentlyCreated = false;
}
app('auth')->guard($guard)->setUser($user);
app('auth')->shouldUse($guard);
return $user;
}
/**
* Set the personal access token model name.
*
* @param string $model
* @return void
*/
public static function usePersonalAccessTokenModel($model)
{
static::$personalAccessTokenModel = $model;
}
/**
* Specify a callback that should be used to fetch the access token from the request.
*
* @param callable|null $callback
* @return void
*/
public static function getAccessTokenFromRequestUsing(?callable $callback)
{
static::$accessTokenRetrievalCallback = $callback;
}
/**
* Specify a callback that should be used to authenticate access tokens.
*
* @param callable $callback
* @return void
*/
public static function authenticateAccessTokensUsing(callable $callback)
{
static::$accessTokenAuthenticationCallback = $callback;
}
/**
* Determine if Sanctum's migrations should be run.
*
* @return bool
*/
public static function shouldRunMigrations()
{
return static::$runsMigrations;
}
/**
* Configure Sanctum to not register its migrations.
*
* @return static
*/
public static function ignoreMigrations()
{
static::$runsMigrations = false;
return new static;
}
/**
* Get the token model class name.
*
* @return string
*/
public static function personalAccessTokenModel()
{
return static::$personalAccessTokenModel;
}
}
sanctum/src/HasApiTokens.php 0000644 00000004512 15242753746 0012070 0 ustar 00 morphMany(Sanctum::$personalAccessTokenModel, 'tokenable');
}
/**
* Determine if the current API token has a given scope.
*
* @param string $ability
* @return bool
*/
public function tokenCan(string $ability)
{
return $this->accessToken && $this->accessToken->can($ability);
}
/**
* Create a new personal access token for the user.
*
* @param string $name
* @param array $abilities
* @param \DateTimeInterface|null $expiresAt
* @return \Laravel\Sanctum\NewAccessToken
*/
public function createToken(string $name, array $abilities = ['*'], DateTimeInterface $expiresAt = null)
{
$plainTextToken = $this->generateTokenString();
$token = $this->tokens()->create([
'name' => $name,
'token' => hash('sha256', $plainTextToken),
'abilities' => $abilities,
'expires_at' => $expiresAt,
]);
return new NewAccessToken($token, $token->getKey().'|'.$plainTextToken);
}
/**
* Generate the token string.
*
* @return string
*/
public function generateTokenString()
{
return sprintf(
'%s%s%s',
config('sanctum.token_prefix', ''),
$tokenEntropy = Str::random(40),
hash('crc32b', $tokenEntropy)
);
}
/**
* Get the access token currently associated with the user.
*
* @return \Laravel\Sanctum\Contracts\HasAbilities
*/
public function currentAccessToken()
{
return $this->accessToken;
}
/**
* Set the current access token for the user.
*
* @param \Laravel\Sanctum\Contracts\HasAbilities $accessToken
* @return $this
*/
public function withAccessToken($accessToken)
{
$this->accessToken = $accessToken;
return $this;
}
}
sanctum/src/TransientToken.php 0000644 00000001015 15242753746 0012502 0 ustar 00 array_merge([
'driver' => 'sanctum',
'provider' => null,
], config('auth.guards.sanctum', [])),
]);
if (! app()->configurationIsCached()) {
$this->mergeConfigFrom(__DIR__.'/../config/sanctum.php', 'sanctum');
}
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
if (app()->runningInConsole()) {
$this->registerMigrations();
$this->publishes([
__DIR__.'/../database/migrations' => database_path('migrations'),
], 'sanctum-migrations');
$this->publishes([
__DIR__.'/../config/sanctum.php' => config_path('sanctum.php'),
], 'sanctum-config');
$this->commands([
PruneExpired::class,
]);
}
$this->defineRoutes();
$this->configureGuard();
$this->configureMiddleware();
}
/**
* Register Sanctum's migration files.
*
* @return void
*/
protected function registerMigrations()
{
if (Sanctum::shouldRunMigrations()) {
return $this->loadMigrationsFrom(__DIR__.'/../database/migrations');
}
}
/**
* Define the Sanctum routes.
*
* @return void
*/
protected function defineRoutes()
{
if (app()->routesAreCached() || config('sanctum.routes') === false) {
return;
}
Route::group(['prefix' => config('sanctum.prefix', 'sanctum')], function () {
Route::get(
'/csrf-cookie',
CsrfCookieController::class.'@show'
)->middleware('web')->name('sanctum.csrf-cookie');
});
}
/**
* Configure the Sanctum authentication guard.
*
* @return void
*/
protected function configureGuard()
{
Auth::resolved(function ($auth) {
$auth->extend('sanctum', function ($app, $name, array $config) use ($auth) {
return tap($this->createGuard($auth, $config), function ($guard) {
app()->refresh('request', $guard, 'setRequest');
});
});
});
}
/**
* Register the guard.
*
* @param \Illuminate\Contracts\Auth\Factory $auth
* @param array $config
* @return RequestGuard
*/
protected function createGuard($auth, $config)
{
return new RequestGuard(
new Guard($auth, config('sanctum.expiration'), $config['provider']),
request(),
$auth->createUserProvider($config['provider'] ?? null)
);
}
/**
* Configure the Sanctum middleware and priority.
*
* @return void
*/
protected function configureMiddleware()
{
$kernel = app()->make(Kernel::class);
$kernel->prependToMiddlewarePriority(EnsureFrontendRequestsAreStateful::class);
}
}
sanctum/src/Guard.php 0000644 00000012216 15242753746 0010601 0 ustar 00 auth = $auth;
$this->expiration = $expiration;
$this->provider = $provider;
}
/**
* Retrieve the authenticated user for the incoming request.
*
* @param \Illuminate\Http\Request $request
* @return mixed
*/
public function __invoke(Request $request)
{
foreach (Arr::wrap(config('sanctum.guard', 'web')) as $guard) {
if ($user = $this->auth->guard($guard)->user()) {
return $this->supportsTokens($user)
? $user->withAccessToken(new TransientToken)
: $user;
}
}
if ($token = $this->getTokenFromRequest($request)) {
$model = Sanctum::$personalAccessTokenModel;
$accessToken = $model::findToken($token);
if (! $this->isValidAccessToken($accessToken) ||
! $this->supportsTokens($accessToken->tokenable)) {
return;
}
$tokenable = $accessToken->tokenable->withAccessToken(
$accessToken
);
event(new TokenAuthenticated($accessToken));
if (method_exists($accessToken->getConnection(), 'hasModifiedRecords') &&
method_exists($accessToken->getConnection(), 'setRecordModificationState')) {
tap($accessToken->getConnection()->hasModifiedRecords(), function ($hasModifiedRecords) use ($accessToken) {
$accessToken->forceFill(['last_used_at' => now()])->save();
$accessToken->getConnection()->setRecordModificationState($hasModifiedRecords);
});
} else {
$accessToken->forceFill(['last_used_at' => now()])->save();
}
return $tokenable;
}
}
/**
* Determine if the tokenable model supports API tokens.
*
* @param mixed $tokenable
* @return bool
*/
protected function supportsTokens($tokenable = null)
{
return $tokenable && in_array(HasApiTokens::class, class_uses_recursive(
get_class($tokenable)
));
}
/**
* Get the token from the request.
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
protected function getTokenFromRequest(Request $request)
{
if (is_callable(Sanctum::$accessTokenRetrievalCallback)) {
return (string) (Sanctum::$accessTokenRetrievalCallback)($request);
}
$token = $request->bearerToken();
return $this->isValidBearerToken($token) ? $token : null;
}
/**
* Determine if the bearer token is in the correct format.
*
* @param string|null $token
* @return bool
*/
protected function isValidBearerToken(string $token = null)
{
if (! is_null($token) && str_contains($token, '|')) {
$model = new Sanctum::$personalAccessTokenModel;
if ($model->getKeyType() === 'int') {
[$id, $token] = explode('|', $token, 2);
return ctype_digit($id) && ! empty($token);
}
}
return ! empty($token);
}
/**
* Determine if the provided access token is valid.
*
* @param mixed $accessToken
* @return bool
*/
protected function isValidAccessToken($accessToken): bool
{
if (! $accessToken) {
return false;
}
$isValid =
(! $this->expiration || $accessToken->created_at->gt(now()->subMinutes($this->expiration)))
&& (! $accessToken->expires_at || ! $accessToken->expires_at->isPast())
&& $this->hasValidProvider($accessToken->tokenable);
if (is_callable(Sanctum::$accessTokenAuthenticationCallback)) {
$isValid = (bool) (Sanctum::$accessTokenAuthenticationCallback)($accessToken, $isValid);
}
return $isValid;
}
/**
* Determine if the tokenable model matches the provider's model type.
*
* @param \Illuminate\Database\Eloquent\Model $tokenable
* @return bool
*/
protected function hasValidProvider($tokenable)
{
if (is_null($this->provider)) {
return true;
}
$model = config("auth.providers.{$this->provider}.model");
return $tokenable instanceof $model;
}
}
sanctum/src/NewAccessToken.php 0000644 00000002427 15242753746 0012416 0 ustar 00 accessToken = $accessToken;
$this->plainTextToken = $plainTextToken;
}
/**
* Get the instance as an array.
*
* @return array
*/
public function toArray()
{
return [
'accessToken' => $this->accessToken,
'plainTextToken' => $this->plainTextToken,
];
}
/**
* Convert the object to its JSON representation.
*
* @param int $options
* @return string
*/
public function toJson($options = 0)
{
return json_encode($this->toArray(), $options);
}
}
sanctum/config/sanctum.php 0000644 00000005623 15242753746 0011673 0 ustar 00 explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
'%s%s',
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
Sanctum::currentApplicationUrlWithPort()
))),
/*
|--------------------------------------------------------------------------
| Sanctum Guards
|--------------------------------------------------------------------------
|
| This array contains the authentication guards that will be checked when
| Sanctum is trying to authenticate a request. If none of these guards
| are able to authenticate the request, Sanctum will use the bearer
| token that's present on an incoming request for authentication.
|
*/
'guard' => ['web'],
/*
|--------------------------------------------------------------------------
| Expiration Minutes
|--------------------------------------------------------------------------
|
| This value controls the number of minutes until an issued token will be
| considered expired. This will override any values set in the token's
| "expires_at" attribute, but first-party sessions are not affected.
|
*/
'expiration' => null,
/*
|--------------------------------------------------------------------------
| Token Prefix
|--------------------------------------------------------------------------
|
| Sanctum can prefix new tokens in order to take advantage of numerous
| security scanning initiatives maintained by open source platforms
| that notify developers if they commit tokens into repositories.
|
| See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning
|
*/
'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''),
/*
|--------------------------------------------------------------------------
| Sanctum Middleware
|--------------------------------------------------------------------------
|
| When authenticating your first-party SPA with Sanctum you may need to
| customize some of the middleware Sanctum uses while processing the
| request. You may change the middleware listed below as required.
|
*/
'middleware' => [
'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
],
];
sanctum/UPGRADE.md 0000644 00000001175 15242753746 0007652 0 ustar 00 # Upgrade Guide
## Upgrading To 3.0 From 2.x
### Minimum Versions
The following dependency versions have been updated:
- The minimum PHP version is now v8.0.2
- The minimum Laravel version is now v9.21
### New `expires_at` Column
Sanctum now supports expiring tokens. To support this feature, a new `expires_at` column must be added to your application's `personal_access_tokens` table. To add the column to your table, create a migration with the following schema change:
```php
Schema::table('personal_access_tokens', function (Blueprint $table) {
$table->timestamp('expires_at')->nullable()->after('last_used_at');
});
```
sanctum/README.md 0000644 00000003020 15242753746 0007507 0 ustar 00
## Introduction
Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.
## Official Documentation
Documentation for Sanctum can be found on the [Laravel website](https://laravel.com/docs/sanctum).
## Contributing
Thank you for considering contributing to Sanctum! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
## Code of Conduct
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
## Security Vulnerabilities
Please review [our security policy](https://github.com/laravel/sanctum/security/policy) on how to report security vulnerabilities.
## License
Laravel Sanctum is open-sourced software licensed under the [MIT license](LICENSE.md).
sanctum/composer.json 0000644 00000003066 15242753746 0010764 0 ustar 00 {
"name": "laravel/sanctum",
"description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.",
"keywords": ["laravel", "sanctum", "auth"],
"license": "MIT",
"support": {
"issues": "https://github.com/laravel/sanctum/issues",
"source": "https://github.com/laravel/sanctum"
},
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"require": {
"php": "^8.0.2",
"ext-json": "*",
"illuminate/console": "^9.21|^10.0",
"illuminate/contracts": "^9.21|^10.0",
"illuminate/database": "^9.21|^10.0",
"illuminate/support": "^9.21|^10.0"
},
"require-dev": {
"mockery/mockery": "^1.0",
"orchestra/testbench": "^7.28.2|^8.8.3",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.6"
},
"autoload": {
"psr-4": {
"Laravel\\Sanctum\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Laravel\\Sanctum\\Tests\\": "tests/",
"Workbench\\App\\": "workbench/app/",
"Workbench\\Database\\Factories\\": "workbench/database/factories/"
}
},
"extra": {
"branch-alias": {
"dev-master": "3.x-dev"
},
"laravel": {
"providers": [
"Laravel\\Sanctum\\SanctumServiceProvider"
]
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true
}
sanctum/LICENSE.md 0000644 00000002063 15242753746 0007642 0 ustar 00 The MIT License (MIT)
Copyright (c) Taylor Otwell
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.
sanctum/testbench.yaml 0000644 00000000066 15242753746 0011102 0 ustar 00 providers:
- Laravel\Sanctum\SanctumServiceProvider
socialite/src/One/TwitterProvider.php 0000644 00000002466 15242753746 0013745 0 ustar 00 hasNecessaryVerifier()) {
throw new MissingVerifierException('Invalid request. Missing OAuth verifier.');
}
$user = $this->server->getUserDetails($token = $this->getToken(), $this->shouldBypassCache($token->getIdentifier(), $token->getSecret()));
$extraDetails = [
'location' => $user->location,
'description' => $user->description,
];
$instance = (new User)->setRaw(array_merge($user->extra, $user->urls, $extraDetails))
->setToken($token->getIdentifier(), $token->getSecret());
return $instance->map([
'id' => $user->uid,
'nickname' => $user->nickname,
'name' => $user->name,
'email' => $user->email,
'avatar' => $user->imageUrl,
'avatar_original' => str_replace('_normal', '', $user->imageUrl),
]);
}
/**
* Set the access level the application should request to the user account.
*
* @param string $scope
* @return void
*/
public function scope(string $scope)
{
$this->server->setApplicationScope($scope);
}
}
socialite/src/One/MissingTemporaryCredentialsException.php 0000644 00000000237 15242753746 0020133 0 ustar 00 token = $token;
$this->tokenSecret = $tokenSecret;
return $this;
}
}
socialite/src/One/AbstractProvider.php 0000644 00000011345 15242753746 0014042 0 ustar 00 server = $server;
$this->request = $request;
}
/**
* Redirect the user to the authentication page for the provider.
*
* @return \Illuminate\Http\RedirectResponse
*/
public function redirect()
{
$this->request->session()->put(
'oauth.temp', $temp = $this->server->getTemporaryCredentials()
);
return new RedirectResponse($this->server->getAuthorizationUrl($temp));
}
/**
* Get the User instance for the authenticated user.
*
* @return \Laravel\Socialite\One\User
*
* @throws \Laravel\Socialite\One\MissingVerifierException
*/
public function user()
{
if (! $this->hasNecessaryVerifier()) {
throw new MissingVerifierException('Invalid request. Missing OAuth verifier.');
}
$token = $this->getToken();
$user = $this->server->getUserDetails(
$token, $this->shouldBypassCache($token->getIdentifier(), $token->getSecret())
);
$instance = (new User)->setRaw($user->extra)
->setToken($token->getIdentifier(), $token->getSecret());
return $instance->map([
'id' => $user->uid,
'nickname' => $user->nickname,
'name' => $user->name,
'email' => $user->email,
'avatar' => $user->imageUrl,
]);
}
/**
* Get a Social User instance from a known access token and secret.
*
* @param string $token
* @param string $secret
* @return \Laravel\Socialite\One\User
*/
public function userFromTokenAndSecret($token, $secret)
{
$tokenCredentials = new TokenCredentials();
$tokenCredentials->setIdentifier($token);
$tokenCredentials->setSecret($secret);
$user = $this->server->getUserDetails(
$tokenCredentials, $this->shouldBypassCache($token, $secret)
);
$instance = (new User)->setRaw($user->extra)
->setToken($tokenCredentials->getIdentifier(), $tokenCredentials->getSecret());
return $instance->map([
'id' => $user->uid,
'nickname' => $user->nickname,
'name' => $user->name,
'email' => $user->email,
'avatar' => $user->imageUrl,
]);
}
/**
* Get the token credentials for the request.
*
* @return \League\OAuth1\Client\Credentials\TokenCredentials
*/
protected function getToken()
{
$temp = $this->request->session()->get('oauth.temp');
if (! $temp) {
throw new MissingTemporaryCredentialsException('Missing temporary OAuth credentials.');
}
return $this->server->getTokenCredentials(
$temp, $this->request->get('oauth_token'), $this->request->get('oauth_verifier')
);
}
/**
* Determine if the request has the necessary OAuth verifier.
*
* @return bool
*/
protected function hasNecessaryVerifier()
{
return $this->request->has(['oauth_token', 'oauth_verifier']);
}
/**
* Determine if the user information cache should be bypassed.
*
* @param string $token
* @param string $secret
* @return bool
*/
protected function shouldBypassCache($token, $secret)
{
$newHash = sha1($token.'_'.$secret);
if (! empty($this->userHash) && $newHash !== $this->userHash) {
$this->userHash = $newHash;
return true;
}
$this->userHash = $this->userHash ?: $newHash;
return false;
}
/**
* Set the request instance.
*
* @param \Illuminate\Http\Request $request
* @return $this
*/
public function setRequest(Request $request)
{
$this->request = $request;
return $this;
}
}
socialite/src/Two/TwitterProvider.php 0000644 00000006041 15242753746 0013766 0 ustar 00 buildAuthUrlFromBase('https://twitter.com/i/oauth2/authorize', $state);
}
/**
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return 'https://api.twitter.com/2/oauth2/token';
}
/**
* {@inheritdoc}
*/
protected function getUserByToken($token)
{
$response = $this->getHttpClient()->get('https://api.twitter.com/2/users/me', [
RequestOptions::HEADERS => ['Authorization' => 'Bearer '.$token],
RequestOptions::QUERY => ['user.fields' => 'profile_image_url'],
]);
return Arr::get(json_decode($response->getBody(), true), 'data');
}
/**
* {@inheritdoc}
*/
protected function mapUserToObject(array $user)
{
return (new User)->setRaw($user)->map([
'id' => $user['id'],
'nickname' => $user['username'],
'name' => $user['name'],
'avatar' => $user['profile_image_url'],
]);
}
/**
* {@inheritdoc}
*/
public function getAccessTokenResponse($code)
{
$response = $this->getHttpClient()->post($this->getTokenUrl(), [
RequestOptions::HEADERS => ['Accept' => 'application/json'],
RequestOptions::AUTH => [$this->clientId, $this->clientSecret],
RequestOptions::FORM_PARAMS => $this->getTokenFields($code),
]);
return json_decode($response->getBody(), true);
}
/**
* {@inheritdoc}
*/
protected function getRefreshTokenResponse($refreshToken)
{
$response = $this->getHttpClient()->post($this->getTokenUrl(), [
RequestOptions::HEADERS => ['Accept' => 'application/json'],
RequestOptions::AUTH => [$this->clientId, $this->clientSecret],
RequestOptions::FORM_PARAMS => [
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken,
'client_id' => $this->clientId,
],
]);
return json_decode($response->getBody(), true);
}
/**
* {@inheritdoc}
*/
protected function getCodeFields($state = null)
{
$fields = parent::getCodeFields($state);
if ($this->isStateless()) {
$fields['state'] = 'state';
}
return $fields;
}
}
socialite/src/Two/GitlabProvider.php 0000644 00000003353 15242753746 0013531 0 ustar 00 host = rtrim($host, '/');
}
return $this;
}
/**
* {@inheritdoc}
*/
protected function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase($this->host.'/oauth/authorize', $state);
}
/**
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return $this->host.'/oauth/token';
}
/**
* {@inheritdoc}
*/
protected function getUserByToken($token)
{
$response = $this->getHttpClient()->get($this->host.'/api/v3/user', [
RequestOptions::QUERY => ['access_token' => $token],
]);
return json_decode($response->getBody(), true);
}
/**
* {@inheritdoc}
*/
protected function mapUserToObject(array $user)
{
return (new User)->setRaw($user)->map([
'id' => $user['id'],
'nickname' => $user['username'],
'name' => $user['name'],
'email' => $user['email'],
'avatar' => $user['avatar_url'],
]);
}
}
socialite/src/Two/XProvider.php 0000644 00000001557 15242753746 0012542 0 ustar 00 buildAuthUrlFromBase('https://x.com/i/oauth2/authorize', $state);
}
/**
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return 'https://api.x.com/2/oauth2/token';
}
/**
* {@inheritdoc}
*/
protected function getUserByToken($token)
{
$response = $this->getHttpClient()->get('https://api.x.com/2/users/me', [
RequestOptions::HEADERS => ['Authorization' => 'Bearer '.$token],
RequestOptions::QUERY => ['user.fields' => 'profile_image_url'],
]);
return Arr::get(json_decode($response->getBody(), true), 'data');
}
}
socialite/src/Two/BitbucketProvider.php 0000644 00000005537 15242753746 0014251 0 ustar 00 buildAuthUrlFromBase('https://bitbucket.org/site/oauth2/authorize', $state);
}
/**
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return 'https://bitbucket.org/site/oauth2/access_token';
}
/**
* {@inheritdoc}
*/
protected function getUserByToken($token)
{
$response = $this->getHttpClient()->get('https://api.bitbucket.org/2.0/user', [
RequestOptions::QUERY => ['access_token' => $token],
]);
$user = json_decode($response->getBody(), true);
if (in_array('email', $this->scopes, true)) {
$user['email'] = $this->getEmailByToken($token);
}
return $user;
}
/**
* Get the email for the given access token.
*
* @param string $token
* @return string|null
*/
protected function getEmailByToken($token)
{
$emailsUrl = 'https://api.bitbucket.org/2.0/user/emails?access_token='.$token;
try {
$response = $this->getHttpClient()->get($emailsUrl);
} catch (Exception $e) {
return;
}
$emails = json_decode($response->getBody(), true);
foreach ($emails['values'] as $email) {
if ($email['type'] === 'email' && $email['is_primary'] && $email['is_confirmed']) {
return $email['email'];
}
}
}
/**
* {@inheritdoc}
*/
protected function mapUserToObject(array $user)
{
return (new User)->setRaw($user)->map([
'id' => $user['uuid'],
'nickname' => $user['username'],
'name' => Arr::get($user, 'display_name'),
'email' => Arr::get($user, 'email'),
'avatar' => Arr::get($user, 'links.avatar.href'),
]);
}
/**
* Get the access token for the given code.
*
* @param string $code
* @return string
*/
public function getAccessToken($code)
{
$response = $this->getHttpClient()->post($this->getTokenUrl(), [
RequestOptions::AUTH => [$this->clientId, $this->clientSecret],
RequestOptions::HEADERS => ['Accept' => 'application/json'],
RequestOptions::FORM_PARAMS => $this->getTokenFields($code),
]);
return json_decode($response->getBody(), true)['access_token'];
}
}
socialite/src/Two/LinkedInProvider.php 0000644 00000010225 15242753746 0014020 0 ustar 00 buildAuthUrlFromBase('https://www.linkedin.com/oauth/v2/authorization', $state);
}
/**
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return 'https://www.linkedin.com/oauth/v2/accessToken';
}
/**
* {@inheritdoc}
*/
protected function getUserByToken($token)
{
$basicProfile = $this->getBasicProfile($token);
$emailAddress = $this->getEmailAddress($token);
return array_merge($basicProfile, $emailAddress);
}
/**
* Get the basic profile fields for the user.
*
* @param string $token
* @return array
*/
protected function getBasicProfile($token)
{
$fields = ['id', 'firstName', 'lastName', 'profilePicture(displayImage~:playableStreams)'];
if (in_array('r_liteprofile', $this->getScopes())) {
array_push($fields, 'vanityName');
}
$response = $this->getHttpClient()->get('https://api.linkedin.com/v2/me', [
RequestOptions::HEADERS => [
'Authorization' => 'Bearer '.$token,
'X-RestLi-Protocol-Version' => '2.0.0',
],
RequestOptions::QUERY => [
'projection' => '('.implode(',', $fields).')',
],
]);
return (array) json_decode($response->getBody(), true);
}
/**
* Get the email address for the user.
*
* @param string $token
* @return array
*/
protected function getEmailAddress($token)
{
$response = $this->getHttpClient()->get('https://api.linkedin.com/v2/emailAddress', [
RequestOptions::HEADERS => [
'Authorization' => 'Bearer '.$token,
'X-RestLi-Protocol-Version' => '2.0.0',
],
RequestOptions::QUERY => [
'q' => 'members',
'projection' => '(elements*(handle~))',
],
]);
return (array) Arr::get((array) json_decode($response->getBody(), true), 'elements.0.handle~');
}
/**
* {@inheritdoc}
*/
protected function mapUserToObject(array $user)
{
$preferredLocale = Arr::get($user, 'firstName.preferredLocale.language').'_'.Arr::get($user, 'firstName.preferredLocale.country');
$firstName = Arr::get($user, 'firstName.localized.'.$preferredLocale);
$lastName = Arr::get($user, 'lastName.localized.'.$preferredLocale);
$images = (array) Arr::get($user, 'profilePicture.displayImage~.elements', []);
$avatar = Arr::first($images, function ($image) {
return (
$image['data']['com.linkedin.digitalmedia.mediaartifact.StillImage']['storageSize']['width'] ??
$image['data']['com.linkedin.digitalmedia.mediaartifact.StillImage']['displaySize']['width']
) === 100;
});
$originalAvatar = Arr::first($images, function ($image) {
return (
$image['data']['com.linkedin.digitalmedia.mediaartifact.StillImage']['storageSize']['width'] ??
$image['data']['com.linkedin.digitalmedia.mediaartifact.StillImage']['displaySize']['width']
) === 800;
});
return (new User)->setRaw($user)->map([
'id' => $user['id'],
'nickname' => null,
'name' => $firstName.' '.$lastName,
'first_name' => $firstName,
'last_name' => $lastName,
'email' => Arr::get($user, 'emailAddress'),
'avatar' => Arr::get($avatar, 'identifiers.0.identifier'),
'avatar_original' => Arr::get($originalAvatar, 'identifiers.0.identifier'),
]);
}
}
socialite/src/Two/GithubProvider.php 0000644 00000005021 15242753746 0013543 0 ustar 00 buildAuthUrlFromBase('https://github.com/login/oauth/authorize', $state);
}
/**
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return 'https://github.com/login/oauth/access_token';
}
/**
* {@inheritdoc}
*/
protected function getUserByToken($token)
{
$userUrl = 'https://api.github.com/user';
$response = $this->getHttpClient()->get(
$userUrl, $this->getRequestOptions($token)
);
$user = json_decode($response->getBody(), true);
if (in_array('user:email', $this->scopes, true)) {
$user['email'] = $this->getEmailByToken($token);
}
return $user;
}
/**
* Get the email for the given access token.
*
* @param string $token
* @return string|null
*/
protected function getEmailByToken($token)
{
$emailsUrl = 'https://api.github.com/user/emails';
try {
$response = $this->getHttpClient()->get(
$emailsUrl, $this->getRequestOptions($token)
);
} catch (Exception $e) {
return;
}
foreach (json_decode($response->getBody(), true) as $email) {
if ($email['primary'] && $email['verified']) {
return $email['email'];
}
}
}
/**
* {@inheritdoc}
*/
protected function mapUserToObject(array $user)
{
return (new User)->setRaw($user)->map([
'id' => $user['id'],
'nodeId' => $user['node_id'],
'nickname' => $user['login'],
'name' => Arr::get($user, 'name'),
'email' => Arr::get($user, 'email'),
'avatar' => $user['avatar_url'],
]);
}
/**
* Get the default options for an HTTP request.
*
* @param string $token
* @return array
*/
protected function getRequestOptions($token)
{
return [
RequestOptions::HEADERS => [
'Accept' => 'application/vnd.github.v3+json',
'Authorization' => 'token '.$token,
],
];
}
}
socialite/src/Two/ProviderInterface.php 0000644 00000000640 15242753746 0014223 0 ustar 00 buildAuthUrlFromBase('https://www.linkedin.com/oauth/v2/authorization', $state);
}
/**
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return 'https://www.linkedin.com/oauth/v2/accessToken';
}
/**
* {@inheritdoc}
*/
protected function getUserByToken($token)
{
return $this->getBasicProfile($token);
}
/**
* Get the basic profile fields for the user.
*
* @param string $token
* @return array
*/
protected function getBasicProfile($token)
{
$response = $this->getHttpClient()->get('https://api.linkedin.com/v2/userinfo', [
RequestOptions::HEADERS => [
'Authorization' => 'Bearer '.$token,
'X-RestLi-Protocol-Version' => '2.0.0',
],
RequestOptions::QUERY => [
'projection' => '(sub,email,name,given_name,family_name,picture)',
],
]);
return (array) json_decode($response->getBody(), true);
}
/**
* {@inheritdoc}
*/
protected function mapUserToObject(array $user)
{
return (new User)->setRaw($user)->map([
'id' => $user['sub'],
'nickname' => null,
'name' => $user['name'],
'first_name' => $user['given_name'],
'last_name' => $user['family_name'],
'email' => $user['email'] ?? null,
'avatar' => $user['picture'] ?? null,
'avatar_original' => $user['picture'] ?? null,
]);
}
}
socialite/src/Two/User.php 0000644 00000003232 15242753746 0011526 0 ustar 00 token = $token;
return $this;
}
/**
* Set the refresh token required to obtain a new access token.
*
* @param string $refreshToken
* @return $this
*/
public function setRefreshToken($refreshToken)
{
$this->refreshToken = $refreshToken;
return $this;
}
/**
* Set the number of seconds the access token is valid for.
*
* @param int $expiresIn
* @return $this
*/
public function setExpiresIn($expiresIn)
{
$this->expiresIn = $expiresIn;
return $this;
}
/**
* Set the scopes that were approved by the user during authentication.
*
* @param array $approvedScopes
* @return $this
*/
public function setApprovedScopes($approvedScopes)
{
$this->approvedScopes = $approvedScopes;
return $this;
}
}
socialite/src/Two/GoogleProvider.php 0000644 00000004735 15242753746 0013550 0 ustar 00 buildAuthUrlFromBase('https://accounts.google.com/o/oauth2/auth', $state);
}
/**
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return 'https://www.googleapis.com/oauth2/v4/token';
}
/**
* {@inheritdoc}
*/
protected function getUserByToken($token)
{
$response = $this->getHttpClient()->get('https://www.googleapis.com/oauth2/v3/userinfo', [
RequestOptions::QUERY => [
'prettyPrint' => 'false',
],
RequestOptions::HEADERS => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$token,
],
]);
return json_decode((string) $response->getBody(), true);
}
/**
* {@inheritdoc}
*/
public function refreshToken($refreshToken)
{
$response = $this->getRefreshTokenResponse($refreshToken);
return new Token(
Arr::get($response, 'access_token'),
Arr::get($response, 'refresh_token', $refreshToken),
Arr::get($response, 'expires_in'),
explode($this->scopeSeparator, Arr::get($response, 'scope', ''))
);
}
/**
* {@inheritdoc}
*/
protected function mapUserToObject(array $user)
{
// Deprecated: Fields added to keep backwards compatibility in 4.0. These will be removed in 5.0
$user['id'] = Arr::get($user, 'sub');
$user['verified_email'] = Arr::get($user, 'email_verified');
$user['link'] = Arr::get($user, 'profile');
return (new User)->setRaw($user)->map([
'id' => Arr::get($user, 'sub'),
'nickname' => Arr::get($user, 'nickname'),
'name' => Arr::get($user, 'name'),
'email' => Arr::get($user, 'email'),
'avatar' => $avatarUrl = Arr::get($user, 'picture'),
'avatar_original' => $avatarUrl,
]);
}
}
socialite/src/Two/Token.php 0000644 00000002047 15242753746 0011673 0 ustar 00 token = $token;
$this->refreshToken = $refreshToken;
$this->expiresIn = $expiresIn;
$this->approvedScopes = $approvedScopes;
}
}
socialite/src/Two/SlackOpenIdProvider.php 0000644 00000003100 15242753746 0014451 0 ustar 00 buildAuthUrlFromBase('https://slack.com/openid/connect/authorize', $state);
}
/**
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return 'https://slack.com/api/openid.connect.token';
}
/**
* {@inheritdoc}
*/
protected function getUserByToken($token)
{
$response = $this->getHttpClient()->get('https://slack.com/api/openid.connect.userInfo', [
RequestOptions::HEADERS => ['Authorization' => 'Bearer '.$token],
]);
return json_decode($response->getBody(), true);
}
/**
* {@inheritdoc}
*/
protected function mapUserToObject(array $user)
{
return (new User)->setRaw($user)->map([
'id' => Arr::get($user, 'sub'),
'nickname' => null,
'name' => Arr::get($user, 'name'),
'email' => Arr::get($user, 'email'),
'avatar' => Arr::get($user, 'picture'),
'organization_id' => Arr::get($user, 'https://slack.com/team_id'),
]);
}
}
socialite/src/Two/SlackProvider.php 0000644 00000005137 15242753746 0013366 0 ustar 00 scopeKey = 'scope';
return $this;
}
/**
* {@inheritdoc}
*/
public function getAuthUrl($state)
{
return $this->buildAuthUrlFromBase('https://slack.com/oauth/v2/authorize', $state);
}
/**
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return 'https://slack.com/api/oauth.v2.access';
}
/**
* {@inheritdoc}
*/
protected function getUserByToken($token)
{
$response = $this->getHttpClient()->get('https://slack.com/api/users.identity', [
RequestOptions::HEADERS => ['Authorization' => 'Bearer '.$token],
]);
return json_decode($response->getBody(), true);
}
/**
* {@inheritdoc}
*/
protected function mapUserToObject(array $user)
{
return (new User)->setRaw($user)->map([
'id' => Arr::get($user, 'user.id'),
'name' => Arr::get($user, 'user.name'),
'email' => Arr::get($user, 'user.email'),
'avatar' => Arr::get($user, 'user.image_512'),
'organization_id' => Arr::get($user, 'team.id'),
]);
}
/**
* {@inheritdoc}
*/
protected function getCodeFields($state = null)
{
$fields = parent::getCodeFields($state);
if ($this->scopeKey === 'user_scope') {
$fields['scope'] = '';
$fields['user_scope'] = $this->formatScopes($this->scopes, $this->scopeSeparator);
}
return $fields;
}
/**
* {@inheritdoc}
*/
public function getAccessTokenResponse($code)
{
$response = $this->getHttpClient()->post($this->getTokenUrl(), [
RequestOptions::HEADERS => $this->getTokenHeaders($code),
RequestOptions::FORM_PARAMS => $this->getTokenFields($code),
]);
$result = json_decode($response->getBody(), true);
if ($this->scopeKey === 'user_scope') {
return $result['authed_user'];
}
return $result;
}
}
socialite/src/Two/InvalidStateException.php 0000644 00000000220 15242753746 0015050 0 ustar 00 buildAuthUrlFromBase('https://www.facebook.com/'.$this->version.'/dialog/oauth', $state);
}
/**
* {@inheritdoc}
*/
protected function getTokenUrl()
{
return $this->graphUrl.'/'.$this->version.'/oauth/access_token';
}
/**
* {@inheritdoc}
*/
public function getAccessTokenResponse($code)
{
$response = $this->getHttpClient()->post($this->getTokenUrl(), [
RequestOptions::FORM_PARAMS => $this->getTokenFields($code),
]);
$data = json_decode($response->getBody(), true);
return Arr::add($data, 'expires_in', Arr::pull($data, 'expires'));
}
/**
* {@inheritdoc}
*/
protected function getUserByToken($token)
{
$this->lastToken = $token;
return $this->getUserByOIDCToken($token) ??
$this->getUserFromAccessToken($token);
}
/**
* Get user based on the OIDC token.
*
* @param string $token
* @return array
*/
protected function getUserByOIDCToken($token)
{
$kid = json_decode(base64_decode(explode('.', $token)[0]), true)['kid'] ?? null;
if ($kid === null) {
return null;
}
$data = (array) JWT::decode($token, $this->getPublicKeyOfOIDCToken($kid));
throw_if($data['aud'] !== $this->clientId, new Exception('Token has incorrect audience.'));
throw_if($data['iss'] !== 'https://www.facebook.com', new Exception('Token has incorrect issuer.'));
$data['id'] = $data['sub'];
if (isset($data['given_name'])) {
$data['first_name'] = $data['given_name'];
}
if (isset($data['family_name'])) {
$data['last_name'] = $data['family_name'];
}
return $data;
}
/**
* Get the public key to verify the signature of OIDC token.
*
* @param string $id
* @return \Firebase\JWT\Key
*/
protected function getPublicKeyOfOIDCToken(string $kid)
{
$response = $this->getHttpClient()->get('https://limited.facebook.com/.well-known/oauth/openid/jwks/');
$key = Arr::first(json_decode($response->getBody()->getContents(), true)['keys'], function ($key) use ($kid) {
return $key['kid'] === $kid;
});
$key['n'] = new BigInteger(JWT::urlsafeB64Decode($key['n']), 256);
$key['e'] = new BigInteger(JWT::urlsafeB64Decode($key['e']), 256);
return new Key((string) RSA::load($key), 'RS256');
}
/**
* Get user based on the access token.
*
* @param string $token
* @return array
*/
protected function getUserFromAccessToken($token)
{
$params = [
'access_token' => $token,
'fields' => implode(',', $this->fields),
];
if (! empty($this->clientSecret)) {
$params['appsecret_proof'] = hash_hmac('sha256', $token, $this->clientSecret);
}
$response = $this->getHttpClient()->get($this->graphUrl.'/'.$this->version.'/me', [
RequestOptions::HEADERS => [
'Accept' => 'application/json',
],
RequestOptions::QUERY => $params,
]);
return json_decode($response->getBody(), true);
}
/**
* {@inheritdoc}
*/
protected function mapUserToObject(array $user)
{
if (! isset($user['sub'])) {
$avatarUrl = $this->graphUrl.'/'.$this->version.'/'.$user['id'].'/picture';
$avatarOriginalUrl = $avatarUrl.'?width=1920';
}
return (new User)->setRaw($user)->map([
'id' => $user['id'],
'nickname' => null,
'name' => $user['name'] ?? null,
'email' => $user['email'] ?? null,
'avatar' => $avatarUrl ?? $user['picture'] ?? null,
'avatar_original' => $avatarOriginalUrl ?? $user['picture'] ?? null,
'profileUrl' => $user['link'] ?? null,
]);
}
/**
* {@inheritdoc}
*/
protected function getCodeFields($state = null)
{
$fields = parent::getCodeFields($state);
if ($this->popup) {
$fields['display'] = 'popup';
}
if ($this->reRequest) {
$fields['auth_type'] = 'rerequest';
}
return $fields;
}
/**
* Set the user fields to request from Facebook.
*
* @param array $fields
* @return $this
*/
public function fields(array $fields)
{
$this->fields = $fields;
return $this;
}
/**
* Set the dialog to be displayed as a popup.
*
* @return $this
*/
public function asPopup()
{
$this->popup = true;
return $this;
}
/**
* Re-request permissions which were previously declined.
*
* @return $this
*/
public function reRequest()
{
$this->reRequest = true;
return $this;
}
/**
* Get the last access token used.
*
* @return string|null
*/
public function lastToken()
{
return $this->lastToken;
}
/**
* Specify which graph version should be used.
*
* @param string $version
* @return $this
*/
public function usingGraphVersion(string $version)
{
$this->version = $version;
return $this;
}
}
socialite/src/Two/AbstractProvider.php 0000644 00000032251 15242753746 0014071 0 ustar 00 guzzle = $guzzle;
$this->request = $request;
$this->clientId = $clientId;
$this->redirectUrl = $redirectUrl;
$this->clientSecret = $clientSecret;
}
/**
* Get the authentication URL for the provider.
*
* @param string $state
* @return string
*/
abstract protected function getAuthUrl($state);
/**
* Get the token URL for the provider.
*
* @return string
*/
abstract protected function getTokenUrl();
/**
* Get the raw user for the given access token.
*
* @param string $token
* @return array
*/
abstract protected function getUserByToken($token);
/**
* Map the raw user array to a Socialite User instance.
*
* @param array $user
* @return \Laravel\Socialite\Two\User
*/
abstract protected function mapUserToObject(array $user);
/**
* Redirect the user of the application to the provider's authentication screen.
*
* @return \Illuminate\Http\RedirectResponse
*/
public function redirect()
{
$state = null;
if ($this->usesState()) {
$this->request->session()->put('state', $state = $this->getState());
}
if ($this->usesPKCE()) {
$this->request->session()->put('code_verifier', $this->getCodeVerifier());
}
return new RedirectResponse($this->getAuthUrl($state));
}
/**
* Build the authentication URL for the provider from the given base URL.
*
* @param string $url
* @param string $state
* @return string
*/
protected function buildAuthUrlFromBase($url, $state)
{
return $url.'?'.http_build_query($this->getCodeFields($state), '', '&', $this->encodingType);
}
/**
* Get the GET parameters for the code request.
*
* @param string|null $state
* @return array
*/
protected function getCodeFields($state = null)
{
$fields = [
'client_id' => $this->clientId,
'redirect_uri' => $this->redirectUrl,
'scope' => $this->formatScopes($this->getScopes(), $this->scopeSeparator),
'response_type' => 'code',
];
if ($this->usesState()) {
$fields['state'] = $state;
}
if ($this->usesPKCE()) {
$fields['code_challenge'] = $this->getCodeChallenge();
$fields['code_challenge_method'] = $this->getCodeChallengeMethod();
}
return array_merge($fields, $this->parameters);
}
/**
* Format the given scopes.
*
* @param array $scopes
* @param string $scopeSeparator
* @return string
*/
protected function formatScopes(array $scopes, $scopeSeparator)
{
return implode($scopeSeparator, $scopes);
}
/**
* {@inheritdoc}
*/
public function user()
{
if ($this->user) {
return $this->user;
}
if ($this->hasInvalidState()) {
throw new InvalidStateException;
}
$response = $this->getAccessTokenResponse($this->getCode());
$user = $this->getUserByToken(Arr::get($response, 'access_token'));
return $this->userInstance($response, $user);
}
/**
* Create a user instance from the given data.
*
* @param array $response
* @param array $user
* @return \Laravel\Socialite\Two\User
*/
protected function userInstance(array $response, array $user)
{
$this->user = $this->mapUserToObject($user);
return $this->user->setToken(Arr::get($response, 'access_token'))
->setRefreshToken(Arr::get($response, 'refresh_token'))
->setExpiresIn(Arr::get($response, 'expires_in'))
->setApprovedScopes(explode($this->scopeSeparator, Arr::get($response, 'scope', '')));
}
/**
* Get a Social User instance from a known access token.
*
* @param string $token
* @return \Laravel\Socialite\Two\User
*/
public function userFromToken($token)
{
$user = $this->mapUserToObject($this->getUserByToken($token));
return $user->setToken($token);
}
/**
* Determine if the current request / session has a mismatching "state".
*
* @return bool
*/
protected function hasInvalidState()
{
if ($this->isStateless()) {
return false;
}
$state = $this->request->session()->pull('state');
return empty($state) || $this->request->input('state') !== $state;
}
/**
* Get the access token response for the given code.
*
* @param string $code
* @return array
*/
public function getAccessTokenResponse($code)
{
$response = $this->getHttpClient()->post($this->getTokenUrl(), [
RequestOptions::HEADERS => $this->getTokenHeaders($code),
RequestOptions::FORM_PARAMS => $this->getTokenFields($code),
]);
return json_decode($response->getBody(), true);
}
/**
* Get the headers for the access token request.
*
* @param string $code
* @return array
*/
protected function getTokenHeaders($code)
{
return ['Accept' => 'application/json'];
}
/**
* Get the POST fields for the token request.
*
* @param string $code
* @return array
*/
protected function getTokenFields($code)
{
$fields = [
'grant_type' => 'authorization_code',
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
'code' => $code,
'redirect_uri' => $this->redirectUrl,
];
if ($this->usesPKCE()) {
$fields['code_verifier'] = $this->request->session()->pull('code_verifier');
}
return array_merge($fields, $this->parameters);
}
/**
* Refresh a user's access token with a refresh token.
*
* @param string $refreshToken
* @return \Laravel\Socialite\Two\Token
*/
public function refreshToken($refreshToken)
{
$response = $this->getRefreshTokenResponse($refreshToken);
return new Token(
Arr::get($response, 'access_token'),
Arr::get($response, 'refresh_token'),
Arr::get($response, 'expires_in'),
explode($this->scopeSeparator, Arr::get($response, 'scope', ''))
);
}
/**
* Get the refresh token response for the given refresh token.
*
* @param string $refreshToken
* @return array
*/
protected function getRefreshTokenResponse($refreshToken)
{
return json_decode($this->getHttpClient()->post($this->getTokenUrl(), [
RequestOptions::HEADERS => ['Accept' => 'application/json'],
RequestOptions::FORM_PARAMS => [
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken,
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
],
])->getBody(), true);
}
/**
* Get the code from the request.
*
* @return string
*/
protected function getCode()
{
return $this->request->input('code');
}
/**
* Merge the scopes of the requested access.
*
* @param array|string $scopes
* @return $this
*/
public function scopes($scopes)
{
$this->scopes = array_unique(array_merge($this->scopes, (array) $scopes));
return $this;
}
/**
* Set the scopes of the requested access.
*
* @param array|string $scopes
* @return $this
*/
public function setScopes($scopes)
{
$this->scopes = array_unique((array) $scopes);
return $this;
}
/**
* Get the current scopes.
*
* @return array
*/
public function getScopes()
{
return $this->scopes;
}
/**
* Set the redirect URL.
*
* @param string $url
* @return $this
*/
public function redirectUrl($url)
{
$this->redirectUrl = $url;
return $this;
}
/**
* Get a instance of the Guzzle HTTP client.
*
* @return \GuzzleHttp\Client
*/
protected function getHttpClient()
{
if (is_null($this->httpClient)) {
$this->httpClient = new Client($this->guzzle);
}
return $this->httpClient;
}
/**
* Set the Guzzle HTTP client instance.
*
* @param \GuzzleHttp\Client $client
* @return $this
*/
public function setHttpClient(Client $client)
{
$this->httpClient = $client;
return $this;
}
/**
* Set the request instance.
*
* @param \Illuminate\Http\Request $request
* @return $this
*/
public function setRequest(Request $request)
{
$this->request = $request;
return $this;
}
/**
* Determine if the provider is operating with state.
*
* @return bool
*/
protected function usesState()
{
return ! $this->stateless;
}
/**
* Determine if the provider is operating as stateless.
*
* @return bool
*/
protected function isStateless()
{
return $this->stateless;
}
/**
* Indicates that the provider should operate as stateless.
*
* @return $this
*/
public function stateless()
{
$this->stateless = true;
return $this;
}
/**
* Get the string used for session state.
*
* @return string
*/
protected function getState()
{
return Str::random(40);
}
/**
* Determine if the provider uses PKCE.
*
* @return bool
*/
protected function usesPKCE()
{
return $this->usesPKCE;
}
/**
* Enables PKCE for the provider.
*
* @return $this
*/
public function enablePKCE()
{
$this->usesPKCE = true;
return $this;
}
/**
* Generates a random string of the right length for the PKCE code verifier.
*
* @return string
*/
protected function getCodeVerifier()
{
return Str::random(96);
}
/**
* Generates the PKCE code challenge based on the PKCE code verifier in the session.
*
* @return string
*/
protected function getCodeChallenge()
{
$hashed = hash('sha256', $this->request->session()->get('code_verifier'), true);
return rtrim(strtr(base64_encode($hashed), '+/', '-_'), '=');
}
/**
* Returns the hash method used to calculate the PKCE code challenge.
*
* @return string
*/
protected function getCodeChallengeMethod()
{
return 'S256';
}
/**
* Set the custom parameters of the request.
*
* @param array $parameters
* @return $this
*/
public function with(array $parameters)
{
$this->parameters = $parameters;
return $this;
}
}
socialite/src/Facades/Socialite.php 0000644 00000001513 15242753746 0013301 0 ustar 00 app->singleton(Factory::class, function ($app) {
return new SocialiteManager($app);
});
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return [Factory::class];
}
}
socialite/src/AbstractUser.php 0000644 00000007205 15242753746 0012445 0 ustar 00 id;
}
/**
* Get the nickname / username for the user.
*
* @return string|null
*/
public function getNickname()
{
return $this->nickname;
}
/**
* Get the full name of the user.
*
* @return string|null
*/
public function getName()
{
return $this->name;
}
/**
* Get the e-mail address of the user.
*
* @return string|null
*/
public function getEmail()
{
return $this->email;
}
/**
* Get the avatar / image URL for the user.
*
* @return string|null
*/
public function getAvatar()
{
return $this->avatar;
}
/**
* Get the raw user array.
*
* @return array
*/
public function getRaw()
{
return $this->user;
}
/**
* Set the raw user array from the provider.
*
* @param array $user
* @return $this
*/
public function setRaw(array $user)
{
$this->user = $user;
return $this;
}
/**
* Map the given array onto the user's properties.
*
* @param array $attributes
* @return $this
*/
public function map(array $attributes)
{
$this->attributes = $attributes;
foreach ($attributes as $key => $value) {
if (property_exists($this, $key)) {
$this->{$key} = $value;
}
}
return $this;
}
/**
* Determine if the given raw user attribute exists.
*
* @param string $offset
* @return bool
*/
#[\ReturnTypeWillChange]
public function offsetExists($offset)
{
return array_key_exists($offset, $this->user);
}
/**
* Get the given key from the raw user.
*
* @param string $offset
* @return mixed
*/
#[\ReturnTypeWillChange]
public function offsetGet($offset)
{
return $this->user[$offset];
}
/**
* Set the given attribute on the raw user array.
*
* @param string $offset
* @param mixed $value
* @return void
*/
#[\ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
$this->user[$offset] = $value;
}
/**
* Unset the given value from the raw user array.
*
* @param string $offset
* @return void
*/
#[\ReturnTypeWillChange]
public function offsetUnset($offset)
{
unset($this->user[$offset]);
}
/**
* Get a user attribute value dynamically.
*
* @param string $key
* @return void
*/
public function __get($key)
{
return $this->attributes[$key] ?? null;
}
}
socialite/src/SocialiteManager.php 0000644 00000017115 15242753746 0013253 0 ustar 00 driver($driver);
}
/**
* Create an instance of the specified driver.
*
* @return \Laravel\Socialite\Two\AbstractProvider
*/
protected function createGithubDriver()
{
$config = $this->config->get('services.github');
return $this->buildProvider(
GithubProvider::class, $config
);
}
/**
* Create an instance of the specified driver.
*
* @return \Laravel\Socialite\Two\AbstractProvider
*/
protected function createFacebookDriver()
{
$config = $this->config->get('services.facebook');
return $this->buildProvider(
FacebookProvider::class, $config
);
}
/**
* Create an instance of the specified driver.
*
* @return \Laravel\Socialite\Two\AbstractProvider
*/
protected function createGoogleDriver()
{
$config = $this->config->get('services.google');
return $this->buildProvider(
GoogleProvider::class, $config
);
}
/**
* Create an instance of the specified driver.
*
* @return \Laravel\Socialite\Two\AbstractProvider
*/
protected function createLinkedinDriver()
{
$config = $this->config->get('services.linkedin');
return $this->buildProvider(
LinkedInProvider::class, $config
);
}
/**
* Create an instance of the specified driver.
*
* @return \Laravel\Socialite\Two\AbstractProvider
*/
protected function createLinkedinOpenidDriver()
{
$config = $this->config->get('services.linkedin-openid');
return $this->buildProvider(
LinkedInOpenIdProvider::class, $config
);
}
/**
* Create an instance of the specified driver.
*
* @return \Laravel\Socialite\Two\AbstractProvider
*/
protected function createBitbucketDriver()
{
$config = $this->config->get('services.bitbucket');
return $this->buildProvider(
BitbucketProvider::class, $config
);
}
/**
* Create an instance of the specified driver.
*
* @return \Laravel\Socialite\Two\AbstractProvider
*/
protected function createGitlabDriver()
{
$config = $this->config->get('services.gitlab');
return $this->buildProvider(
GitlabProvider::class, $config
)->setHost($config['host'] ?? null);
}
/**
* Create an instance of the specified driver.
*
* @return \Laravel\Socialite\One\AbstractProvider|\Laravel\Socialite\Two\AbstractProvider
*/
protected function createTwitterDriver()
{
$config = $this->config->get('services.twitter');
if (($config['oauth'] ?? null) === 2) {
return $this->createTwitterOAuth2Driver();
}
return new TwitterProvider(
$this->container->make('request'), new TwitterServer($this->formatConfig($config))
);
}
/**
* Create an instance of the specified driver.
*
* @return \Laravel\Socialite\Two\AbstractProvider
*/
protected function createTwitterOAuth2Driver()
{
$config = $this->config->get('services.twitter') ?? $this->config->get('services.twitter-oauth-2');
return $this->buildProvider(
TwitterOAuth2Provider::class, $config
);
}
/**
* Create an instance of the specified driver.
*
* @return \Laravel\Socialite\Two\AbstractProvider
*/
protected function createXDriver()
{
$config = $this->config->get('services.x') ?? $this->config->get('services.x-oauth-2');
return $this->buildProvider(
XProvider::class, $config
);
}
/**
* Create an instance of the specified driver.
*
* @return \Laravel\Socialite\Two\AbstractProvider
*/
protected function createSlackDriver()
{
$config = $this->config->get('services.slack');
return $this->buildProvider(
SlackProvider::class, $config
);
}
/**
* Create an instance of the specified driver.
*
* @return \Laravel\Socialite\Two\AbstractProvider
*/
protected function createSlackOpenidDriver()
{
$config = $this->config->get('services.slack-openid');
return $this->buildProvider(
SlackOpenIdProvider::class, $config
);
}
/**
* Build an OAuth 2 provider instance.
*
* @param string $provider
* @param array $config
* @return \Laravel\Socialite\Two\AbstractProvider
*/
public function buildProvider($provider, $config)
{
return new $provider(
$this->container->make('request'), $config['client_id'],
$config['client_secret'], $this->formatRedirectUrl($config),
Arr::get($config, 'guzzle', [])
);
}
/**
* Format the server configuration.
*
* @param array $config
* @return array
*/
public function formatConfig(array $config)
{
return array_merge([
'identifier' => $config['client_id'],
'secret' => $config['client_secret'],
'callback_uri' => $this->formatRedirectUrl($config),
], $config);
}
/**
* Format the callback URL, resolving a relative URI if needed.
*
* @param array $config
* @return string
*/
protected function formatRedirectUrl(array $config)
{
$redirect = value($config['redirect']);
return Str::startsWith($redirect ?? '', '/')
? $this->container->make('url')->to($redirect)
: $redirect;
}
/**
* Forget all of the resolved driver instances.
*
* @return $this
*/
public function forgetDrivers()
{
$this->drivers = [];
return $this;
}
/**
* Set the container instance used by the manager.
*
* @param \Illuminate\Contracts\Container\Container $container
* @return $this
*/
public function setContainer($container)
{
$this->app = $container;
$this->container = $container;
$this->config = $container->make('config');
return $this;
}
/**
* Get the default driver name.
*
* @return string
*
* @throws \InvalidArgumentException
*/
public function getDefaultDriver()
{
throw new InvalidArgumentException('No Socialite driver was specified.');
}
}
socialite/README.md 0000644 00000003565 15242753746 0010027 0 ustar 00
## Introduction
Laravel Socialite provides an expressive, fluent interface to OAuth authentication with Facebook, Twitter, Google, LinkedIn, GitHub, GitLab and Bitbucket. It handles almost all of the boilerplate social authentication code you are dreading writing.
**We are not accepting new adapters.**
Adapters for other platforms are listed at the community driven [Socialite Providers](https://socialiteproviders.com/) website.
## Official Documentation
Documentation for Socialite can be found on the [Laravel website](https://laravel.com/docs/socialite).
## Contributing
Thank you for considering contributing to Socialite! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
## Code of Conduct
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
## Security Vulnerabilities
Please review [our security policy](https://github.com/laravel/socialite/security/policy) on how to report security vulnerabilities.
## License
Laravel Socialite is open-sourced software licensed under the [MIT license](LICENSE.md).
socialite/composer.json 0000644 00000003357 15242753746 0011271 0 ustar 00 {
"name": "laravel/socialite",
"description": "Laravel wrapper around OAuth 1 & OAuth 2 libraries.",
"keywords": ["oauth", "laravel"],
"license": "MIT",
"homepage": "https://laravel.com",
"support": {
"issues": "https://github.com/laravel/socialite/issues",
"source": "https://github.com/laravel/socialite"
},
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"require": {
"php": "^7.2|^8.0",
"ext-json": "*",
"firebase/php-jwt": "^6.4",
"guzzlehttp/guzzle": "^6.0|^7.0",
"illuminate/contracts": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
"illuminate/http": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
"illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0",
"league/oauth1-client": "^1.10.1",
"phpseclib/phpseclib": "^3.0"
},
"require-dev": {
"mockery/mockery": "^1.0",
"orchestra/testbench": "^4.0|^5.0|^6.0|^7.0|^8.0|^9.0",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^8.0|^9.3|^10.4"
},
"autoload": {
"psr-4": {
"Laravel\\Socialite\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Laravel\\Socialite\\Tests\\": "tests/"
}
},
"extra": {
"branch-alias": {
"dev-master": "5.x-dev"
},
"laravel": {
"providers": [
"Laravel\\Socialite\\SocialiteServiceProvider"
],
"aliases": {
"Socialite": "Laravel\\Socialite\\Facades\\Socialite"
}
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev",
"prefer-stable": true
}
socialite/LICENSE.md 0000644 00000002063 15242753746 0010144 0 ustar 00 The MIT License (MIT)
Copyright (c) Taylor Otwell
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.
framework/src/Illuminate/Routing/Events/ResponsePrepared.php 0000644 00000001253 15242753746 0020340 0 ustar 00 request = $request;
$this->response = $response;
}
}
framework/src/Illuminate/Routing/Events/RouteMatched.php 0000644 00000001121 15242753746 0017435 0 ustar 00 route = $route;
$this->request = $request;
}
}
framework/src/Illuminate/Routing/Events/PreparingResponse.php 0000644 00000001142 15242753746 0020522 0 ustar 00 request = $request;
$this->response = $response;
}
}
framework/src/Illuminate/Routing/Events/Routing.php 0000644 00000000610 15242753746 0016502 0 ustar 00 request = $request;
}
}
framework/src/Illuminate/Routing/Exceptions/UrlGenerationException.php 0000644 00000001643 15242753746 0022374 0 ustar 00 getName(),
$route->uri()
);
if (count($parameters) > 0) {
$message .= sprintf(' [Missing %s: %s]', $parameterLabel, implode(', ', $parameters));
}
$message .= '.';
return new static($message);
}
}
framework/src/Illuminate/Routing/Exceptions/BackedEnumCaseNotFoundException.php 0000644 00000000706 15242753746 0024064 0 ustar 00 originalException = $originalException;
parent::__construct($originalException->getMessage());
}
/**
* Render the exception.
*
* @return \Illuminate\Http\Response
*/
public function render()
{
return new Response('');
}
/**
* Get the actual exception thrown during the stream.
*
* @return \Throwable
*/
public function getInnerException()
{
return $this->originalException;
}
}
framework/src/Illuminate/Routing/Controllers/HasMiddleware.php 0000644 00000000423 15242753746 0020630 0 ustar 00 middleware = $middleware;
}
/**
* Specify the only controller methods the middleware should apply to.
*
* @param array|string $only
* @return $this
*/
public function only(array|string $only)
{
$this->only = Arr::wrap($only);
return $this;
}
/**
* Specify the controller methods the middleware should not apply to.
*
* @param array|string $except
* @return $this
*/
public function except(array|string $except)
{
$this->except = Arr::wrap($except);
return $this;
}
}
framework/src/Illuminate/Routing/ViewController.php 0000644 00000002645 15242753746 0016577 0 ustar 00 response = $response;
}
/**
* Invoke the controller method.
*
* @param mixed ...$args
* @return \Illuminate\Http\Response
*/
public function __invoke(...$args)
{
$routeParameters = array_filter($args, function ($key) {
return ! in_array($key, ['view', 'data', 'status', 'headers']);
}, ARRAY_FILTER_USE_KEY);
$args['data'] = array_merge($args['data'], $routeParameters);
return $this->response->view(
$args['view'],
$args['data'],
$args['status'],
$args['headers']
);
}
/**
* Execute an action on the controller.
*
* @param string $method
* @param array $parameters
* @return \Symfony\Component\HttpFoundation\Response
*/
public function callAction($method, $parameters)
{
return $this->{$method}(...$parameters);
}
}
framework/src/Illuminate/Routing/CompiledRouteCollection.php 0000644 00000021444 15242753746 0020406 0 ustar 00 compiled = $compiled;
$this->attributes = $attributes;
$this->routes = new RouteCollection;
}
/**
* Add a Route instance to the collection.
*
* @param \Illuminate\Routing\Route $route
* @return \Illuminate\Routing\Route
*/
public function add(Route $route)
{
return $this->routes->add($route);
}
/**
* Refresh the name look-up table.
*
* This is done in case any names are fluently defined or if routes are overwritten.
*
* @return void
*/
public function refreshNameLookups()
{
//
}
/**
* Refresh the action look-up table.
*
* This is done in case any actions are overwritten with new controllers.
*
* @return void
*/
public function refreshActionLookups()
{
//
}
/**
* Find the first route matching a given request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Routing\Route
*
* @throws \Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
public function match(Request $request)
{
$matcher = new CompiledUrlMatcher(
$this->compiled, (new RequestContext)->fromRequest(
$trimmedRequest = $this->requestWithoutTrailingSlash($request)
)
);
$route = null;
try {
if ($result = $matcher->matchRequest($trimmedRequest)) {
$route = $this->getByName($result['_route']);
}
} catch (ResourceNotFoundException|MethodNotAllowedException) {
try {
return $this->routes->match($request);
} catch (NotFoundHttpException) {
//
}
}
if ($route && $route->isFallback) {
try {
$dynamicRoute = $this->routes->match($request);
if (! $dynamicRoute->isFallback) {
$route = $dynamicRoute;
}
} catch (NotFoundHttpException|MethodNotAllowedHttpException) {
//
}
}
return $this->handleMatchedRoute($request, $route);
}
/**
* Get a cloned instance of the given request without any trailing slash on the URI.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Request
*/
protected function requestWithoutTrailingSlash(Request $request)
{
$trimmedRequest = $request->duplicate();
$parts = explode('?', $request->server->get('REQUEST_URI'), 2);
$trimmedRequest->server->set(
'REQUEST_URI', rtrim($parts[0], '/').(isset($parts[1]) ? '?'.$parts[1] : '')
);
return $trimmedRequest;
}
/**
* Get routes from the collection by method.
*
* @param string|null $method
* @return \Illuminate\Routing\Route[]
*/
public function get($method = null)
{
return $this->getRoutesByMethod()[$method] ?? [];
}
/**
* Determine if the route collection contains a given named route.
*
* @param string $name
* @return bool
*/
public function hasNamedRoute($name)
{
return isset($this->attributes[$name]) || $this->routes->hasNamedRoute($name);
}
/**
* Get a route instance by its name.
*
* @param string $name
* @return \Illuminate\Routing\Route|null
*/
public function getByName($name)
{
if (isset($this->attributes[$name])) {
return $this->newRoute($this->attributes[$name]);
}
return $this->routes->getByName($name);
}
/**
* Get a route instance by its controller action.
*
* @param string $action
* @return \Illuminate\Routing\Route|null
*/
public function getByAction($action)
{
$attributes = collect($this->attributes)->first(function (array $attributes) use ($action) {
if (isset($attributes['action']['controller'])) {
return trim($attributes['action']['controller'], '\\') === $action;
}
return $attributes['action']['uses'] === $action;
});
if ($attributes) {
return $this->newRoute($attributes);
}
return $this->routes->getByAction($action);
}
/**
* Get all of the routes in the collection.
*
* @return \Illuminate\Routing\Route[]
*/
public function getRoutes()
{
return collect($this->attributes)
->map(function (array $attributes) {
return $this->newRoute($attributes);
})
->merge($this->routes->getRoutes())
->values()
->all();
}
/**
* Get all of the routes keyed by their HTTP verb / method.
*
* @return array
*/
public function getRoutesByMethod()
{
return collect($this->getRoutes())
->groupBy(function (Route $route) {
return $route->methods();
})
->map(function (Collection $routes) {
return $routes->mapWithKeys(function (Route $route) {
return [$route->getDomain().$route->uri => $route];
})->all();
})
->all();
}
/**
* Get all of the routes keyed by their name.
*
* @return \Illuminate\Routing\Route[]
*/
public function getRoutesByName()
{
return collect($this->getRoutes())
->keyBy(function (Route $route) {
return $route->getName();
})
->all();
}
/**
* Resolve an array of attributes to a Route instance.
*
* @param array $attributes
* @return \Illuminate\Routing\Route
*/
protected function newRoute(array $attributes)
{
if (empty($attributes['action']['prefix'] ?? '')) {
$baseUri = $attributes['uri'];
} else {
$prefix = trim($attributes['action']['prefix'], '/');
$baseUri = trim(implode(
'/', array_slice(
explode('/', trim($attributes['uri'], '/')),
count($prefix !== '' ? explode('/', $prefix) : [])
)
), '/');
}
return $this->router->newRoute($attributes['methods'], $baseUri === '' ? '/' : $baseUri, $attributes['action'])
->setFallback($attributes['fallback'])
->setDefaults($attributes['defaults'])
->setWheres($attributes['wheres'])
->setBindingFields($attributes['bindingFields'])
->block($attributes['lockSeconds'] ?? null, $attributes['waitSeconds'] ?? null)
->withTrashed($attributes['withTrashed'] ?? false);
}
/**
* Set the router instance on the route.
*
* @param \Illuminate\Routing\Router $router
* @return $this
*/
public function setRouter(Router $router)
{
$this->router = $router;
return $this;
}
/**
* Set the container instance on the route.
*
* @param \Illuminate\Container\Container $container
* @return $this
*/
public function setContainer(Container $container)
{
$this->container = $container;
return $this;
}
}
framework/src/Illuminate/Routing/ControllerDispatcher.php 0000644 00000004317 15242753746 0017751 0 ustar 00 container = $container;
}
/**
* Dispatch a request to a given controller and method.
*
* @param \Illuminate\Routing\Route $route
* @param mixed $controller
* @param string $method
* @return mixed
*/
public function dispatch(Route $route, $controller, $method)
{
$parameters = $this->resolveParameters($route, $controller, $method);
if (method_exists($controller, 'callAction')) {
return $controller->callAction($method, $parameters);
}
return $controller->{$method}(...array_values($parameters));
}
/**
* Resolve the parameters for the controller.
*
* @param \Illuminate\Routing\Route $route
* @param mixed $controller
* @param string $method
* @return array
*/
protected function resolveParameters(Route $route, $controller, $method)
{
return $this->resolveClassMethodDependencies(
$route->parametersWithoutNulls(), $controller, $method
);
}
/**
* Get the middleware for the controller instance.
*
* @param \Illuminate\Routing\Controller $controller
* @param string $method
* @return array
*/
public function getMiddleware($controller, $method)
{
if (! method_exists($controller, 'getMiddleware')) {
return [];
}
return collect($controller->getMiddleware())->reject(function ($data) use ($method) {
return static::methodExcludedByOptions($method, $data['options']);
})->pluck('middleware')->all();
}
}
framework/src/Illuminate/Routing/FiltersControllerMiddleware.php 0000644 00000001014 15242753746 0021260 0 ustar 00 all();
}
$this->items = $this->sortMiddleware($priorityMap, $middlewares);
}
/**
* Sort the middlewares by the given priority map.
*
* Each call to this method makes one discrete middleware movement if necessary.
*
* @param array $priorityMap
* @param array $middlewares
* @return array
*/
protected function sortMiddleware($priorityMap, $middlewares)
{
$lastIndex = 0;
foreach ($middlewares as $index => $middleware) {
if (! is_string($middleware)) {
continue;
}
$priorityIndex = $this->priorityMapIndex($priorityMap, $middleware);
if (! is_null($priorityIndex)) {
// This middleware is in the priority map. If we have encountered another middleware
// that was also in the priority map and was at a lower priority than the current
// middleware, we will move this middleware to be above the previous encounter.
if (isset($lastPriorityIndex) && $priorityIndex < $lastPriorityIndex) {
return $this->sortMiddleware(
$priorityMap, array_values($this->moveMiddleware($middlewares, $index, $lastIndex))
);
}
// This middleware is in the priority map; but, this is the first middleware we have
// encountered from the map thus far. We'll save its current index plus its index
// from the priority map so we can compare against them on the next iterations.
$lastIndex = $index;
$lastPriorityIndex = $priorityIndex;
}
}
return Router::uniqueMiddleware($middlewares);
}
/**
* Calculate the priority map index of the middleware.
*
* @param array $priorityMap
* @param string $middleware
* @return int|null
*/
protected function priorityMapIndex($priorityMap, $middleware)
{
foreach ($this->middlewareNames($middleware) as $name) {
$priorityIndex = array_search($name, $priorityMap);
if ($priorityIndex !== false) {
return $priorityIndex;
}
}
}
/**
* Resolve the middleware names to look for in the priority array.
*
* @param string $middleware
* @return \Generator
*/
protected function middlewareNames($middleware)
{
$stripped = head(explode(':', $middleware));
yield $stripped;
$interfaces = @class_implements($stripped);
if ($interfaces !== false) {
foreach ($interfaces as $interface) {
yield $interface;
}
}
$parents = @class_parents($stripped);
if ($parents !== false) {
foreach ($parents as $parent) {
yield $parent;
}
}
}
/**
* Splice a middleware into a new position and remove the old entry.
*
* @param array $middlewares
* @param int $from
* @param int $to
* @return array
*/
protected function moveMiddleware($middlewares, $from, $to)
{
array_splice($middlewares, $to, 0, $middlewares[$from]);
unset($middlewares[$from + 1]);
return $middlewares;
}
}
framework/src/Illuminate/Routing/RouteRegistrar.php 0000644 00000020607 15242753746 0016600 0 ustar 00 'as',
'scopeBindings' => 'scope_bindings',
'withoutMiddleware' => 'excluded_middleware',
];
/**
* Create a new route registrar instance.
*
* @param \Illuminate\Routing\Router $router
* @return void
*/
public function __construct(Router $router)
{
$this->router = $router;
}
/**
* Set the value for a given attribute.
*
* @param string $key
* @param mixed $value
* @return $this
*
* @throws \InvalidArgumentException
*/
public function attribute($key, $value)
{
if (! in_array($key, $this->allowedAttributes)) {
throw new InvalidArgumentException("Attribute [{$key}] does not exist.");
}
if ($key === 'middleware') {
foreach ($value as $index => $middleware) {
$value[$index] = (string) $middleware;
}
}
$attributeKey = Arr::get($this->aliases, $key, $key);
if ($key === 'withoutMiddleware') {
$value = array_merge(
(array) ($this->attributes[$attributeKey] ?? []), Arr::wrap($value)
);
}
$this->attributes[$attributeKey] = $value;
return $this;
}
/**
* Route a resource to a controller.
*
* @param string $name
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\PendingResourceRegistration
*/
public function resource($name, $controller, array $options = [])
{
return $this->router->resource($name, $controller, $this->attributes + $options);
}
/**
* Route an API resource to a controller.
*
* @param string $name
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\PendingResourceRegistration
*/
public function apiResource($name, $controller, array $options = [])
{
return $this->router->apiResource($name, $controller, $this->attributes + $options);
}
/**
* Route a singleton resource to a controller.
*
* @param string $name
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\PendingSingletonResourceRegistration
*/
public function singleton($name, $controller, array $options = [])
{
return $this->router->singleton($name, $controller, $this->attributes + $options);
}
/**
* Route an API singleton resource to a controller.
*
* @param string $name
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\PendingSingletonResourceRegistration
*/
public function apiSingleton($name, $controller, array $options = [])
{
return $this->router->apiSingleton($name, $controller, $this->attributes + $options);
}
/**
* Create a route group with shared attributes.
*
* @param \Closure|array|string $callback
* @return $this
*/
public function group($callback)
{
$this->router->group($this->attributes, $callback);
return $this;
}
/**
* Register a new route with the given verbs.
*
* @param array|string $methods
* @param string $uri
* @param \Closure|array|string|null $action
* @return \Illuminate\Routing\Route
*/
public function match($methods, $uri, $action = null)
{
return $this->router->match($methods, $uri, $this->compileAction($action));
}
/**
* Register a new route with the router.
*
* @param string $method
* @param string $uri
* @param \Closure|array|string|null $action
* @return \Illuminate\Routing\Route
*/
protected function registerRoute($method, $uri, $action = null)
{
if (! is_array($action)) {
$action = array_merge($this->attributes, $action ? ['uses' => $action] : []);
}
return $this->router->{$method}($uri, $this->compileAction($action));
}
/**
* Compile the action into an array including the attributes.
*
* @param \Closure|array|string|null $action
* @return array
*/
protected function compileAction($action)
{
if (is_null($action)) {
return $this->attributes;
}
if (is_string($action) || $action instanceof Closure) {
$action = ['uses' => $action];
}
if (is_array($action) &&
array_is_list($action) &&
Reflector::isCallable($action)) {
if (strncmp($action[0], '\\', 1)) {
$action[0] = '\\'.$action[0];
}
$action = [
'uses' => $action[0].'@'.$action[1],
'controller' => $action[0].'@'.$action[1],
];
}
return array_merge($this->attributes, $action);
}
/**
* Dynamically handle calls into the route registrar.
*
* @param string $method
* @param array $parameters
* @return \Illuminate\Routing\Route|$this
*
* @throws \BadMethodCallException
*/
public function __call($method, $parameters)
{
if (in_array($method, $this->passthru)) {
return $this->registerRoute($method, ...$parameters);
}
if (in_array($method, $this->allowedAttributes)) {
if ($method === 'middleware') {
return $this->attribute($method, is_array($parameters[0]) ? $parameters[0] : $parameters);
}
return $this->attribute($method, array_key_exists(0, $parameters) ? $parameters[0] : true);
}
throw new BadMethodCallException(sprintf(
'Method %s::%s does not exist.', static::class, $method
));
}
}
framework/src/Illuminate/Routing/RouteGroup.php 0000644 00000005262 15242753746 0015732 0 ustar 00 static::formatNamespace($new, $old),
'prefix' => static::formatPrefix($new, $old, $prependExistingPrefix),
'where' => static::formatWhere($new, $old),
]);
return array_merge_recursive(Arr::except(
$old, ['namespace', 'prefix', 'where', 'as']
), $new);
}
/**
* Format the namespace for the new group attributes.
*
* @param array $new
* @param array $old
* @return string|null
*/
protected static function formatNamespace($new, $old)
{
if (isset($new['namespace'])) {
return isset($old['namespace']) && ! str_starts_with($new['namespace'], '\\')
? trim($old['namespace'], '\\').'\\'.trim($new['namespace'], '\\')
: trim($new['namespace'], '\\');
}
return $old['namespace'] ?? null;
}
/**
* Format the prefix for the new group attributes.
*
* @param array $new
* @param array $old
* @param bool $prependExistingPrefix
* @return string|null
*/
protected static function formatPrefix($new, $old, $prependExistingPrefix = true)
{
$old = $old['prefix'] ?? '';
if ($prependExistingPrefix) {
return isset($new['prefix']) ? trim($old, '/').'/'.trim($new['prefix'], '/') : $old;
}
return isset($new['prefix']) ? trim($new['prefix'], '/').'/'.trim($old, '/') : $old;
}
/**
* Format the "wheres" for the new group attributes.
*
* @param array $new
* @param array $old
* @return array
*/
protected static function formatWhere($new, $old)
{
return array_merge(
$old['where'] ?? [],
$new['where'] ?? []
);
}
/**
* Format the "as" clause of the new group attributes.
*
* @param array $new
* @param array $old
* @return array
*/
protected static function formatAs($new, $old)
{
if (isset($old['as'])) {
$new['as'] = $old['as'].($new['as'] ?? '');
}
return $new;
}
}
framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php 0000644 00000023520 15242753746 0021232 0 ustar 00 limiter = $limiter;
}
/**
* Specify the named rate limiter to use for the middleware.
*
* @param string $name
* @return string
*/
public static function using($name)
{
return static::class.':'.$name;
}
/**
* Specify the rate limiter configuration for the middleware.
*
* @param int $maxAttempts
* @param int $decayMinutes
* @param string $prefix
* @return string
*
* @named-arguments-supported
*/
public static function with($maxAttempts = 60, $decayMinutes = 1, $prefix = '')
{
return static::class.':'.implode(',', func_get_args());
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param int|string $maxAttempts
* @param float|int $decayMinutes
* @param string $prefix
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \Illuminate\Http\Exceptions\ThrottleRequestsException
*/
public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1, $prefix = '')
{
if (is_string($maxAttempts)
&& func_num_args() === 3
&& ! is_null($limiter = $this->limiter->limiter($maxAttempts))) {
return $this->handleRequestUsingNamedLimiter($request, $next, $maxAttempts, $limiter);
}
return $this->handleRequest(
$request,
$next,
[
(object) [
'key' => $prefix.$this->resolveRequestSignature($request),
'maxAttempts' => $this->resolveMaxAttempts($request, $maxAttempts),
'decayMinutes' => $decayMinutes,
'responseCallback' => null,
],
]
);
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string $limiterName
* @param \Closure $limiter
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \Illuminate\Http\Exceptions\ThrottleRequestsException
*/
protected function handleRequestUsingNamedLimiter($request, Closure $next, $limiterName, Closure $limiter)
{
$limiterResponse = $limiter($request);
if ($limiterResponse instanceof Response) {
return $limiterResponse;
} elseif ($limiterResponse instanceof Unlimited) {
return $next($request);
}
return $this->handleRequest(
$request,
$next,
collect(Arr::wrap($limiterResponse))->map(function ($limit) use ($limiterName) {
return (object) [
'key' => self::$shouldHashKeys ? md5($limiterName.$limit->key) : $limiterName.':'.$limit->key,
'maxAttempts' => $limit->maxAttempts,
'decayMinutes' => $limit->decayMinutes,
'responseCallback' => $limit->responseCallback,
];
})->all()
);
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param array $limits
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \Illuminate\Http\Exceptions\ThrottleRequestsException
*/
protected function handleRequest($request, Closure $next, array $limits)
{
foreach ($limits as $limit) {
if ($this->limiter->tooManyAttempts($limit->key, $limit->maxAttempts)) {
throw $this->buildException($request, $limit->key, $limit->maxAttempts, $limit->responseCallback);
}
$this->limiter->hit($limit->key, $limit->decayMinutes * 60);
}
$response = $next($request);
foreach ($limits as $limit) {
$response = $this->addHeaders(
$response,
$limit->maxAttempts,
$this->calculateRemainingAttempts($limit->key, $limit->maxAttempts)
);
}
return $response;
}
/**
* Resolve the number of attempts if the user is authenticated or not.
*
* @param \Illuminate\Http\Request $request
* @param int|string $maxAttempts
* @return int
*/
protected function resolveMaxAttempts($request, $maxAttempts)
{
if (str_contains($maxAttempts, '|')) {
$maxAttempts = explode('|', $maxAttempts, 2)[$request->user() ? 1 : 0];
}
if (! is_numeric($maxAttempts) && $request->user()) {
$maxAttempts = $request->user()->{$maxAttempts};
}
return (int) $maxAttempts;
}
/**
* Resolve request signature.
*
* @param \Illuminate\Http\Request $request
* @return string
*
* @throws \RuntimeException
*/
protected function resolveRequestSignature($request)
{
if ($user = $request->user()) {
return $this->formatIdentifier($user->getAuthIdentifier());
} elseif ($route = $request->route()) {
return $this->formatIdentifier($route->getDomain().'|'.$request->ip());
}
throw new RuntimeException('Unable to generate the request signature. Route unavailable.');
}
/**
* Create a 'too many attempts' exception.
*
* @param \Illuminate\Http\Request $request
* @param string $key
* @param int $maxAttempts
* @param callable|null $responseCallback
* @return \Illuminate\Http\Exceptions\ThrottleRequestsException|\Illuminate\Http\Exceptions\HttpResponseException
*/
protected function buildException($request, $key, $maxAttempts, $responseCallback = null)
{
$retryAfter = $this->getTimeUntilNextRetry($key);
$headers = $this->getHeaders(
$maxAttempts,
$this->calculateRemainingAttempts($key, $maxAttempts, $retryAfter),
$retryAfter
);
return is_callable($responseCallback)
? new HttpResponseException($responseCallback($request, $headers))
: new ThrottleRequestsException('Too Many Attempts.', null, $headers);
}
/**
* Get the number of seconds until the next retry.
*
* @param string $key
* @return int
*/
protected function getTimeUntilNextRetry($key)
{
return $this->limiter->availableIn($key);
}
/**
* Add the limit header information to the given response.
*
* @param \Symfony\Component\HttpFoundation\Response $response
* @param int $maxAttempts
* @param int $remainingAttempts
* @param int|null $retryAfter
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function addHeaders(Response $response, $maxAttempts, $remainingAttempts, $retryAfter = null)
{
$response->headers->add(
$this->getHeaders($maxAttempts, $remainingAttempts, $retryAfter, $response)
);
return $response;
}
/**
* Get the limit headers information.
*
* @param int $maxAttempts
* @param int $remainingAttempts
* @param int|null $retryAfter
* @param \Symfony\Component\HttpFoundation\Response|null $response
* @return array
*/
protected function getHeaders($maxAttempts,
$remainingAttempts,
$retryAfter = null,
?Response $response = null)
{
if ($response &&
! is_null($response->headers->get('X-RateLimit-Remaining')) &&
(int) $response->headers->get('X-RateLimit-Remaining') <= (int) $remainingAttempts) {
return [];
}
$headers = [
'X-RateLimit-Limit' => $maxAttempts,
'X-RateLimit-Remaining' => $remainingAttempts,
];
if (! is_null($retryAfter)) {
$headers['Retry-After'] = $retryAfter;
$headers['X-RateLimit-Reset'] = $this->availableAt($retryAfter);
}
return $headers;
}
/**
* Calculate the number of remaining attempts.
*
* @param string $key
* @param int $maxAttempts
* @param int|null $retryAfter
* @return int
*/
protected function calculateRemainingAttempts($key, $maxAttempts, $retryAfter = null)
{
return is_null($retryAfter) ? $this->limiter->retriesLeft($key, $maxAttempts) : 0;
}
/**
* Format the given identifier based on the configured hashing settings.
*
* @param string $value
* @return string
*/
private function formatIdentifier($value)
{
return self::$shouldHashKeys ? sha1($value) : $value;
}
/**
* Specify whether rate limiter keys should be hashed.
*
* @param bool $shouldHashKeys
* @return void
*/
public static function shouldHashKeys(bool $shouldHashKeys = true)
{
self::$shouldHashKeys = $shouldHashKeys;
}
}
framework/src/Illuminate/Routing/Middleware/ValidateSignature.php 0000644 00000004157 15242753746 0021311 0 ustar 00
*/
protected $ignore = [
//
];
/**
* Specify that the URL signature is for a relative URL.
*
* @param array|string $ignore
* @return string
*/
public static function relative($ignore = [])
{
$ignore = Arr::wrap($ignore);
return static::class.':'.implode(',', empty($ignore) ? ['relative'] : ['relative', ...$ignore]);
}
/**
* Specify that the URL signature is for an absolute URL.
*
* @param array|string $ignore
* @return class-string
*/
public static function absolute($ignore = [])
{
$ignore = Arr::wrap($ignore);
return empty($ignore)
? static::class
: static::class.':'.implode(',', $ignore);
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param array|null $args
* @return \Illuminate\Http\Response
*
* @throws \Illuminate\Routing\Exceptions\InvalidSignatureException
*/
public function handle($request, Closure $next, ...$args)
{
[$relative, $ignore] = $this->parseArguments($args);
if ($request->hasValidSignatureWhileIgnoring($ignore, ! $relative)) {
return $next($request);
}
throw new InvalidSignatureException;
}
/**
* Parse the additional arguments given to the middleware.
*
* @param array $args
* @return array
*/
protected function parseArguments(array $args)
{
$relative = ! empty($args) && $args[0] === 'relative';
if ($relative) {
array_shift($args);
}
$ignore = array_merge(
property_exists($this, 'except') ? $this->except : $this->ignore,
$args
);
return [$relative, $ignore];
}
}
framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php 0000644 00000002250 15242753746 0021517 0 ustar 00 router = $router;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
try {
$this->router->substituteBindings($route = $request->route());
$this->router->substituteImplicitBindings($route);
} catch (ModelNotFoundException $exception) {
if ($route->getMissing()) {
return $route->getMissing()($request, $exception);
}
throw $exception;
}
return $next($request);
}
}
framework/src/Illuminate/Routing/Middleware/ThrottleRequestsWithRedis.php 0000644 00000006640 15242753746 0023061 0 ustar 00 redis = $redis;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param array $limits
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \Illuminate\Http\Exceptions\ThrottleRequestsException
*/
protected function handleRequest($request, Closure $next, array $limits)
{
foreach ($limits as $limit) {
if ($this->tooManyAttempts($limit->key, $limit->maxAttempts, $limit->decayMinutes)) {
throw $this->buildException($request, $limit->key, $limit->maxAttempts, $limit->responseCallback);
}
}
$response = $next($request);
foreach ($limits as $limit) {
$response = $this->addHeaders(
$response,
$limit->maxAttempts,
$this->calculateRemainingAttempts($limit->key, $limit->maxAttempts)
);
}
return $response;
}
/**
* Determine if the given key has been "accessed" too many times.
*
* @param string $key
* @param int $maxAttempts
* @param int $decayMinutes
* @return mixed
*/
protected function tooManyAttempts($key, $maxAttempts, $decayMinutes)
{
$limiter = new DurationLimiter(
$this->getRedisConnection(), $key, $maxAttempts, $decayMinutes * 60
);
return tap(! $limiter->acquire(), function () use ($key, $limiter) {
[$this->decaysAt[$key], $this->remaining[$key]] = [
$limiter->decaysAt, $limiter->remaining,
];
});
}
/**
* Calculate the number of remaining attempts.
*
* @param string $key
* @param int $maxAttempts
* @param int|null $retryAfter
* @return int
*/
protected function calculateRemainingAttempts($key, $maxAttempts, $retryAfter = null)
{
return is_null($retryAfter) ? $this->remaining[$key] : 0;
}
/**
* Get the number of seconds until the lock is released.
*
* @param string $key
* @return int
*/
protected function getTimeUntilNextRetry($key)
{
return $this->decaysAt[$key] - $this->currentTime();
}
/**
* Get the Redis connection that should be used for throttling.
*
* @return \Illuminate\Redis\Connections\Connection
*/
protected function getRedisConnection()
{
return $this->redis->connection();
}
}
framework/src/Illuminate/Routing/Router.php 0000644 00000117455 15242753746 0015107 0 ustar 00 events = $events;
$this->routes = new RouteCollection;
$this->container = $container ?: new Container;
}
/**
* Register a new GET route with the router.
*
* @param string $uri
* @param array|string|callable|null $action
* @return \Illuminate\Routing\Route
*/
public function get($uri, $action = null)
{
return $this->addRoute(['GET', 'HEAD'], $uri, $action);
}
/**
* Register a new POST route with the router.
*
* @param string $uri
* @param array|string|callable|null $action
* @return \Illuminate\Routing\Route
*/
public function post($uri, $action = null)
{
return $this->addRoute('POST', $uri, $action);
}
/**
* Register a new PUT route with the router.
*
* @param string $uri
* @param array|string|callable|null $action
* @return \Illuminate\Routing\Route
*/
public function put($uri, $action = null)
{
return $this->addRoute('PUT', $uri, $action);
}
/**
* Register a new PATCH route with the router.
*
* @param string $uri
* @param array|string|callable|null $action
* @return \Illuminate\Routing\Route
*/
public function patch($uri, $action = null)
{
return $this->addRoute('PATCH', $uri, $action);
}
/**
* Register a new DELETE route with the router.
*
* @param string $uri
* @param array|string|callable|null $action
* @return \Illuminate\Routing\Route
*/
public function delete($uri, $action = null)
{
return $this->addRoute('DELETE', $uri, $action);
}
/**
* Register a new OPTIONS route with the router.
*
* @param string $uri
* @param array|string|callable|null $action
* @return \Illuminate\Routing\Route
*/
public function options($uri, $action = null)
{
return $this->addRoute('OPTIONS', $uri, $action);
}
/**
* Register a new route responding to all verbs.
*
* @param string $uri
* @param array|string|callable|null $action
* @return \Illuminate\Routing\Route
*/
public function any($uri, $action = null)
{
return $this->addRoute(self::$verbs, $uri, $action);
}
/**
* Register a new fallback route with the router.
*
* @param array|string|callable|null $action
* @return \Illuminate\Routing\Route
*/
public function fallback($action)
{
$placeholder = 'fallbackPlaceholder';
return $this->addRoute(
'GET', "{{$placeholder}}", $action
)->where($placeholder, '.*')->fallback();
}
/**
* Create a redirect from one URI to another.
*
* @param string $uri
* @param string $destination
* @param int $status
* @return \Illuminate\Routing\Route
*/
public function redirect($uri, $destination, $status = 302)
{
return $this->any($uri, '\Illuminate\Routing\RedirectController')
->defaults('destination', $destination)
->defaults('status', $status);
}
/**
* Create a permanent redirect from one URI to another.
*
* @param string $uri
* @param string $destination
* @return \Illuminate\Routing\Route
*/
public function permanentRedirect($uri, $destination)
{
return $this->redirect($uri, $destination, 301);
}
/**
* Register a new route that returns a view.
*
* @param string $uri
* @param string $view
* @param array $data
* @param int|array $status
* @param array $headers
* @return \Illuminate\Routing\Route
*/
public function view($uri, $view, $data = [], $status = 200, array $headers = [])
{
return $this->match(['GET', 'HEAD'], $uri, '\Illuminate\Routing\ViewController')
->setDefaults([
'view' => $view,
'data' => $data,
'status' => is_array($status) ? 200 : $status,
'headers' => is_array($status) ? $status : $headers,
]);
}
/**
* Register a new route with the given verbs.
*
* @param array|string $methods
* @param string $uri
* @param array|string|callable|null $action
* @return \Illuminate\Routing\Route
*/
public function match($methods, $uri, $action = null)
{
return $this->addRoute(array_map('strtoupper', (array) $methods), $uri, $action);
}
/**
* Register an array of resource controllers.
*
* @param array $resources
* @param array $options
* @return void
*/
public function resources(array $resources, array $options = [])
{
foreach ($resources as $name => $controller) {
$this->resource($name, $controller, $options);
}
}
/**
* Route a resource to a controller.
*
* @param string $name
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\PendingResourceRegistration
*/
public function resource($name, $controller, array $options = [])
{
if ($this->container && $this->container->bound(ResourceRegistrar::class)) {
$registrar = $this->container->make(ResourceRegistrar::class);
} else {
$registrar = new ResourceRegistrar($this);
}
return new PendingResourceRegistration(
$registrar, $name, $controller, $options
);
}
/**
* Register an array of API resource controllers.
*
* @param array $resources
* @param array $options
* @return void
*/
public function apiResources(array $resources, array $options = [])
{
foreach ($resources as $name => $controller) {
$this->apiResource($name, $controller, $options);
}
}
/**
* Route an API resource to a controller.
*
* @param string $name
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\PendingResourceRegistration
*/
public function apiResource($name, $controller, array $options = [])
{
$only = ['index', 'show', 'store', 'update', 'destroy'];
if (isset($options['except'])) {
$only = array_diff($only, (array) $options['except']);
}
return $this->resource($name, $controller, array_merge([
'only' => $only,
], $options));
}
/**
* Register an array of singleton resource controllers.
*
* @param array $singletons
* @param array $options
* @return void
*/
public function singletons(array $singletons, array $options = [])
{
foreach ($singletons as $name => $controller) {
$this->singleton($name, $controller, $options);
}
}
/**
* Route a singleton resource to a controller.
*
* @param string $name
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\PendingSingletonResourceRegistration
*/
public function singleton($name, $controller, array $options = [])
{
if ($this->container && $this->container->bound(ResourceRegistrar::class)) {
$registrar = $this->container->make(ResourceRegistrar::class);
} else {
$registrar = new ResourceRegistrar($this);
}
return new PendingSingletonResourceRegistration(
$registrar, $name, $controller, $options
);
}
/**
* Register an array of API singleton resource controllers.
*
* @param array $singletons
* @param array $options
* @return void
*/
public function apiSingletons(array $singletons, array $options = [])
{
foreach ($singletons as $name => $controller) {
$this->apiSingleton($name, $controller, $options);
}
}
/**
* Route an API singleton resource to a controller.
*
* @param string $name
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\PendingSingletonResourceRegistration
*/
public function apiSingleton($name, $controller, array $options = [])
{
$only = ['store', 'show', 'update', 'destroy'];
if (isset($options['except'])) {
$only = array_diff($only, (array) $options['except']);
}
return $this->singleton($name, $controller, array_merge([
'only' => $only,
], $options));
}
/**
* Create a route group with shared attributes.
*
* @param array $attributes
* @param \Closure|array|string $routes
* @return $this
*/
public function group(array $attributes, $routes)
{
foreach (Arr::wrap($routes) as $groupRoutes) {
$this->updateGroupStack($attributes);
// Once we have updated the group stack, we'll load the provided routes and
// merge in the group's attributes when the routes are created. After we
// have created the routes, we will pop the attributes off the stack.
$this->loadRoutes($groupRoutes);
array_pop($this->groupStack);
}
return $this;
}
/**
* Update the group stack with the given attributes.
*
* @param array $attributes
* @return void
*/
protected function updateGroupStack(array $attributes)
{
if ($this->hasGroupStack()) {
$attributes = $this->mergeWithLastGroup($attributes);
}
$this->groupStack[] = $attributes;
}
/**
* Merge the given array with the last group stack.
*
* @param array $new
* @param bool $prependExistingPrefix
* @return array
*/
public function mergeWithLastGroup($new, $prependExistingPrefix = true)
{
return RouteGroup::merge($new, end($this->groupStack), $prependExistingPrefix);
}
/**
* Load the provided routes.
*
* @param \Closure|string $routes
* @return void
*/
protected function loadRoutes($routes)
{
if ($routes instanceof Closure) {
$routes($this);
} else {
(new RouteFileRegistrar($this))->register($routes);
}
}
/**
* Get the prefix from the last group on the stack.
*
* @return string
*/
public function getLastGroupPrefix()
{
if ($this->hasGroupStack()) {
$last = end($this->groupStack);
return $last['prefix'] ?? '';
}
return '';
}
/**
* Add a route to the underlying route collection.
*
* @param array|string $methods
* @param string $uri
* @param array|string|callable|null $action
* @return \Illuminate\Routing\Route
*/
public function addRoute($methods, $uri, $action)
{
return $this->routes->add($this->createRoute($methods, $uri, $action));
}
/**
* Create a new route instance.
*
* @param array|string $methods
* @param string $uri
* @param mixed $action
* @return \Illuminate\Routing\Route
*/
protected function createRoute($methods, $uri, $action)
{
// If the route is routing to a controller we will parse the route action into
// an acceptable array format before registering it and creating this route
// instance itself. We need to build the Closure that will call this out.
if ($this->actionReferencesController($action)) {
$action = $this->convertToControllerAction($action);
}
$route = $this->newRoute(
$methods, $this->prefix($uri), $action
);
// If we have groups that need to be merged, we will merge them now after this
// route has already been created and is ready to go. After we're done with
// the merge we will be ready to return the route back out to the caller.
if ($this->hasGroupStack()) {
$this->mergeGroupAttributesIntoRoute($route);
}
$this->addWhereClausesToRoute($route);
return $route;
}
/**
* Determine if the action is routing to a controller.
*
* @param mixed $action
* @return bool
*/
protected function actionReferencesController($action)
{
if (! $action instanceof Closure) {
return is_string($action) || (isset($action['uses']) && is_string($action['uses']));
}
return false;
}
/**
* Add a controller based route action to the action array.
*
* @param array|string $action
* @return array
*/
protected function convertToControllerAction($action)
{
if (is_string($action)) {
$action = ['uses' => $action];
}
// Here we'll merge any group "controller" and "uses" statements if necessary so that
// the action has the proper clause for this property. Then, we can simply set the
// name of this controller on the action plus return the action array for usage.
if ($this->hasGroupStack()) {
$action['uses'] = $this->prependGroupController($action['uses']);
$action['uses'] = $this->prependGroupNamespace($action['uses']);
}
// Here we will set this controller name on the action array just so we always
// have a copy of it for reference if we need it. This can be used while we
// search for a controller name or do some other type of fetch operation.
$action['controller'] = $action['uses'];
return $action;
}
/**
* Prepend the last group namespace onto the use clause.
*
* @param string $class
* @return string
*/
protected function prependGroupNamespace($class)
{
$group = end($this->groupStack);
return isset($group['namespace']) && ! str_starts_with($class, '\\') && ! str_starts_with($class, $group['namespace'])
? $group['namespace'].'\\'.$class : $class;
}
/**
* Prepend the last group controller onto the use clause.
*
* @param string $class
* @return string
*/
protected function prependGroupController($class)
{
$group = end($this->groupStack);
if (! isset($group['controller'])) {
return $class;
}
if (class_exists($class)) {
return $class;
}
if (str_contains($class, '@')) {
return $class;
}
return $group['controller'].'@'.$class;
}
/**
* Create a new Route object.
*
* @param array|string $methods
* @param string $uri
* @param mixed $action
* @return \Illuminate\Routing\Route
*/
public function newRoute($methods, $uri, $action)
{
return (new Route($methods, $uri, $action))
->setRouter($this)
->setContainer($this->container);
}
/**
* Prefix the given URI with the last prefix.
*
* @param string $uri
* @return string
*/
protected function prefix($uri)
{
return trim(trim($this->getLastGroupPrefix(), '/').'/'.trim($uri, '/'), '/') ?: '/';
}
/**
* Add the necessary where clauses to the route based on its initial registration.
*
* @param \Illuminate\Routing\Route $route
* @return \Illuminate\Routing\Route
*/
protected function addWhereClausesToRoute($route)
{
$route->where(array_merge(
$this->patterns, $route->getAction()['where'] ?? []
));
return $route;
}
/**
* Merge the group stack with the controller action.
*
* @param \Illuminate\Routing\Route $route
* @return void
*/
protected function mergeGroupAttributesIntoRoute($route)
{
$route->setAction($this->mergeWithLastGroup(
$route->getAction(),
$prependExistingPrefix = false
));
}
/**
* Return the response returned by the given route.
*
* @param string $name
* @return \Symfony\Component\HttpFoundation\Response
*/
public function respondWithRoute($name)
{
$route = tap($this->routes->getByName($name))->bind($this->currentRequest);
return $this->runRoute($this->currentRequest, $route);
}
/**
* Dispatch the request to the application.
*
* @param \Illuminate\Http\Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function dispatch(Request $request)
{
$this->currentRequest = $request;
return $this->dispatchToRoute($request);
}
/**
* Dispatch the request to a route and return the response.
*
* @param \Illuminate\Http\Request $request
* @return \Symfony\Component\HttpFoundation\Response
*/
public function dispatchToRoute(Request $request)
{
return $this->runRoute($request, $this->findRoute($request));
}
/**
* Find the route matching a given request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Routing\Route
*/
protected function findRoute($request)
{
$this->events->dispatch(new Routing($request));
$this->current = $route = $this->routes->match($request);
$route->setContainer($this->container);
$this->container->instance(Route::class, $route);
return $route;
}
/**
* Return the response for the given route.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Routing\Route $route
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function runRoute(Request $request, Route $route)
{
$request->setRouteResolver(fn () => $route);
$this->events->dispatch(new RouteMatched($route, $request));
return $this->prepareResponse($request,
$this->runRouteWithinStack($route, $request)
);
}
/**
* Run the given route within a Stack "onion" instance.
*
* @param \Illuminate\Routing\Route $route
* @param \Illuminate\Http\Request $request
* @return mixed
*/
protected function runRouteWithinStack(Route $route, Request $request)
{
$shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
$this->container->make('middleware.disable') === true;
$middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route);
return (new Pipeline($this->container))
->send($request)
->through($middleware)
->then(fn ($request) => $this->prepareResponse(
$request, $route->run()
));
}
/**
* Gather the middleware for the given route with resolved class names.
*
* @param \Illuminate\Routing\Route $route
* @return array
*/
public function gatherRouteMiddleware(Route $route)
{
return $this->resolveMiddleware($route->gatherMiddleware(), $route->excludedMiddleware());
}
/**
* Resolve a flat array of middleware classes from the provided array.
*
* @param array $middleware
* @param array $excluded
* @return array
*/
public function resolveMiddleware(array $middleware, array $excluded = [])
{
$excluded = collect($excluded)->map(function ($name) {
return (array) MiddlewareNameResolver::resolve($name, $this->middleware, $this->middlewareGroups);
})->flatten()->values()->all();
$middleware = collect($middleware)->map(function ($name) {
return (array) MiddlewareNameResolver::resolve($name, $this->middleware, $this->middlewareGroups);
})->flatten()->reject(function ($name) use ($excluded) {
if (empty($excluded)) {
return false;
}
if ($name instanceof Closure) {
return false;
}
if (in_array($name, $excluded, true)) {
return true;
}
if (! class_exists($name)) {
return false;
}
$reflection = new ReflectionClass($name);
return collect($excluded)->contains(
fn ($exclude) => class_exists($exclude) && $reflection->isSubclassOf($exclude)
);
})->values();
return $this->sortMiddleware($middleware);
}
/**
* Sort the given middleware by priority.
*
* @param \Illuminate\Support\Collection $middlewares
* @return array
*/
protected function sortMiddleware(Collection $middlewares)
{
return (new SortedMiddleware($this->middlewarePriority, $middlewares))->all();
}
/**
* Create a response instance from the given value.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* @param mixed $response
* @return \Symfony\Component\HttpFoundation\Response
*/
public function prepareResponse($request, $response)
{
$this->events->dispatch(new PreparingResponse($request, $response));
return tap(static::toResponse($request, $response), function ($response) use ($request) {
$this->events->dispatch(new ResponsePrepared($request, $response));
});
}
/**
* Static version of prepareResponse.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* @param mixed $response
* @return \Symfony\Component\HttpFoundation\Response
*/
public static function toResponse($request, $response)
{
if ($response instanceof Responsable) {
$response = $response->toResponse($request);
}
if ($response instanceof PsrResponseInterface) {
$response = (new HttpFoundationFactory)->createResponse($response);
} elseif ($response instanceof Model && $response->wasRecentlyCreated) {
$response = new JsonResponse($response, 201);
} elseif ($response instanceof Stringable) {
$response = new Response($response->__toString(), 200, ['Content-Type' => 'text/html']);
} elseif (! $response instanceof SymfonyResponse &&
($response instanceof Arrayable ||
$response instanceof Jsonable ||
$response instanceof ArrayObject ||
$response instanceof JsonSerializable ||
$response instanceof stdClass ||
is_array($response))) {
$response = new JsonResponse($response);
} elseif (! $response instanceof SymfonyResponse) {
$response = new Response($response, 200, ['Content-Type' => 'text/html']);
}
if ($response->getStatusCode() === Response::HTTP_NOT_MODIFIED) {
$response->setNotModified();
}
return $response->prepare($request);
}
/**
* Substitute the route bindings onto the route.
*
* @param \Illuminate\Routing\Route $route
* @return \Illuminate\Routing\Route
*
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
* @throws \Illuminate\Routing\Exceptions\BackedEnumCaseNotFoundException
*/
public function substituteBindings($route)
{
foreach ($route->parameters() as $key => $value) {
if (isset($this->binders[$key])) {
$route->setParameter($key, $this->performBinding($key, $value, $route));
}
}
return $route;
}
/**
* Substitute the implicit route bindings for the given route.
*
* @param \Illuminate\Routing\Route $route
* @return void
*
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
* @throws \Illuminate\Routing\Exceptions\BackedEnumCaseNotFoundException
*/
public function substituteImplicitBindings($route)
{
$default = fn () => ImplicitRouteBinding::resolveForRoute($this->container, $route);
return call_user_func(
$this->implicitBindingCallback ?? $default, $this->container, $route, $default
);
}
/**
* Register a callback to to run after implicit bindings are substituted.
*
* @param callable $callback
* @return $this
*/
public function substituteImplicitBindingsUsing($callback)
{
$this->implicitBindingCallback = $callback;
return $this;
}
/**
* Call the binding callback for the given key.
*
* @param string $key
* @param string $value
* @param \Illuminate\Routing\Route $route
* @return mixed
*
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
*/
protected function performBinding($key, $value, $route)
{
return call_user_func($this->binders[$key], $value, $route);
}
/**
* Register a route matched event listener.
*
* @param string|callable $callback
* @return void
*/
public function matched($callback)
{
$this->events->listen(Events\RouteMatched::class, $callback);
}
/**
* Get all of the defined middleware short-hand names.
*
* @return array
*/
public function getMiddleware()
{
return $this->middleware;
}
/**
* Register a short-hand name for a middleware.
*
* @param string $name
* @param string $class
* @return $this
*/
public function aliasMiddleware($name, $class)
{
$this->middleware[$name] = $class;
return $this;
}
/**
* Check if a middlewareGroup with the given name exists.
*
* @param string $name
* @return bool
*/
public function hasMiddlewareGroup($name)
{
return array_key_exists($name, $this->middlewareGroups);
}
/**
* Get all of the defined middleware groups.
*
* @return array
*/
public function getMiddlewareGroups()
{
return $this->middlewareGroups;
}
/**
* Register a group of middleware.
*
* @param string $name
* @param array $middleware
* @return $this
*/
public function middlewareGroup($name, array $middleware)
{
$this->middlewareGroups[$name] = $middleware;
return $this;
}
/**
* Add a middleware to the beginning of a middleware group.
*
* If the middleware is already in the group, it will not be added again.
*
* @param string $group
* @param string $middleware
* @return $this
*/
public function prependMiddlewareToGroup($group, $middleware)
{
if (isset($this->middlewareGroups[$group]) && ! in_array($middleware, $this->middlewareGroups[$group])) {
array_unshift($this->middlewareGroups[$group], $middleware);
}
return $this;
}
/**
* Add a middleware to the end of a middleware group.
*
* If the middleware is already in the group, it will not be added again.
*
* @param string $group
* @param string $middleware
* @return $this
*/
public function pushMiddlewareToGroup($group, $middleware)
{
if (! array_key_exists($group, $this->middlewareGroups)) {
$this->middlewareGroups[$group] = [];
}
if (! in_array($middleware, $this->middlewareGroups[$group])) {
$this->middlewareGroups[$group][] = $middleware;
}
return $this;
}
/**
* Remove the given middleware from the specified group.
*
* @param string $group
* @param string $middleware
* @return $this
*/
public function removeMiddlewareFromGroup($group, $middleware)
{
if (! $this->hasMiddlewareGroup($group)) {
return $this;
}
$reversedMiddlewaresArray = array_flip($this->middlewareGroups[$group]);
if (! array_key_exists($middleware, $reversedMiddlewaresArray)) {
return $this;
}
$middlewareKey = $reversedMiddlewaresArray[$middleware];
unset($this->middlewareGroups[$group][$middlewareKey]);
return $this;
}
/**
* Flush the router's middleware groups.
*
* @return $this
*/
public function flushMiddlewareGroups()
{
$this->middlewareGroups = [];
return $this;
}
/**
* Add a new route parameter binder.
*
* @param string $key
* @param string|callable $binder
* @return void
*/
public function bind($key, $binder)
{
$this->binders[str_replace('-', '_', $key)] = RouteBinding::forCallback(
$this->container, $binder
);
}
/**
* Register a model binder for a wildcard.
*
* @param string $key
* @param string $class
* @param \Closure|null $callback
* @return void
*/
public function model($key, $class, Closure $callback = null)
{
$this->bind($key, RouteBinding::forModel($this->container, $class, $callback));
}
/**
* Get the binding callback for a given binding.
*
* @param string $key
* @return \Closure|null
*/
public function getBindingCallback($key)
{
if (isset($this->binders[$key = str_replace('-', '_', $key)])) {
return $this->binders[$key];
}
}
/**
* Get the global "where" patterns.
*
* @return array
*/
public function getPatterns()
{
return $this->patterns;
}
/**
* Set a global where pattern on all routes.
*
* @param string $key
* @param string $pattern
* @return void
*/
public function pattern($key, $pattern)
{
$this->patterns[$key] = $pattern;
}
/**
* Set a group of global where patterns on all routes.
*
* @param array $patterns
* @return void
*/
public function patterns($patterns)
{
foreach ($patterns as $key => $pattern) {
$this->pattern($key, $pattern);
}
}
/**
* Determine if the router currently has a group stack.
*
* @return bool
*/
public function hasGroupStack()
{
return ! empty($this->groupStack);
}
/**
* Get the current group stack for the router.
*
* @return array
*/
public function getGroupStack()
{
return $this->groupStack;
}
/**
* Get a route parameter for the current route.
*
* @param string $key
* @param string|null $default
* @return mixed
*/
public function input($key, $default = null)
{
return $this->current()->parameter($key, $default);
}
/**
* Get the request currently being dispatched.
*
* @return \Illuminate\Http\Request
*/
public function getCurrentRequest()
{
return $this->currentRequest;
}
/**
* Get the currently dispatched route instance.
*
* @return \Illuminate\Routing\Route|null
*/
public function getCurrentRoute()
{
return $this->current();
}
/**
* Get the currently dispatched route instance.
*
* @return \Illuminate\Routing\Route|null
*/
public function current()
{
return $this->current;
}
/**
* Check if a route with the given name exists.
*
* @param string|array $name
* @return bool
*/
public function has($name)
{
$names = is_array($name) ? $name : func_get_args();
foreach ($names as $value) {
if (! $this->routes->hasNamedRoute($value)) {
return false;
}
}
return true;
}
/**
* Get the current route name.
*
* @return string|null
*/
public function currentRouteName()
{
return $this->current() ? $this->current()->getName() : null;
}
/**
* Alias for the "currentRouteNamed" method.
*
* @param mixed ...$patterns
* @return bool
*/
public function is(...$patterns)
{
return $this->currentRouteNamed(...$patterns);
}
/**
* Determine if the current route matches a pattern.
*
* @param mixed ...$patterns
* @return bool
*/
public function currentRouteNamed(...$patterns)
{
return $this->current() && $this->current()->named(...$patterns);
}
/**
* Get the current route action.
*
* @return string|null
*/
public function currentRouteAction()
{
if ($this->current()) {
return $this->current()->getAction()['controller'] ?? null;
}
}
/**
* Alias for the "currentRouteUses" method.
*
* @param array ...$patterns
* @return bool
*/
public function uses(...$patterns)
{
foreach ($patterns as $pattern) {
if (Str::is($pattern, $this->currentRouteAction())) {
return true;
}
}
return false;
}
/**
* Determine if the current route action matches a given action.
*
* @param string $action
* @return bool
*/
public function currentRouteUses($action)
{
return $this->currentRouteAction() == $action;
}
/**
* Set the unmapped global resource parameters to singular.
*
* @param bool $singular
* @return void
*/
public function singularResourceParameters($singular = true)
{
ResourceRegistrar::singularParameters($singular);
}
/**
* Set the global resource parameter mapping.
*
* @param array $parameters
* @return void
*/
public function resourceParameters(array $parameters = [])
{
ResourceRegistrar::setParameters($parameters);
}
/**
* Get or set the verbs used in the resource URIs.
*
* @param array $verbs
* @return array|null
*/
public function resourceVerbs(array $verbs = [])
{
return ResourceRegistrar::verbs($verbs);
}
/**
* Get the underlying route collection.
*
* @return \Illuminate\Routing\RouteCollectionInterface
*/
public function getRoutes()
{
return $this->routes;
}
/**
* Set the route collection instance.
*
* @param \Illuminate\Routing\RouteCollection $routes
* @return void
*/
public function setRoutes(RouteCollection $routes)
{
foreach ($routes as $route) {
$route->setRouter($this)->setContainer($this->container);
}
$this->routes = $routes;
$this->container->instance('routes', $this->routes);
}
/**
* Set the compiled route collection instance.
*
* @param array $routes
* @return void
*/
public function setCompiledRoutes(array $routes)
{
$this->routes = (new CompiledRouteCollection($routes['compiled'], $routes['attributes']))
->setRouter($this)
->setContainer($this->container);
$this->container->instance('routes', $this->routes);
}
/**
* Remove any duplicate middleware from the given array.
*
* @param array $middleware
* @return array
*/
public static function uniqueMiddleware(array $middleware)
{
$seen = [];
$result = [];
foreach ($middleware as $value) {
$key = \is_object($value) ? \spl_object_id($value) : $value;
if (! isset($seen[$key])) {
$seen[$key] = true;
$result[] = $value;
}
}
return $result;
}
/**
* Set the container instance used by the router.
*
* @param \Illuminate\Container\Container $container
* @return $this
*/
public function setContainer(Container $container)
{
$this->container = $container;
return $this;
}
/**
* Dynamically handle calls into the router instance.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
if (static::hasMacro($method)) {
return $this->macroCall($method, $parameters);
}
if ($method === 'middleware') {
return (new RouteRegistrar($this))->attribute($method, is_array($parameters[0]) ? $parameters[0] : $parameters);
}
if ($method !== 'where' && Str::startsWith($method, 'where')) {
return (new RouteRegistrar($this))->{$method}(...$parameters);
}
return (new RouteRegistrar($this))->attribute($method, array_key_exists(0, $parameters) ? $parameters[0] : true);
}
}
framework/src/Illuminate/Routing/ImplicitRouteBinding.php 0000644 00000010603 15242753746 0017676 0 ustar 00
* @throws \Illuminate\Routing\Exceptions\BackedEnumCaseNotFoundException
*/
public static function resolveForRoute($container, $route)
{
$parameters = $route->parameters();
$route = static::resolveBackedEnumsForRoute($route, $parameters);
foreach ($route->signatureParameters(['subClass' => UrlRoutable::class]) as $parameter) {
if (! $parameterName = static::getParameterName($parameter->getName(), $parameters)) {
continue;
}
$parameterValue = $parameters[$parameterName];
if ($parameterValue instanceof UrlRoutable) {
continue;
}
$instance = $container->make(Reflector::getParameterClassName($parameter));
$parent = $route->parentOfParameter($parameterName);
$routeBindingMethod = $route->allowsTrashedBindings() && in_array(SoftDeletes::class, class_uses_recursive($instance))
? 'resolveSoftDeletableRouteBinding'
: 'resolveRouteBinding';
if ($parent instanceof UrlRoutable &&
! $route->preventsScopedBindings() &&
($route->enforcesScopedBindings() || array_key_exists($parameterName, $route->bindingFields()))) {
$childRouteBindingMethod = $route->allowsTrashedBindings() && in_array(SoftDeletes::class, class_uses_recursive($instance))
? 'resolveSoftDeletableChildRouteBinding'
: 'resolveChildRouteBinding';
if (! $model = $parent->{$childRouteBindingMethod}(
$parameterName, $parameterValue, $route->bindingFieldFor($parameterName)
)) {
throw (new ModelNotFoundException)->setModel(get_class($instance), [$parameterValue]);
}
} elseif (! $model = $instance->{$routeBindingMethod}($parameterValue, $route->bindingFieldFor($parameterName))) {
throw (new ModelNotFoundException)->setModel(get_class($instance), [$parameterValue]);
}
$route->setParameter($parameterName, $model);
}
}
/**
* Resolve the Backed Enums route bindings for the route.
*
* @param \Illuminate\Routing\Route $route
* @param array $parameters
* @return \Illuminate\Routing\Route
*
* @throws \Illuminate\Routing\Exceptions\BackedEnumCaseNotFoundException
*/
protected static function resolveBackedEnumsForRoute($route, $parameters)
{
foreach ($route->signatureParameters(['backedEnum' => true]) as $parameter) {
if (! $parameterName = static::getParameterName($parameter->getName(), $parameters)) {
continue;
}
$parameterValue = $parameters[$parameterName];
$backedEnumClass = $parameter->getType()?->getName();
$backedEnum = $parameterValue instanceof $backedEnumClass
? $parameterValue
: $backedEnumClass::tryFrom((string) $parameterValue);
if (is_null($backedEnum)) {
throw new BackedEnumCaseNotFoundException($backedEnumClass, $parameterValue);
}
$route->setParameter($parameterName, $backedEnum);
}
return $route;
}
/**
* Return the parameter name if it exists in the given parameters.
*
* @param string $name
* @param array $parameters
* @return string|null
*/
protected static function getParameterName($name, $parameters)
{
if (array_key_exists($name, $parameters)) {
return $name;
}
if (array_key_exists($snakedName = Str::snake($name), $parameters)) {
return $snakedName;
}
}
}
framework/src/Illuminate/Routing/Console/stubs/controller.model.stub 0000644 00000002241 15242753746 0022023 0 ustar 00 resolveStubPath('/stubs/middleware.stub');
}
/**
* Resolve the fully-qualified path to the stub.
*
* @param string $stub
* @return string
*/
protected function resolveStubPath($stub)
{
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
? $customPath
: __DIR__.$stub;
}
/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
* @return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Http\Middleware';
}
}
framework/src/Illuminate/Routing/Console/ControllerMakeCommand.php 0000644 00000025701 15242753746 0021441 0 ustar 00 option('type')) {
$stub = "/stubs/controller.{$type}.stub";
} elseif ($this->option('parent')) {
$stub = $this->option('singleton')
? '/stubs/controller.nested.singleton.stub'
: '/stubs/controller.nested.stub';
} elseif ($this->option('model')) {
$stub = '/stubs/controller.model.stub';
} elseif ($this->option('invokable')) {
$stub = '/stubs/controller.invokable.stub';
} elseif ($this->option('singleton')) {
$stub = '/stubs/controller.singleton.stub';
} elseif ($this->option('resource')) {
$stub = '/stubs/controller.stub';
}
if ($this->option('api') && is_null($stub)) {
$stub = '/stubs/controller.api.stub';
} elseif ($this->option('api') && ! is_null($stub) && ! $this->option('invokable')) {
$stub = str_replace('.stub', '.api.stub', $stub);
}
$stub ??= '/stubs/controller.plain.stub';
return $this->resolveStubPath($stub);
}
/**
* Resolve the fully-qualified path to the stub.
*
* @param string $stub
* @return string
*/
protected function resolveStubPath($stub)
{
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
? $customPath
: __DIR__.$stub;
}
/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
* @return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Http\Controllers';
}
/**
* Build the class with the given name.
*
* Remove the base controller import if we are already in the base namespace.
*
* @param string $name
* @return string
*/
protected function buildClass($name)
{
$controllerNamespace = $this->getNamespace($name);
$replace = [];
if ($this->option('parent')) {
$replace = $this->buildParentReplacements();
}
if ($this->option('model')) {
$replace = $this->buildModelReplacements($replace);
}
if ($this->option('creatable')) {
$replace['abort(404);'] = '//';
}
$replace["use {$controllerNamespace}\Controller;\n"] = '';
return str_replace(
array_keys($replace), array_values($replace), parent::buildClass($name)
);
}
/**
* Build the replacements for a parent controller.
*
* @return array
*/
protected function buildParentReplacements()
{
$parentModelClass = $this->parseModel($this->option('parent'));
if (! class_exists($parentModelClass) &&
confirm("A {$parentModelClass} model does not exist. Do you want to generate it?", default: true)) {
$this->call('make:model', ['name' => $parentModelClass]);
}
return [
'ParentDummyFullModelClass' => $parentModelClass,
'{{ namespacedParentModel }}' => $parentModelClass,
'{{namespacedParentModel}}' => $parentModelClass,
'ParentDummyModelClass' => class_basename($parentModelClass),
'{{ parentModel }}' => class_basename($parentModelClass),
'{{parentModel}}' => class_basename($parentModelClass),
'ParentDummyModelVariable' => lcfirst(class_basename($parentModelClass)),
'{{ parentModelVariable }}' => lcfirst(class_basename($parentModelClass)),
'{{parentModelVariable}}' => lcfirst(class_basename($parentModelClass)),
];
}
/**
* Build the model replacement values.
*
* @param array $replace
* @return array
*/
protected function buildModelReplacements(array $replace)
{
$modelClass = $this->parseModel($this->option('model'));
if (! class_exists($modelClass) && confirm("A {$modelClass} model does not exist. Do you want to generate it?", default: true)) {
$this->call('make:model', ['name' => $modelClass]);
}
$replace = $this->buildFormRequestReplacements($replace, $modelClass);
return array_merge($replace, [
'DummyFullModelClass' => $modelClass,
'{{ namespacedModel }}' => $modelClass,
'{{namespacedModel}}' => $modelClass,
'DummyModelClass' => class_basename($modelClass),
'{{ model }}' => class_basename($modelClass),
'{{model}}' => class_basename($modelClass),
'DummyModelVariable' => lcfirst(class_basename($modelClass)),
'{{ modelVariable }}' => lcfirst(class_basename($modelClass)),
'{{modelVariable}}' => lcfirst(class_basename($modelClass)),
]);
}
/**
* Get the fully-qualified model class name.
*
* @param string $model
* @return string
*
* @throws \InvalidArgumentException
*/
protected function parseModel($model)
{
if (preg_match('([^A-Za-z0-9_/\\\\])', $model)) {
throw new InvalidArgumentException('Model name contains invalid characters.');
}
return $this->qualifyModel($model);
}
/**
* Build the model replacement values.
*
* @param array $replace
* @param string $modelClass
* @return array
*/
protected function buildFormRequestReplacements(array $replace, $modelClass)
{
[$namespace, $storeRequestClass, $updateRequestClass] = [
'Illuminate\\Http', 'Request', 'Request',
];
if ($this->option('requests')) {
$namespace = 'App\\Http\\Requests';
[$storeRequestClass, $updateRequestClass] = $this->generateFormRequests(
$modelClass, $storeRequestClass, $updateRequestClass
);
}
$namespacedRequests = $namespace.'\\'.$storeRequestClass.';';
if ($storeRequestClass !== $updateRequestClass) {
$namespacedRequests .= PHP_EOL.'use '.$namespace.'\\'.$updateRequestClass.';';
}
return array_merge($replace, [
'{{ storeRequest }}' => $storeRequestClass,
'{{storeRequest}}' => $storeRequestClass,
'{{ updateRequest }}' => $updateRequestClass,
'{{updateRequest}}' => $updateRequestClass,
'{{ namespacedStoreRequest }}' => $namespace.'\\'.$storeRequestClass,
'{{namespacedStoreRequest}}' => $namespace.'\\'.$storeRequestClass,
'{{ namespacedUpdateRequest }}' => $namespace.'\\'.$updateRequestClass,
'{{namespacedUpdateRequest}}' => $namespace.'\\'.$updateRequestClass,
'{{ namespacedRequests }}' => $namespacedRequests,
'{{namespacedRequests}}' => $namespacedRequests,
]);
}
/**
* Generate the form requests for the given model and classes.
*
* @param string $modelClass
* @param string $storeRequestClass
* @param string $updateRequestClass
* @return array
*/
protected function generateFormRequests($modelClass, $storeRequestClass, $updateRequestClass)
{
$storeRequestClass = 'Store'.class_basename($modelClass).'Request';
$this->call('make:request', [
'name' => $storeRequestClass,
]);
$updateRequestClass = 'Update'.class_basename($modelClass).'Request';
$this->call('make:request', [
'name' => $updateRequestClass,
]);
return [$storeRequestClass, $updateRequestClass];
}
/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return [
['api', null, InputOption::VALUE_NONE, 'Exclude the create and edit methods from the controller'],
['type', null, InputOption::VALUE_REQUIRED, 'Manually specify the controller stub file to use'],
['force', null, InputOption::VALUE_NONE, 'Create the class even if the controller already exists'],
['invokable', 'i', InputOption::VALUE_NONE, 'Generate a single method, invokable controller class'],
['model', 'm', InputOption::VALUE_OPTIONAL, 'Generate a resource controller for the given model'],
['parent', 'p', InputOption::VALUE_OPTIONAL, 'Generate a nested resource controller class'],
['resource', 'r', InputOption::VALUE_NONE, 'Generate a resource controller class'],
['requests', 'R', InputOption::VALUE_NONE, 'Generate FormRequest classes for store and update'],
['singleton', 's', InputOption::VALUE_NONE, 'Generate a singleton resource controller class'],
['creatable', null, InputOption::VALUE_NONE, 'Indicate that a singleton resource should be creatable'],
];
}
/**
* Interact further with the user if they were prompted for missing arguments.
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @param \Symfony\Component\Console\Output\OutputInterface $output
* @return void
*/
protected function afterPromptingForMissingArguments(InputInterface $input, OutputInterface $output)
{
if ($this->didReceiveOptions($input)) {
return;
}
$type = select('Which type of controller would you like?', [
'empty' => 'Empty',
'resource' => 'Resource',
'singleton' => 'Singleton',
'api' => 'API',
'invokable' => 'Invokable',
]);
if ($type !== 'empty') {
$input->setOption($type, true);
}
if (in_array($type, ['api', 'resource', 'singleton'])) {
$model = suggest(
"What model should this $type controller be for? (Optional)",
$this->possibleModels()
);
if ($model) {
$input->setOption('model', $model);
}
}
}
}
framework/src/Illuminate/Routing/Contracts/ControllerDispatcher.php 0000644 00000001205 15242753746 0021702 0 ustar 00 name = $name;
$this->options = $options;
$this->registrar = $registrar;
$this->controller = $controller;
}
/**
* Set the methods the controller should apply to.
*
* @param array|string|mixed $methods
* @return \Illuminate\Routing\PendingResourceRegistration
*/
public function only($methods)
{
$this->options['only'] = is_array($methods) ? $methods : func_get_args();
return $this;
}
/**
* Set the methods the controller should exclude.
*
* @param array|string|mixed $methods
* @return \Illuminate\Routing\PendingResourceRegistration
*/
public function except($methods)
{
$this->options['except'] = is_array($methods) ? $methods : func_get_args();
return $this;
}
/**
* Set the route names for controller actions.
*
* @param array|string $names
* @return \Illuminate\Routing\PendingResourceRegistration
*/
public function names($names)
{
$this->options['names'] = $names;
return $this;
}
/**
* Set the route name for a controller action.
*
* @param string $method
* @param string $name
* @return \Illuminate\Routing\PendingResourceRegistration
*/
public function name($method, $name)
{
$this->options['names'][$method] = $name;
return $this;
}
/**
* Override the route parameter names.
*
* @param array|string $parameters
* @return \Illuminate\Routing\PendingResourceRegistration
*/
public function parameters($parameters)
{
$this->options['parameters'] = $parameters;
return $this;
}
/**
* Override a route parameter's name.
*
* @param string $previous
* @param string $new
* @return \Illuminate\Routing\PendingResourceRegistration
*/
public function parameter($previous, $new)
{
$this->options['parameters'][$previous] = $new;
return $this;
}
/**
* Add middleware to the resource routes.
*
* @param mixed $middleware
* @return \Illuminate\Routing\PendingResourceRegistration
*/
public function middleware($middleware)
{
$middleware = Arr::wrap($middleware);
foreach ($middleware as $key => $value) {
$middleware[$key] = (string) $value;
}
$this->options['middleware'] = $middleware;
return $this;
}
/**
* Specify middleware that should be removed from the resource routes.
*
* @param array|string $middleware
* @return $this|array
*/
public function withoutMiddleware($middleware)
{
$this->options['excluded_middleware'] = array_merge(
(array) ($this->options['excluded_middleware'] ?? []), Arr::wrap($middleware)
);
return $this;
}
/**
* Add "where" constraints to the resource routes.
*
* @param mixed $wheres
* @return \Illuminate\Routing\PendingResourceRegistration
*/
public function where($wheres)
{
$this->options['wheres'] = $wheres;
return $this;
}
/**
* Indicate that the resource routes should have "shallow" nesting.
*
* @param bool $shallow
* @return \Illuminate\Routing\PendingResourceRegistration
*/
public function shallow($shallow = true)
{
$this->options['shallow'] = $shallow;
return $this;
}
/**
* Define the callable that should be invoked on a missing model exception.
*
* @param callable $callback
* @return $this
*/
public function missing($callback)
{
$this->options['missing'] = $callback;
return $this;
}
/**
* Indicate that the resource routes should be scoped using the given binding fields.
*
* @param array $fields
* @return \Illuminate\Routing\PendingResourceRegistration
*/
public function scoped(array $fields = [])
{
$this->options['bindingFields'] = $fields;
return $this;
}
/**
* Define which routes should allow "trashed" models to be retrieved when resolving implicit model bindings.
*
* @param array $methods
* @return \Illuminate\Routing\PendingResourceRegistration
*/
public function withTrashed(array $methods = [])
{
$this->options['trashed'] = $methods;
return $this;
}
/**
* Register the resource route.
*
* @return \Illuminate\Routing\RouteCollection
*/
public function register()
{
$this->registered = true;
return $this->registrar->register(
$this->name, $this->controller, $this->options
);
}
/**
* Handle the object's destruction.
*
* @return void
*/
public function __destruct()
{
if (! $this->registered) {
$this->register();
}
}
}
framework/src/Illuminate/Routing/Matching/MethodValidator.php 0000644 00000000766 15242753746 0020443 0 ustar 00 getMethod(), $route->methods());
}
}
framework/src/Illuminate/Routing/Matching/SchemeValidator.php 0000644 00000001153 15242753746 0020416 0 ustar 00 httpOnly()) {
return ! $request->secure();
} elseif ($route->secure()) {
return $request->secure();
}
return true;
}
}
framework/src/Illuminate/Routing/Matching/HostValidator.php 0000644 00000001161 15242753746 0020126 0 ustar 00 getCompiled()->getHostRegex();
if (is_null($hostRegex)) {
return true;
}
return preg_match($hostRegex, $request->getHost());
}
}
framework/src/Illuminate/Routing/Matching/ValidatorInterface.php 0000644 00000000621 15242753746 0021111 0 ustar 00 getPathInfo(), '/') ?: '/';
return preg_match($route->getCompiled()->getRegex(), rawurldecode($path));
}
}
framework/src/Illuminate/Routing/CallableDispatcher.php 0000644 00000002532 15242753746 0017322 0 ustar 00 container = $container;
}
/**
* Dispatch a request to a given callable.
*
* @param \Illuminate\Routing\Route $route
* @param callable $callable
* @return mixed
*/
public function dispatch(Route $route, $callable)
{
return $callable(...array_values($this->resolveParameters($route, $callable)));
}
/**
* Resolve the parameters for the callable.
*
* @param \Illuminate\Routing\Route $route
* @param callable $callable
* @return array
*/
protected function resolveParameters(Route $route, $callable)
{
return $this->resolveMethodDependencies($route->parametersWithoutNulls(), new ReflectionFunction($callable));
}
}
framework/src/Illuminate/Routing/Controller.php 0000644 00000003174 15242753746 0015742 0 ustar 00 middleware[] = [
'middleware' => $m,
'options' => &$options,
];
}
return new ControllerMiddlewareOptions($options);
}
/**
* Get the middleware assigned to the controller.
*
* @return array
*/
public function getMiddleware()
{
return $this->middleware;
}
/**
* Execute an action on the controller.
*
* @param string $method
* @param array $parameters
* @return \Symfony\Component\HttpFoundation\Response
*/
public function callAction($method, $parameters)
{
return $this->{$method}(...array_values($parameters));
}
/**
* Handle calls to missing methods on the controller.
*
* @param string $method
* @param array $parameters
* @return mixed
*
* @throws \BadMethodCallException
*/
public function __call($method, $parameters)
{
throw new BadMethodCallException(sprintf(
'Method %s::%s does not exist.', static::class, $method
));
}
}
framework/src/Illuminate/Routing/Redirector.php 0000644 00000016271 15242753746 0015723 0 ustar 00 generator = $generator;
}
/**
* Create a new redirect response to the previous location.
*
* @param int $status
* @param array $headers
* @param mixed $fallback
* @return \Illuminate\Http\RedirectResponse
*/
public function back($status = 302, $headers = [], $fallback = false)
{
return $this->createRedirect($this->generator->previous($fallback), $status, $headers);
}
/**
* Create a new redirect response to the current URI.
*
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse
*/
public function refresh($status = 302, $headers = [])
{
return $this->to($this->generator->getRequest()->path(), $status, $headers);
}
/**
* Create a new redirect response, while putting the current URL in the session.
*
* @param string $path
* @param int $status
* @param array $headers
* @param bool|null $secure
* @return \Illuminate\Http\RedirectResponse
*/
public function guest($path, $status = 302, $headers = [], $secure = null)
{
$request = $this->generator->getRequest();
$intended = $request->isMethod('GET') && $request->route() && ! $request->expectsJson()
? $this->generator->full()
: $this->generator->previous();
if ($intended) {
$this->setIntendedUrl($intended);
}
return $this->to($path, $status, $headers, $secure);
}
/**
* Create a new redirect response to the previously intended location.
*
* @param mixed $default
* @param int $status
* @param array $headers
* @param bool|null $secure
* @return \Illuminate\Http\RedirectResponse
*/
public function intended($default = '/', $status = 302, $headers = [], $secure = null)
{
$path = $this->session->pull('url.intended', $default);
return $this->to($path, $status, $headers, $secure);
}
/**
* Create a new redirect response to the given path.
*
* @param string $path
* @param int $status
* @param array $headers
* @param bool|null $secure
* @return \Illuminate\Http\RedirectResponse
*/
public function to($path, $status = 302, $headers = [], $secure = null)
{
return $this->createRedirect($this->generator->to($path, [], $secure), $status, $headers);
}
/**
* Create a new redirect response to an external URL (no validation).
*
* @param string $path
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse
*/
public function away($path, $status = 302, $headers = [])
{
return $this->createRedirect($path, $status, $headers);
}
/**
* Create a new redirect response to the given HTTPS path.
*
* @param string $path
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse
*/
public function secure($path, $status = 302, $headers = [])
{
return $this->to($path, $status, $headers, true);
}
/**
* Create a new redirect response to a named route.
*
* @param string $route
* @param mixed $parameters
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse
*/
public function route($route, $parameters = [], $status = 302, $headers = [])
{
return $this->to($this->generator->route($route, $parameters), $status, $headers);
}
/**
* Create a new redirect response to a signed named route.
*
* @param string $route
* @param mixed $parameters
* @param \DateTimeInterface|\DateInterval|int|null $expiration
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse
*/
public function signedRoute($route, $parameters = [], $expiration = null, $status = 302, $headers = [])
{
return $this->to($this->generator->signedRoute($route, $parameters, $expiration), $status, $headers);
}
/**
* Create a new redirect response to a signed named route.
*
* @param string $route
* @param \DateTimeInterface|\DateInterval|int|null $expiration
* @param mixed $parameters
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse
*/
public function temporarySignedRoute($route, $expiration, $parameters = [], $status = 302, $headers = [])
{
return $this->to($this->generator->temporarySignedRoute($route, $expiration, $parameters), $status, $headers);
}
/**
* Create a new redirect response to a controller action.
*
* @param string|array $action
* @param mixed $parameters
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse
*/
public function action($action, $parameters = [], $status = 302, $headers = [])
{
return $this->to($this->generator->action($action, $parameters), $status, $headers);
}
/**
* Create a new redirect response.
*
* @param string $path
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse
*/
protected function createRedirect($path, $status, $headers)
{
return tap(new RedirectResponse($path, $status, $headers), function ($redirect) {
if (isset($this->session)) {
$redirect->setSession($this->session);
}
$redirect->setRequest($this->generator->getRequest());
});
}
/**
* Get the URL generator instance.
*
* @return \Illuminate\Routing\UrlGenerator
*/
public function getUrlGenerator()
{
return $this->generator;
}
/**
* Set the active session store.
*
* @param \Illuminate\Session\Store $session
* @return void
*/
public function setSession(SessionStore $session)
{
$this->session = $session;
}
/**
* Get the "intended" URL from the session.
*
* @return string|null
*/
public function getIntendedUrl()
{
return $this->session->get('url.intended');
}
/**
* Set the "intended" URL in the session.
*
* @param string $url
* @return $this
*/
public function setIntendedUrl($url)
{
$this->session->put('url.intended', $url);
return $this;
}
}
framework/src/Illuminate/Routing/RouteUrlGenerator.php 0000644 00000022064 15242753746 0017246 0 ustar 00 '/',
'%40' => '@',
'%3A' => ':',
'%3B' => ';',
'%2C' => ',',
'%3D' => '=',
'%2B' => '+',
'%21' => '!',
'%2A' => '*',
'%7C' => '|',
'%3F' => '?',
'%26' => '&',
'%23' => '#',
'%25' => '%',
];
/**
* Create a new Route URL generator.
*
* @param \Illuminate\Routing\UrlGenerator $url
* @param \Illuminate\Http\Request $request
* @return void
*/
public function __construct($url, $request)
{
$this->url = $url;
$this->request = $request;
}
/**
* Generate a URL for the given route.
*
* @param \Illuminate\Routing\Route $route
* @param array $parameters
* @param bool $absolute
* @return string
*
* @throws \Illuminate\Routing\Exceptions\UrlGenerationException
*/
public function to($route, $parameters = [], $absolute = false)
{
$domain = $this->getRouteDomain($route, $parameters);
// First we will construct the entire URI including the root and query string. Once it
// has been constructed, we'll make sure we don't have any missing parameters or we
// will need to throw the exception to let the developers know one was not given.
$uri = $this->addQueryString($this->url->format(
$root = $this->replaceRootParameters($route, $domain, $parameters),
$this->replaceRouteParameters($route->uri(), $parameters),
$route
), $parameters);
if (preg_match_all('/{(.*?)}/', $uri, $matchedMissingParameters)) {
throw UrlGenerationException::forMissingParameters($route, $matchedMissingParameters[1]);
}
// Once we have ensured that there are no missing parameters in the URI we will encode
// the URI and prepare it for returning to the developer. If the URI is supposed to
// be absolute, we will return it as-is. Otherwise we will remove the URL's root.
$uri = strtr(rawurlencode($uri), $this->dontEncode);
if (! $absolute) {
$uri = preg_replace('#^(//|[^/?])+#', '', $uri);
if ($base = $this->request->getBaseUrl()) {
$uri = preg_replace('#^'.$base.'#i', '', $uri);
}
return '/'.ltrim($uri, '/');
}
return $uri;
}
/**
* Get the formatted domain for a given route.
*
* @param \Illuminate\Routing\Route $route
* @param array $parameters
* @return string
*/
protected function getRouteDomain($route, &$parameters)
{
return $route->getDomain() ? $this->formatDomain($route, $parameters) : null;
}
/**
* Format the domain and port for the route and request.
*
* @param \Illuminate\Routing\Route $route
* @param array $parameters
* @return string
*/
protected function formatDomain($route, &$parameters)
{
return $this->addPortToDomain(
$this->getRouteScheme($route).$route->getDomain()
);
}
/**
* Get the scheme for the given route.
*
* @param \Illuminate\Routing\Route $route
* @return string
*/
protected function getRouteScheme($route)
{
if ($route->httpOnly()) {
return 'http://';
} elseif ($route->httpsOnly()) {
return 'https://';
}
return $this->url->formatScheme();
}
/**
* Add the port to the domain if necessary.
*
* @param string $domain
* @return string
*/
protected function addPortToDomain($domain)
{
$secure = $this->request->isSecure();
$port = (int) $this->request->getPort();
return ($secure && $port === 443) || (! $secure && $port === 80)
? $domain : $domain.':'.$port;
}
/**
* Replace the parameters on the root path.
*
* @param \Illuminate\Routing\Route $route
* @param string $domain
* @param array $parameters
* @return string
*/
protected function replaceRootParameters($route, $domain, &$parameters)
{
$scheme = $this->getRouteScheme($route);
return $this->replaceRouteParameters(
$this->url->formatRoot($scheme, $domain), $parameters
);
}
/**
* Replace all of the wildcard parameters for a route path.
*
* @param string $path
* @param array $parameters
* @return string
*/
protected function replaceRouteParameters($path, array &$parameters)
{
$path = $this->replaceNamedParameters($path, $parameters);
$path = preg_replace_callback('/\{.*?\}/', function ($match) use (&$parameters) {
// Reset only the numeric keys...
$parameters = array_merge($parameters);
return (! isset($parameters[0]) && ! str_ends_with($match[0], '?}'))
? $match[0]
: Arr::pull($parameters, 0);
}, $path);
return trim(preg_replace('/\{.*?\?\}/', '', $path), '/');
}
/**
* Replace all of the named parameters in the path.
*
* @param string $path
* @param array $parameters
* @return string
*/
protected function replaceNamedParameters($path, &$parameters)
{
return preg_replace_callback('/\{(.*?)(\?)?\}/', function ($m) use (&$parameters) {
if (isset($parameters[$m[1]]) && $parameters[$m[1]] !== '') {
return Arr::pull($parameters, $m[1]);
} elseif (isset($this->defaultParameters[$m[1]])) {
return $this->defaultParameters[$m[1]];
} elseif (isset($parameters[$m[1]])) {
Arr::pull($parameters, $m[1]);
}
return $m[0];
}, $path);
}
/**
* Add a query string to the URI.
*
* @param string $uri
* @param array $parameters
* @return mixed|string
*/
protected function addQueryString($uri, array $parameters)
{
// If the URI has a fragment we will move it to the end of this URI since it will
// need to come after any query string that may be added to the URL else it is
// not going to be available. We will remove it then append it back on here.
if (! is_null($fragment = parse_url($uri, PHP_URL_FRAGMENT))) {
$uri = preg_replace('/#.*/', '', $uri);
}
$uri .= $this->getRouteQueryString($parameters);
return is_null($fragment) ? $uri : $uri."#{$fragment}";
}
/**
* Get the query string for a given route.
*
* @param array $parameters
* @return string
*/
protected function getRouteQueryString(array $parameters)
{
// First we will get all of the string parameters that are remaining after we
// have replaced the route wildcards. We'll then build a query string from
// these string parameters then use it as a starting point for the rest.
if (count($parameters) === 0) {
return '';
}
$query = Arr::query(
$keyed = $this->getStringParameters($parameters)
);
// Lastly, if there are still parameters remaining, we will fetch the numeric
// parameters that are in the array and add them to the query string or we
// will make the initial query string if it wasn't started with strings.
if (count($keyed) < count($parameters)) {
$query .= '&'.implode(
'&', $this->getNumericParameters($parameters)
);
}
$query = trim($query, '&');
return $query === '' ? '' : "?{$query}";
}
/**
* Get the string parameters from a given list.
*
* @param array $parameters
* @return array
*/
protected function getStringParameters(array $parameters)
{
return array_filter($parameters, 'is_string', ARRAY_FILTER_USE_KEY);
}
/**
* Get the numeric parameters from a given list.
*
* @param array $parameters
* @return array
*/
protected function getNumericParameters(array $parameters)
{
return array_filter($parameters, 'is_numeric', ARRAY_FILTER_USE_KEY);
}
/**
* Set the default named parameters used by the URL generator.
*
* @param array $defaults
* @return void
*/
public function defaults(array $defaults)
{
$this->defaultParameters = array_merge(
$this->defaultParameters, $defaults
);
}
}
framework/src/Illuminate/Routing/PendingSingletonResourceRegistration.php 0000644 00000012435 15242753746 0023171 0 ustar 00 name = $name;
$this->options = $options;
$this->registrar = $registrar;
$this->controller = $controller;
}
/**
* Set the methods the controller should apply to.
*
* @param array|string|mixed $methods
* @return \Illuminate\Routing\PendingSingletonResourceRegistration
*/
public function only($methods)
{
$this->options['only'] = is_array($methods) ? $methods : func_get_args();
return $this;
}
/**
* Set the methods the controller should exclude.
*
* @param array|string|mixed $methods
* @return \Illuminate\Routing\PendingSingletonResourceRegistration
*/
public function except($methods)
{
$this->options['except'] = is_array($methods) ? $methods : func_get_args();
return $this;
}
/**
* Indicate that the resource should have creation and storage routes.
*
* @return $this
*/
public function creatable()
{
$this->options['creatable'] = true;
return $this;
}
/**
* Indicate that the resource should have a deletion route.
*
* @return $this
*/
public function destroyable()
{
$this->options['destroyable'] = true;
return $this;
}
/**
* Set the route names for controller actions.
*
* @param array|string $names
* @return \Illuminate\Routing\PendingSingletonResourceRegistration
*/
public function names($names)
{
$this->options['names'] = $names;
return $this;
}
/**
* Set the route name for a controller action.
*
* @param string $method
* @param string $name
* @return \Illuminate\Routing\PendingSingletonResourceRegistration
*/
public function name($method, $name)
{
$this->options['names'][$method] = $name;
return $this;
}
/**
* Override the route parameter names.
*
* @param array|string $parameters
* @return \Illuminate\Routing\PendingSingletonResourceRegistration
*/
public function parameters($parameters)
{
$this->options['parameters'] = $parameters;
return $this;
}
/**
* Override a route parameter's name.
*
* @param string $previous
* @param string $new
* @return \Illuminate\Routing\PendingSingletonResourceRegistration
*/
public function parameter($previous, $new)
{
$this->options['parameters'][$previous] = $new;
return $this;
}
/**
* Add middleware to the resource routes.
*
* @param mixed $middleware
* @return \Illuminate\Routing\PendingSingletonResourceRegistration
*/
public function middleware($middleware)
{
$middleware = Arr::wrap($middleware);
foreach ($middleware as $key => $value) {
$middleware[$key] = (string) $value;
}
$this->options['middleware'] = $middleware;
return $this;
}
/**
* Specify middleware that should be removed from the resource routes.
*
* @param array|string $middleware
* @return $this|array
*/
public function withoutMiddleware($middleware)
{
$this->options['excluded_middleware'] = array_merge(
(array) ($this->options['excluded_middleware'] ?? []), Arr::wrap($middleware)
);
return $this;
}
/**
* Add "where" constraints to the resource routes.
*
* @param mixed $wheres
* @return \Illuminate\Routing\PendingSingletonResourceRegistration
*/
public function where($wheres)
{
$this->options['wheres'] = $wheres;
return $this;
}
/**
* Register the singleton resource route.
*
* @return \Illuminate\Routing\RouteCollection
*/
public function register()
{
$this->registered = true;
return $this->registrar->singleton(
$this->name, $this->controller, $this->options
);
}
/**
* Handle the object's destruction.
*
* @return void
*/
public function __destruct()
{
if (! $this->registered) {
$this->register();
}
}
}
framework/src/Illuminate/Routing/ControllerMiddlewareOptions.php 0000644 00000001761 15242753746 0021314 0 ustar 00 options = &$options;
}
/**
* Set the controller methods the middleware should apply to.
*
* @param array|string|mixed $methods
* @return $this
*/
public function only($methods)
{
$this->options['only'] = is_array($methods) ? $methods : func_get_args();
return $this;
}
/**
* Set the controller methods the middleware should exclude.
*
* @param array|string|mixed $methods
* @return $this
*/
public function except($methods)
{
$this->options['except'] = is_array($methods) ? $methods : func_get_args();
return $this;
}
}
framework/src/Illuminate/Routing/RouteBinding.php 0000644 00000005343 15242753746 0016210 0 ustar 00 make($class), $method];
return $callable($value, $route);
};
}
/**
* Create a Route model binding for a model.
*
* @param \Illuminate\Container\Container $container
* @param string $class
* @param \Closure|null $callback
* @return \Closure
*
* @throws \Illuminate\Database\Eloquent\ModelNotFoundException<\Illuminate\Database\Eloquent\Model>
*/
public static function forModel($container, $class, $callback = null)
{
return function ($value) use ($container, $class, $callback) {
if (is_null($value)) {
return;
}
// For model binders, we will attempt to retrieve the models using the first
// method on the model instance. If we cannot retrieve the models we'll
// throw a not found exception otherwise we will return the instance.
$instance = $container->make($class);
if ($model = $instance->resolveRouteBinding($value)) {
return $model;
}
// If a callback was supplied to the method we will call that to determine
// what we should do when the model is not found. This just gives these
// developer a little greater flexibility to decide what will happen.
if ($callback instanceof Closure) {
return $callback($value);
}
throw (new ModelNotFoundException)->setModel($class);
};
}
}
framework/src/Illuminate/Routing/ResourceRegistrar.php 0000644 00000051173 15242753746 0017273 0 ustar 00 'create',
'edit' => 'edit',
];
/**
* Create a new resource registrar instance.
*
* @param \Illuminate\Routing\Router $router
* @return void
*/
public function __construct(Router $router)
{
$this->router = $router;
}
/**
* Route a resource to a controller.
*
* @param string $name
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\RouteCollection
*/
public function register($name, $controller, array $options = [])
{
if (isset($options['parameters']) && ! isset($this->parameters)) {
$this->parameters = $options['parameters'];
}
// If the resource name contains a slash, we will assume the developer wishes to
// register these resource routes with a prefix so we will set that up out of
// the box so they don't have to mess with it. Otherwise, we will continue.
if (str_contains($name, '/')) {
$this->prefixedResource($name, $controller, $options);
return;
}
// We need to extract the base resource from the resource name. Nested resources
// are supported in the framework, but we need to know what name to use for a
// place-holder on the route parameters, which should be the base resources.
$base = $this->getResourceWildcard(last(explode('.', $name)));
$defaults = $this->resourceDefaults;
$collection = new RouteCollection;
$resourceMethods = $this->getResourceMethods($defaults, $options);
foreach ($resourceMethods as $m) {
$route = $this->{'addResource'.ucfirst($m)}(
$name, $base, $controller, $options
);
if (isset($options['bindingFields'])) {
$this->setResourceBindingFields($route, $options['bindingFields']);
}
if (isset($options['trashed']) &&
in_array($m, ! empty($options['trashed']) ? $options['trashed'] : array_intersect($resourceMethods, ['show', 'edit', 'update']))) {
$route->withTrashed();
}
$collection->add($route);
}
return $collection;
}
/**
* Route a singleton resource to a controller.
*
* @param string $name
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\RouteCollection
*/
public function singleton($name, $controller, array $options = [])
{
if (isset($options['parameters']) && ! isset($this->parameters)) {
$this->parameters = $options['parameters'];
}
// If the resource name contains a slash, we will assume the developer wishes to
// register these singleton routes with a prefix so we will set that up out of
// the box so they don't have to mess with it. Otherwise, we will continue.
if (str_contains($name, '/')) {
$this->prefixedSingleton($name, $controller, $options);
return;
}
$defaults = $this->singletonResourceDefaults;
if (isset($options['creatable'])) {
$defaults = array_merge($defaults, ['create', 'store', 'destroy']);
} elseif (isset($options['destroyable'])) {
$defaults = array_merge($defaults, ['destroy']);
}
$collection = new RouteCollection;
$resourceMethods = $this->getResourceMethods($defaults, $options);
foreach ($resourceMethods as $m) {
$route = $this->{'addSingleton'.ucfirst($m)}(
$name, $controller, $options
);
if (isset($options['bindingFields'])) {
$this->setResourceBindingFields($route, $options['bindingFields']);
}
$collection->add($route);
}
return $collection;
}
/**
* Build a set of prefixed resource routes.
*
* @param string $name
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\Router
*/
protected function prefixedResource($name, $controller, array $options)
{
[$name, $prefix] = $this->getResourcePrefix($name);
// We need to extract the base resource from the resource name. Nested resources
// are supported in the framework, but we need to know what name to use for a
// place-holder on the route parameters, which should be the base resources.
$callback = function ($me) use ($name, $controller, $options) {
$me->resource($name, $controller, $options);
};
return $this->router->group(compact('prefix'), $callback);
}
/**
* Build a set of prefixed singleton routes.
*
* @param string $name
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\Router
*/
protected function prefixedSingleton($name, $controller, array $options)
{
[$name, $prefix] = $this->getResourcePrefix($name);
// We need to extract the base resource from the resource name. Nested resources
// are supported in the framework, but we need to know what name to use for a
// place-holder on the route parameters, which should be the base resources.
$callback = function ($me) use ($name, $controller, $options) {
$me->singleton($name, $controller, $options);
};
return $this->router->group(compact('prefix'), $callback);
}
/**
* Extract the resource and prefix from a resource name.
*
* @param string $name
* @return array
*/
protected function getResourcePrefix($name)
{
$segments = explode('/', $name);
// To get the prefix, we will take all of the name segments and implode them on
// a slash. This will generate a proper URI prefix for us. Then we take this
// last segment, which will be considered the final resources name we use.
$prefix = implode('/', array_slice($segments, 0, -1));
return [end($segments), $prefix];
}
/**
* Get the applicable resource methods.
*
* @param array $defaults
* @param array $options
* @return array
*/
protected function getResourceMethods($defaults, $options)
{
$methods = $defaults;
if (isset($options['only'])) {
$methods = array_intersect($methods, (array) $options['only']);
}
if (isset($options['except'])) {
$methods = array_diff($methods, (array) $options['except']);
}
return array_values($methods);
}
/**
* Add the index method for a resourceful route.
*
* @param string $name
* @param string $base
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\Route
*/
protected function addResourceIndex($name, $base, $controller, $options)
{
$uri = $this->getResourceUri($name);
unset($options['missing']);
$action = $this->getResourceAction($name, $controller, 'index', $options);
return $this->router->get($uri, $action);
}
/**
* Add the create method for a resourceful route.
*
* @param string $name
* @param string $base
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\Route
*/
protected function addResourceCreate($name, $base, $controller, $options)
{
$uri = $this->getResourceUri($name).'/'.static::$verbs['create'];
unset($options['missing']);
$action = $this->getResourceAction($name, $controller, 'create', $options);
return $this->router->get($uri, $action);
}
/**
* Add the store method for a resourceful route.
*
* @param string $name
* @param string $base
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\Route
*/
protected function addResourceStore($name, $base, $controller, $options)
{
$uri = $this->getResourceUri($name);
unset($options['missing']);
$action = $this->getResourceAction($name, $controller, 'store', $options);
return $this->router->post($uri, $action);
}
/**
* Add the show method for a resourceful route.
*
* @param string $name
* @param string $base
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\Route
*/
protected function addResourceShow($name, $base, $controller, $options)
{
$name = $this->getShallowName($name, $options);
$uri = $this->getResourceUri($name).'/{'.$base.'}';
$action = $this->getResourceAction($name, $controller, 'show', $options);
return $this->router->get($uri, $action);
}
/**
* Add the edit method for a resourceful route.
*
* @param string $name
* @param string $base
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\Route
*/
protected function addResourceEdit($name, $base, $controller, $options)
{
$name = $this->getShallowName($name, $options);
$uri = $this->getResourceUri($name).'/{'.$base.'}/'.static::$verbs['edit'];
$action = $this->getResourceAction($name, $controller, 'edit', $options);
return $this->router->get($uri, $action);
}
/**
* Add the update method for a resourceful route.
*
* @param string $name
* @param string $base
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\Route
*/
protected function addResourceUpdate($name, $base, $controller, $options)
{
$name = $this->getShallowName($name, $options);
$uri = $this->getResourceUri($name).'/{'.$base.'}';
$action = $this->getResourceAction($name, $controller, 'update', $options);
return $this->router->match(['PUT', 'PATCH'], $uri, $action);
}
/**
* Add the destroy method for a resourceful route.
*
* @param string $name
* @param string $base
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\Route
*/
protected function addResourceDestroy($name, $base, $controller, $options)
{
$name = $this->getShallowName($name, $options);
$uri = $this->getResourceUri($name).'/{'.$base.'}';
$action = $this->getResourceAction($name, $controller, 'destroy', $options);
return $this->router->delete($uri, $action);
}
/**
* Add the create method for a singleton route.
*
* @param string $name
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\Route
*/
protected function addSingletonCreate($name, $controller, $options)
{
$uri = $this->getResourceUri($name).'/'.static::$verbs['create'];
unset($options['missing']);
$action = $this->getResourceAction($name, $controller, 'create', $options);
return $this->router->get($uri, $action);
}
/**
* Add the store method for a singleton route.
*
* @param string $name
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\Route
*/
protected function addSingletonStore($name, $controller, $options)
{
$uri = $this->getResourceUri($name);
unset($options['missing']);
$action = $this->getResourceAction($name, $controller, 'store', $options);
return $this->router->post($uri, $action);
}
/**
* Add the show method for a singleton route.
*
* @param string $name
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\Route
*/
protected function addSingletonShow($name, $controller, $options)
{
$uri = $this->getResourceUri($name);
unset($options['missing']);
$action = $this->getResourceAction($name, $controller, 'show', $options);
return $this->router->get($uri, $action);
}
/**
* Add the edit method for a singleton route.
*
* @param string $name
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\Route
*/
protected function addSingletonEdit($name, $controller, $options)
{
$name = $this->getShallowName($name, $options);
$uri = $this->getResourceUri($name).'/'.static::$verbs['edit'];
$action = $this->getResourceAction($name, $controller, 'edit', $options);
return $this->router->get($uri, $action);
}
/**
* Add the update method for a singleton route.
*
* @param string $name
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\Route
*/
protected function addSingletonUpdate($name, $controller, $options)
{
$name = $this->getShallowName($name, $options);
$uri = $this->getResourceUri($name);
$action = $this->getResourceAction($name, $controller, 'update', $options);
return $this->router->match(['PUT', 'PATCH'], $uri, $action);
}
/**
* Add the destroy method for a singleton route.
*
* @param string $name
* @param string $controller
* @param array $options
* @return \Illuminate\Routing\Route
*/
protected function addSingletonDestroy($name, $controller, $options)
{
$name = $this->getShallowName($name, $options);
$uri = $this->getResourceUri($name);
$action = $this->getResourceAction($name, $controller, 'destroy', $options);
return $this->router->delete($uri, $action);
}
/**
* Get the name for a given resource with shallowness applied when applicable.
*
* @param string $name
* @param array $options
* @return string
*/
protected function getShallowName($name, $options)
{
return isset($options['shallow']) && $options['shallow']
? last(explode('.', $name))
: $name;
}
/**
* Set the route's binding fields if the resource is scoped.
*
* @param \Illuminate\Routing\Route $route
* @param array $bindingFields
* @return void
*/
protected function setResourceBindingFields($route, $bindingFields)
{
preg_match_all('/(?<={).*?(?=})/', $route->uri, $matches);
$fields = array_fill_keys($matches[0], null);
$route->setBindingFields(array_replace(
$fields, array_intersect_key($bindingFields, $fields)
));
}
/**
* Get the base resource URI for a given resource.
*
* @param string $resource
* @return string
*/
public function getResourceUri($resource)
{
if (! str_contains($resource, '.')) {
return $resource;
}
// Once we have built the base URI, we'll remove the parameter holder for this
// base resource name so that the individual route adders can suffix these
// paths however they need to, as some do not have any parameters at all.
$segments = explode('.', $resource);
$uri = $this->getNestedResourceUri($segments);
return str_replace('/{'.$this->getResourceWildcard(end($segments)).'}', '', $uri);
}
/**
* Get the URI for a nested resource segment array.
*
* @param array $segments
* @return string
*/
protected function getNestedResourceUri(array $segments)
{
// We will spin through the segments and create a place-holder for each of the
// resource segments, as well as the resource itself. Then we should get an
// entire string for the resource URI that contains all nested resources.
return implode('/', array_map(function ($s) {
return $s.'/{'.$this->getResourceWildcard($s).'}';
}, $segments));
}
/**
* Format a resource parameter for usage.
*
* @param string $value
* @return string
*/
public function getResourceWildcard($value)
{
if (isset($this->parameters[$value])) {
$value = $this->parameters[$value];
} elseif (isset(static::$parameterMap[$value])) {
$value = static::$parameterMap[$value];
} elseif ($this->parameters === 'singular' || static::$singularParameters) {
$value = Str::singular($value);
}
return str_replace('-', '_', $value);
}
/**
* Get the action array for a resource route.
*
* @param string $resource
* @param string $controller
* @param string $method
* @param array $options
* @return array
*/
protected function getResourceAction($resource, $controller, $method, $options)
{
$name = $this->getResourceRouteName($resource, $method, $options);
$action = ['as' => $name, 'uses' => $controller.'@'.$method];
if (isset($options['middleware'])) {
$action['middleware'] = $options['middleware'];
}
if (isset($options['excluded_middleware'])) {
$action['excluded_middleware'] = $options['excluded_middleware'];
}
if (isset($options['wheres'])) {
$action['where'] = $options['wheres'];
}
if (isset($options['missing'])) {
$action['missing'] = $options['missing'];
}
return $action;
}
/**
* Get the name for a given resource.
*
* @param string $resource
* @param string $method
* @param array $options
* @return string
*/
protected function getResourceRouteName($resource, $method, $options)
{
$name = $resource;
// If the names array has been provided to us we will check for an entry in the
// array first. We will also check for the specific method within this array
// so the names may be specified on a more "granular" level using methods.
if (isset($options['names'])) {
if (is_string($options['names'])) {
$name = $options['names'];
} elseif (isset($options['names'][$method])) {
return $options['names'][$method];
}
}
// If a global prefix has been assigned to all names for this resource, we will
// grab that so we can prepend it onto the name when we create this name for
// the resource action. Otherwise we'll just use an empty string for here.
$prefix = isset($options['as']) ? $options['as'].'.' : '';
return trim(sprintf('%s%s.%s', $prefix, $name, $method), '.');
}
/**
* Set or unset the unmapped global parameters to singular.
*
* @param bool $singular
* @return void
*/
public static function singularParameters($singular = true)
{
static::$singularParameters = (bool) $singular;
}
/**
* Get the global parameter map.
*
* @return array
*/
public static function getParameters()
{
return static::$parameterMap;
}
/**
* Set the global parameter mapping.
*
* @param array $parameters
* @return void
*/
public static function setParameters(array $parameters = [])
{
static::$parameterMap = $parameters;
}
/**
* Get or set the action verbs used in the resource URIs.
*
* @param array $verbs
* @return array
*/
public static function verbs(array $verbs = [])
{
if (empty($verbs)) {
return static::$verbs;
}
static::$verbs = array_merge(static::$verbs, $verbs);
}
}
framework/src/Illuminate/Routing/RouteAction.php 0000644 00000006610 15242753746 0016051 0 ustar 00 $action] : [
'uses' => $action[0].'@'.$action[1],
'controller' => $action[0].'@'.$action[1],
];
}
// If no "uses" property has been set, we will dig through the array to find a
// Closure instance within this list. We will set the first Closure we come
// across into the "uses" property that will get fired off by this route.
elseif (! isset($action['uses'])) {
$action['uses'] = static::findCallable($action);
}
if (! static::containsSerializedClosure($action) && is_string($action['uses']) && ! str_contains($action['uses'], '@')) {
$action['uses'] = static::makeInvokable($action['uses']);
}
return $action;
}
/**
* Get an action for a route that has no action.
*
* @param string $uri
* @return array
*
* @throws \LogicException
*/
protected static function missingAction($uri)
{
return ['uses' => function () use ($uri) {
throw new LogicException("Route for [{$uri}] has no action.");
}];
}
/**
* Find the callable in an action array.
*
* @param array $action
* @return callable
*/
protected static function findCallable(array $action)
{
return Arr::first($action, function ($value, $key) {
return Reflector::isCallable($value) && is_numeric($key);
});
}
/**
* Make an action for an invokable controller.
*
* @param string $action
* @return string
*
* @throws \UnexpectedValueException
*/
protected static function makeInvokable($action)
{
if (! method_exists($action, '__invoke')) {
throw new UnexpectedValueException("Invalid route action: [{$action}].");
}
return $action.'@__invoke';
}
/**
* Determine if the given array actions contain a serialized Closure.
*
* @param array $action
* @return bool
*/
public static function containsSerializedClosure(array $action)
{
return is_string($action['uses']) && Str::startsWith($action['uses'], [
'O:47:"Laravel\\SerializableClosure\\SerializableClosure',
'O:55:"Laravel\\SerializableClosure\\UnsignedSerializableClosure',
]) !== false;
}
}
framework/src/Illuminate/Routing/RouteDependencyResolverTrait.php 0000644 00000000207 15242753746 0021434 0 ustar 00 resolveMethodDependencies(
$parameters, new ReflectionMethod($instance, $method)
);
}
/**
* Resolve the given method's type-hinted dependencies.
*
* @param array $parameters
* @param \ReflectionFunctionAbstract $reflector
* @return array
*/
public function resolveMethodDependencies(array $parameters, ReflectionFunctionAbstract $reflector)
{
$instanceCount = 0;
$values = array_values($parameters);
$skippableValue = new stdClass;
foreach ($reflector->getParameters() as $key => $parameter) {
$instance = $this->transformDependency($parameter, $parameters, $skippableValue);
if ($instance !== $skippableValue) {
$instanceCount++;
$this->spliceIntoParameters($parameters, $key, $instance);
} elseif (! isset($values[$key - $instanceCount]) &&
$parameter->isDefaultValueAvailable()) {
$this->spliceIntoParameters($parameters, $key, $parameter->getDefaultValue());
}
}
return $parameters;
}
/**
* Attempt to transform the given parameter into a class instance.
*
* @param \ReflectionParameter $parameter
* @param array $parameters
* @param object $skippableValue
* @return mixed
*/
protected function transformDependency(ReflectionParameter $parameter, $parameters, $skippableValue)
{
$className = Reflector::getParameterClassName($parameter);
// If the parameter has a type-hinted class, we will check to see if it is already in
// the list of parameters. If it is we will just skip it as it is probably a model
// binding and we do not want to mess with those; otherwise, we resolve it here.
if ($className && ! $this->alreadyInParameters($className, $parameters)) {
$isEnum = (new ReflectionClass($className))->isEnum();
return $parameter->isDefaultValueAvailable()
? ($isEnum ? $parameter->getDefaultValue() : null)
: $this->container->make($className);
}
return $skippableValue;
}
/**
* Determine if an object of the given class is in a list of parameters.
*
* @param string $class
* @param array $parameters
* @return bool
*/
protected function alreadyInParameters($class, array $parameters)
{
return ! is_null(Arr::first($parameters, fn ($value) => $value instanceof $class));
}
/**
* Splice the given value into the parameter list.
*
* @param array $parameters
* @param string $offset
* @param mixed $value
* @return void
*/
protected function spliceIntoParameters(array &$parameters, $offset, $value)
{
array_splice(
$parameters, $offset, 0, [$value]
);
}
}
framework/src/Illuminate/Routing/RouteUri.php 0000644 00000002444 15242753746 0015374 0 ustar 00 uri = $uri;
$this->bindingFields = $bindingFields;
}
/**
* Parse the given URI.
*
* @param string $uri
* @return static
*/
public static function parse($uri)
{
preg_match_all('/\{([\w\:]+?)\??\}/', $uri, $matches);
$bindingFields = [];
foreach ($matches[0] as $match) {
if (! str_contains($match, ':')) {
continue;
}
$segments = explode(':', trim($match, '{}?'));
$bindingFields[$segments[0]] = $segments[1];
$uri = str_contains($match, '?')
? str_replace($match, '{'.$segments[0].'?}', $uri)
: str_replace($match, '{'.$segments[0].'}', $uri);
}
return new static($uri, $bindingFields);
}
}
framework/src/Illuminate/Routing/Route.php 0000644 00000076737 15242753746 0014734 0 ustar 00 uri = $uri;
$this->methods = (array) $methods;
$this->action = Arr::except($this->parseAction($action), ['prefix']);
if (in_array('GET', $this->methods) && ! in_array('HEAD', $this->methods)) {
$this->methods[] = 'HEAD';
}
$this->prefix(is_array($action) ? Arr::get($action, 'prefix') : '');
}
/**
* Parse the route action into a standard array.
*
* @param callable|array|null $action
* @return array
*
* @throws \UnexpectedValueException
*/
protected function parseAction($action)
{
return RouteAction::parse($this->uri, $action);
}
/**
* Run the route action and return the response.
*
* @return mixed
*/
public function run()
{
$this->container = $this->container ?: new Container;
try {
if ($this->isControllerAction()) {
return $this->runController();
}
return $this->runCallable();
} catch (HttpResponseException $e) {
return $e->getResponse();
}
}
/**
* Checks whether the route's action is a controller.
*
* @return bool
*/
protected function isControllerAction()
{
return is_string($this->action['uses']) && ! $this->isSerializedClosure();
}
/**
* Run the route action and return the response.
*
* @return mixed
*/
protected function runCallable()
{
$callable = $this->action['uses'];
if ($this->isSerializedClosure()) {
$callable = unserialize($this->action['uses'])->getClosure();
}
return $this->container[CallableDispatcher::class]->dispatch($this, $callable);
}
/**
* Determine if the route action is a serialized Closure.
*
* @return bool
*/
protected function isSerializedClosure()
{
return RouteAction::containsSerializedClosure($this->action);
}
/**
* Run the route action and return the response.
*
* @return mixed
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
protected function runController()
{
return $this->controllerDispatcher()->dispatch(
$this, $this->getController(), $this->getControllerMethod()
);
}
/**
* Get the controller instance for the route.
*
* @return mixed
*/
public function getController()
{
if (! $this->isControllerAction()) {
return null;
}
if (! $this->controller) {
$class = $this->getControllerClass();
$this->controller = $this->container->make(ltrim($class, '\\'));
}
return $this->controller;
}
/**
* Get the controller class used for the route.
*
* @return string|null
*/
public function getControllerClass()
{
return $this->isControllerAction() ? $this->parseControllerCallback()[0] : null;
}
/**
* Get the controller method used for the route.
*
* @return string
*/
protected function getControllerMethod()
{
return $this->parseControllerCallback()[1];
}
/**
* Parse the controller.
*
* @return array
*/
protected function parseControllerCallback()
{
return Str::parseCallback($this->action['uses']);
}
/**
* Flush the cached container instance on the route.
*
* @return void
*/
public function flushController()
{
$this->computedMiddleware = null;
$this->controller = null;
}
/**
* Determine if the route matches a given request.
*
* @param \Illuminate\Http\Request $request
* @param bool $includingMethod
* @return bool
*/
public function matches(Request $request, $includingMethod = true)
{
$this->compileRoute();
foreach (self::getValidators() as $validator) {
if (! $includingMethod && $validator instanceof MethodValidator) {
continue;
}
if (! $validator->matches($this, $request)) {
return false;
}
}
return true;
}
/**
* Compile the route into a Symfony CompiledRoute instance.
*
* @return \Symfony\Component\Routing\CompiledRoute
*/
protected function compileRoute()
{
if (! $this->compiled) {
$this->compiled = $this->toSymfonyRoute()->compile();
}
return $this->compiled;
}
/**
* Bind the route to a given request for execution.
*
* @param \Illuminate\Http\Request $request
* @return $this
*/
public function bind(Request $request)
{
$this->compileRoute();
$this->parameters = (new RouteParameterBinder($this))
->parameters($request);
$this->originalParameters = $this->parameters;
return $this;
}
/**
* Determine if the route has parameters.
*
* @return bool
*/
public function hasParameters()
{
return isset($this->parameters);
}
/**
* Determine a given parameter exists from the route.
*
* @param string $name
* @return bool
*/
public function hasParameter($name)
{
if ($this->hasParameters()) {
return array_key_exists($name, $this->parameters());
}
return false;
}
/**
* Get a given parameter from the route.
*
* @param string $name
* @param string|object|null $default
* @return string|object|null
*/
public function parameter($name, $default = null)
{
return Arr::get($this->parameters(), $name, $default);
}
/**
* Get original value of a given parameter from the route.
*
* @param string $name
* @param string|null $default
* @return string|null
*/
public function originalParameter($name, $default = null)
{
return Arr::get($this->originalParameters(), $name, $default);
}
/**
* Set a parameter to the given value.
*
* @param string $name
* @param string|object|null $value
* @return void
*/
public function setParameter($name, $value)
{
$this->parameters();
$this->parameters[$name] = $value;
}
/**
* Unset a parameter on the route if it is set.
*
* @param string $name
* @return void
*/
public function forgetParameter($name)
{
$this->parameters();
unset($this->parameters[$name]);
}
/**
* Get the key / value list of parameters for the route.
*
* @return array
*
* @throws \LogicException
*/
public function parameters()
{
if (isset($this->parameters)) {
return $this->parameters;
}
throw new LogicException('Route is not bound.');
}
/**
* Get the key / value list of original parameters for the route.
*
* @return array
*
* @throws \LogicException
*/
public function originalParameters()
{
if (isset($this->originalParameters)) {
return $this->originalParameters;
}
throw new LogicException('Route is not bound.');
}
/**
* Get the key / value list of parameters without null values.
*
* @return array
*/
public function parametersWithoutNulls()
{
return array_filter($this->parameters(), fn ($p) => ! is_null($p));
}
/**
* Get all of the parameter names for the route.
*
* @return array
*/
public function parameterNames()
{
if (isset($this->parameterNames)) {
return $this->parameterNames;
}
return $this->parameterNames = $this->compileParameterNames();
}
/**
* Get the parameter names for the route.
*
* @return array
*/
protected function compileParameterNames()
{
preg_match_all('/\{(.*?)\}/', $this->getDomain().$this->uri, $matches);
return array_map(fn ($m) => trim($m, '?'), $matches[1]);
}
/**
* Get the parameters that are listed in the route / controller signature.
*
* @param array $conditions
* @return array
*/
public function signatureParameters($conditions = [])
{
if (is_string($conditions)) {
$conditions = ['subClass' => $conditions];
}
return RouteSignatureParameters::fromAction($this->action, $conditions);
}
/**
* Get the binding field for the given parameter.
*
* @param string|int $parameter
* @return string|null
*/
public function bindingFieldFor($parameter)
{
$fields = is_int($parameter) ? array_values($this->bindingFields) : $this->bindingFields;
return $fields[$parameter] ?? null;
}
/**
* Get the binding fields for the route.
*
* @return array
*/
public function bindingFields()
{
return $this->bindingFields ?? [];
}
/**
* Set the binding fields for the route.
*
* @param array $bindingFields
* @return $this
*/
public function setBindingFields(array $bindingFields)
{
$this->bindingFields = $bindingFields;
return $this;
}
/**
* Get the parent parameter of the given parameter.
*
* @param string $parameter
* @return string|null
*/
public function parentOfParameter($parameter)
{
$key = array_search($parameter, array_keys($this->parameters));
if ($key === 0 || $key === false) {
return;
}
return array_values($this->parameters)[$key - 1];
}
/**
* Allow "trashed" models to be retrieved when resolving implicit model bindings for this route.
*
* @param bool $withTrashed
* @return $this
*/
public function withTrashed($withTrashed = true)
{
$this->withTrashedBindings = $withTrashed;
return $this;
}
/**
* Determines if the route allows "trashed" models to be retrieved when resolving implicit model bindings.
*
* @return bool
*/
public function allowsTrashedBindings()
{
return $this->withTrashedBindings;
}
/**
* Set a default value for the route.
*
* @param string $key
* @param mixed $value
* @return $this
*/
public function defaults($key, $value)
{
$this->defaults[$key] = $value;
return $this;
}
/**
* Set the default values for the route.
*
* @param array $defaults
* @return $this
*/
public function setDefaults(array $defaults)
{
$this->defaults = $defaults;
return $this;
}
/**
* Set a regular expression requirement on the route.
*
* @param array|string $name
* @param string|null $expression
* @return $this
*/
public function where($name, $expression = null)
{
foreach ($this->parseWhere($name, $expression) as $name => $expression) {
$this->wheres[$name] = $expression;
}
return $this;
}
/**
* Parse arguments to the where method into an array.
*
* @param array|string $name
* @param string $expression
* @return array
*/
protected function parseWhere($name, $expression)
{
return is_array($name) ? $name : [$name => $expression];
}
/**
* Set a list of regular expression requirements on the route.
*
* @param array $wheres
* @return $this
*/
public function setWheres(array $wheres)
{
foreach ($wheres as $name => $expression) {
$this->where($name, $expression);
}
return $this;
}
/**
* Mark this route as a fallback route.
*
* @return $this
*/
public function fallback()
{
$this->isFallback = true;
return $this;
}
/**
* Set the fallback value.
*
* @param bool $isFallback
* @return $this
*/
public function setFallback($isFallback)
{
$this->isFallback = $isFallback;
return $this;
}
/**
* Get the HTTP verbs the route responds to.
*
* @return array
*/
public function methods()
{
return $this->methods;
}
/**
* Determine if the route only responds to HTTP requests.
*
* @return bool
*/
public function httpOnly()
{
return in_array('http', $this->action, true);
}
/**
* Determine if the route only responds to HTTPS requests.
*
* @return bool
*/
public function httpsOnly()
{
return $this->secure();
}
/**
* Determine if the route only responds to HTTPS requests.
*
* @return bool
*/
public function secure()
{
return in_array('https', $this->action, true);
}
/**
* Get or set the domain for the route.
*
* @param string|null $domain
* @return $this|string|null
*/
public function domain($domain = null)
{
if (is_null($domain)) {
return $this->getDomain();
}
$parsed = RouteUri::parse($domain);
$this->action['domain'] = $parsed->uri;
$this->bindingFields = array_merge(
$this->bindingFields, $parsed->bindingFields
);
return $this;
}
/**
* Get the domain defined for the route.
*
* @return string|null
*/
public function getDomain()
{
return isset($this->action['domain'])
? str_replace(['http://', 'https://'], '', $this->action['domain']) : null;
}
/**
* Get the prefix of the route instance.
*
* @return string|null
*/
public function getPrefix()
{
return $this->action['prefix'] ?? null;
}
/**
* Add a prefix to the route URI.
*
* @param string $prefix
* @return $this
*/
public function prefix($prefix)
{
$prefix ??= '';
$this->updatePrefixOnAction($prefix);
$uri = rtrim($prefix, '/').'/'.ltrim($this->uri, '/');
return $this->setUri($uri !== '/' ? trim($uri, '/') : $uri);
}
/**
* Update the "prefix" attribute on the action array.
*
* @param string $prefix
* @return void
*/
protected function updatePrefixOnAction($prefix)
{
if (! empty($newPrefix = trim(rtrim($prefix, '/').'/'.ltrim($this->action['prefix'] ?? '', '/'), '/'))) {
$this->action['prefix'] = $newPrefix;
}
}
/**
* Get the URI associated with the route.
*
* @return string
*/
public function uri()
{
return $this->uri;
}
/**
* Set the URI that the route responds to.
*
* @param string $uri
* @return $this
*/
public function setUri($uri)
{
$this->uri = $this->parseUri($uri);
return $this;
}
/**
* Parse the route URI and normalize / store any implicit binding fields.
*
* @param string $uri
* @return string
*/
protected function parseUri($uri)
{
$this->bindingFields = [];
return tap(RouteUri::parse($uri), function ($uri) {
$this->bindingFields = $uri->bindingFields;
})->uri;
}
/**
* Get the name of the route instance.
*
* @return string|null
*/
public function getName()
{
return $this->action['as'] ?? null;
}
/**
* Add or change the route name.
*
* @param string $name
* @return $this
*/
public function name($name)
{
$this->action['as'] = isset($this->action['as']) ? $this->action['as'].$name : $name;
return $this;
}
/**
* Determine whether the route's name matches the given patterns.
*
* @param mixed ...$patterns
* @return bool
*/
public function named(...$patterns)
{
if (is_null($routeName = $this->getName())) {
return false;
}
foreach ($patterns as $pattern) {
if (Str::is($pattern, $routeName)) {
return true;
}
}
return false;
}
/**
* Set the handler for the route.
*
* @param \Closure|array|string $action
* @return $this
*/
public function uses($action)
{
if (is_array($action)) {
$action = $action[0].'@'.$action[1];
}
$action = is_string($action) ? $this->addGroupNamespaceToStringUses($action) : $action;
return $this->setAction(array_merge($this->action, $this->parseAction([
'uses' => $action,
'controller' => $action,
])));
}
/**
* Parse a string based action for the "uses" fluent method.
*
* @param string $action
* @return string
*/
protected function addGroupNamespaceToStringUses($action)
{
$groupStack = last($this->router->getGroupStack());
if (isset($groupStack['namespace']) && ! str_starts_with($action, '\\')) {
return $groupStack['namespace'].'\\'.$action;
}
return $action;
}
/**
* Get the action name for the route.
*
* @return string
*/
public function getActionName()
{
return $this->action['controller'] ?? 'Closure';
}
/**
* Get the method name of the route action.
*
* @return string
*/
public function getActionMethod()
{
return Arr::last(explode('@', $this->getActionName()));
}
/**
* Get the action array or one of its properties for the route.
*
* @param string|null $key
* @return mixed
*/
public function getAction($key = null)
{
return Arr::get($this->action, $key);
}
/**
* Set the action array for the route.
*
* @param array $action
* @return $this
*/
public function setAction(array $action)
{
$this->action = $action;
if (isset($this->action['domain'])) {
$this->domain($this->action['domain']);
}
return $this;
}
/**
* Get the value of the action that should be taken on a missing model exception.
*
* @return \Closure|null
*/
public function getMissing()
{
$missing = $this->action['missing'] ?? null;
return is_string($missing) &&
Str::startsWith($missing, [
'O:47:"Laravel\\SerializableClosure\\SerializableClosure',
'O:55:"Laravel\\SerializableClosure\\UnsignedSerializableClosure',
]) ? unserialize($missing) : $missing;
}
/**
* Define the callable that should be invoked on a missing model exception.
*
* @param \Closure $missing
* @return $this
*/
public function missing($missing)
{
$this->action['missing'] = $missing;
return $this;
}
/**
* Get all middleware, including the ones from the controller.
*
* @return array
*/
public function gatherMiddleware()
{
if (! is_null($this->computedMiddleware)) {
return $this->computedMiddleware;
}
$this->computedMiddleware = [];
return $this->computedMiddleware = Router::uniqueMiddleware(array_merge(
$this->middleware(), $this->controllerMiddleware()
));
}
/**
* Get or set the middlewares attached to the route.
*
* @param array|string|null $middleware
* @return $this|array
*/
public function middleware($middleware = null)
{
if (is_null($middleware)) {
return (array) ($this->action['middleware'] ?? []);
}
if (! is_array($middleware)) {
$middleware = func_get_args();
}
foreach ($middleware as $index => $value) {
$middleware[$index] = (string) $value;
}
$this->action['middleware'] = array_merge(
(array) ($this->action['middleware'] ?? []), $middleware
);
return $this;
}
/**
* Specify that the "Authorize" / "can" middleware should be applied to the route with the given options.
*
* @param string $ability
* @param array|string $models
* @return $this
*/
public function can($ability, $models = [])
{
return empty($models)
? $this->middleware(['can:'.$ability])
: $this->middleware(['can:'.$ability.','.implode(',', Arr::wrap($models))]);
}
/**
* Get the middleware for the route's controller.
*
* @return array
*/
public function controllerMiddleware()
{
if (! $this->isControllerAction()) {
return [];
}
[$controllerClass, $controllerMethod] = [
$this->getControllerClass(),
$this->getControllerMethod(),
];
if (is_a($controllerClass, HasMiddleware::class, true)) {
return $this->staticallyProvidedControllerMiddleware(
$controllerClass, $controllerMethod
);
}
if (method_exists($controllerClass, 'getMiddleware')) {
return $this->controllerDispatcher()->getMiddleware(
$this->getController(), $controllerMethod
);
}
return [];
}
/**
* Get the statically provided controller middleware for the given class and method.
*
* @param string $class
* @param string $method
* @return array
*/
protected function staticallyProvidedControllerMiddleware(string $class, string $method)
{
return collect($class::middleware())->reject(function ($middleware) use ($method) {
return static::methodExcludedByOptions(
$method, ['only' => $middleware->only, 'except' => $middleware->except]
);
})->map->middleware->values()->all();
}
/**
* Specify middleware that should be removed from the given route.
*
* @param array|string $middleware
* @return $this
*/
public function withoutMiddleware($middleware)
{
$this->action['excluded_middleware'] = array_merge(
(array) ($this->action['excluded_middleware'] ?? []), Arr::wrap($middleware)
);
return $this;
}
/**
* Get the middleware that should be removed from the route.
*
* @return array
*/
public function excludedMiddleware()
{
return (array) ($this->action['excluded_middleware'] ?? []);
}
/**
* Indicate that the route should enforce scoping of multiple implicit Eloquent bindings.
*
* @return $this
*/
public function scopeBindings()
{
$this->action['scope_bindings'] = true;
return $this;
}
/**
* Indicate that the route should not enforce scoping of multiple implicit Eloquent bindings.
*
* @return $this
*/
public function withoutScopedBindings()
{
$this->action['scope_bindings'] = false;
return $this;
}
/**
* Determine if the route should enforce scoping of multiple implicit Eloquent bindings.
*
* @return bool
*/
public function enforcesScopedBindings()
{
return (bool) ($this->action['scope_bindings'] ?? false);
}
/**
* Determine if the route should prevent scoping of multiple implicit Eloquent bindings.
*
* @return bool
*/
public function preventsScopedBindings()
{
return isset($this->action['scope_bindings']) && $this->action['scope_bindings'] === false;
}
/**
* Specify that the route should not allow concurrent requests from the same session.
*
* @param int|null $lockSeconds
* @param int|null $waitSeconds
* @return $this
*/
public function block($lockSeconds = 10, $waitSeconds = 10)
{
$this->lockSeconds = $lockSeconds;
$this->waitSeconds = $waitSeconds;
return $this;
}
/**
* Specify that the route should allow concurrent requests from the same session.
*
* @return $this
*/
public function withoutBlocking()
{
return $this->block(null, null);
}
/**
* Get the maximum number of seconds the route's session lock should be held for.
*
* @return int|null
*/
public function locksFor()
{
return $this->lockSeconds;
}
/**
* Get the maximum number of seconds to wait while attempting to acquire a session lock.
*
* @return int|null
*/
public function waitsFor()
{
return $this->waitSeconds;
}
/**
* Get the dispatcher for the route's controller.
*
* @return \Illuminate\Routing\Contracts\ControllerDispatcher
*/
public function controllerDispatcher()
{
if ($this->container->bound(ControllerDispatcherContract::class)) {
return $this->container->make(ControllerDispatcherContract::class);
}
return new ControllerDispatcher($this->container);
}
/**
* Get the route validators for the instance.
*
* @return array
*/
public static function getValidators()
{
if (isset(static::$validators)) {
return static::$validators;
}
// To match the route, we will use a chain of responsibility pattern with the
// validator implementations. We will spin through each one making sure it
// passes and then we will know if the route as a whole matches request.
return static::$validators = [
new UriValidator, new MethodValidator,
new SchemeValidator, new HostValidator,
];
}
/**
* Convert the route to a Symfony route.
*
* @return \Symfony\Component\Routing\Route
*/
public function toSymfonyRoute()
{
return new SymfonyRoute(
preg_replace('/\{(\w+?)\?\}/', '{$1}', $this->uri()), $this->getOptionalParameterNames(),
$this->wheres, ['utf8' => true],
$this->getDomain() ?: '', [], $this->methods
);
}
/**
* Get the optional parameter names for the route.
*
* @return array
*/
protected function getOptionalParameterNames()
{
preg_match_all('/\{(\w+?)\?\}/', $this->uri(), $matches);
return isset($matches[1]) ? array_fill_keys($matches[1], null) : [];
}
/**
* Get the compiled version of the route.
*
* @return \Symfony\Component\Routing\CompiledRoute
*/
public function getCompiled()
{
return $this->compiled;
}
/**
* Set the router instance on the route.
*
* @param \Illuminate\Routing\Router $router
* @return $this
*/
public function setRouter(Router $router)
{
$this->router = $router;
return $this;
}
/**
* Set the container instance on the route.
*
* @param \Illuminate\Container\Container $container
* @return $this
*/
public function setContainer(Container $container)
{
$this->container = $container;
return $this;
}
/**
* Prepare the route instance for serialization.
*
* @return void
*
* @throws \LogicException
*/
public function prepareForSerialization()
{
if ($this->action['uses'] instanceof Closure) {
$this->action['uses'] = serialize(
SerializableClosure::unsigned($this->action['uses'])
);
}
if (isset($this->action['missing']) && $this->action['missing'] instanceof Closure) {
$this->action['missing'] = serialize(
SerializableClosure::unsigned($this->action['missing'])
);
}
$this->compileRoute();
unset($this->router, $this->container);
}
/**
* Dynamically access route parameters.
*
* @param string $key
* @return mixed
*/
public function __get($key)
{
return $this->parameter($key);
}
}
framework/src/Illuminate/Routing/MiddlewareNameResolver.php 0000644 00000006152 15242753746 0020216 0 ustar 00 view = $view;
$this->redirector = $redirector;
}
/**
* Create a new response instance.
*
* @param mixed $content
* @param int $status
* @param array $headers
* @return \Illuminate\Http\Response
*/
public function make($content = '', $status = 200, array $headers = [])
{
return new Response($content, $status, $headers);
}
/**
* Create a new "no content" response.
*
* @param int $status
* @param array $headers
* @return \Illuminate\Http\Response
*/
public function noContent($status = 204, array $headers = [])
{
return $this->make('', $status, $headers);
}
/**
* Create a new response for a given view.
*
* @param string|array $view
* @param array $data
* @param int $status
* @param array $headers
* @return \Illuminate\Http\Response
*/
public function view($view, $data = [], $status = 200, array $headers = [])
{
if (is_array($view)) {
return $this->make($this->view->first($view, $data), $status, $headers);
}
return $this->make($this->view->make($view, $data), $status, $headers);
}
/**
* Create a new JSON response instance.
*
* @param mixed $data
* @param int $status
* @param array $headers
* @param int $options
* @return \Illuminate\Http\JsonResponse
*/
public function json($data = [], $status = 200, array $headers = [], $options = 0)
{
return new JsonResponse($data, $status, $headers, $options);
}
/**
* Create a new JSONP response instance.
*
* @param string $callback
* @param mixed $data
* @param int $status
* @param array $headers
* @param int $options
* @return \Illuminate\Http\JsonResponse
*/
public function jsonp($callback, $data = [], $status = 200, array $headers = [], $options = 0)
{
return $this->json($data, $status, $headers, $options)->setCallback($callback);
}
/**
* Create a new streamed response instance.
*
* @param callable $callback
* @param int $status
* @param array $headers
* @return \Symfony\Component\HttpFoundation\StreamedResponse
*/
public function stream($callback, $status = 200, array $headers = [])
{
return new StreamedResponse($callback, $status, $headers);
}
/**
* Create a new streamed response instance.
*
* @param array $data
* @param int $status
* @param array $headers
* @param int $encodingOptions
* @return \Symfony\Component\HttpFoundation\StreamedJsonResponse
*/
public function streamJson($data, $status = 200, $headers = [], $encodingOptions = JsonResponse::DEFAULT_ENCODING_OPTIONS)
{
return new StreamedJsonResponse($data, $status, $headers, $encodingOptions);
}
/**
* Create a new streamed response instance as a file download.
*
* @param callable $callback
* @param string|null $name
* @param array $headers
* @param string|null $disposition
* @return \Symfony\Component\HttpFoundation\StreamedResponse
*/
public function streamDownload($callback, $name = null, array $headers = [], $disposition = 'attachment')
{
$withWrappedException = function () use ($callback) {
try {
$callback();
} catch (Throwable $e) {
throw new StreamedResponseException($e);
}
};
$response = new StreamedResponse($withWrappedException, 200, $headers);
if (! is_null($name)) {
$response->headers->set('Content-Disposition', $response->headers->makeDisposition(
$disposition,
$name,
$this->fallbackName($name)
));
}
return $response;
}
/**
* Create a new file download response.
*
* @param \SplFileInfo|string $file
* @param string|null $name
* @param array $headers
* @param string|null $disposition
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
*/
public function download($file, $name = null, array $headers = [], $disposition = 'attachment')
{
$response = new BinaryFileResponse($file, 200, $headers, true, $disposition);
if (! is_null($name)) {
return $response->setContentDisposition($disposition, $name, $this->fallbackName($name));
}
return $response;
}
/**
* Convert the string to ASCII characters that are equivalent to the given name.
*
* @param string $name
* @return string
*/
protected function fallbackName($name)
{
return str_replace('%', '', Str::ascii($name));
}
/**
* Return the raw contents of a binary file.
*
* @param \SplFileInfo|string $file
* @param array $headers
* @return \Symfony\Component\HttpFoundation\BinaryFileResponse
*/
public function file($file, array $headers = [])
{
return new BinaryFileResponse($file, 200, $headers);
}
/**
* Create a new redirect response to the given path.
*
* @param string $path
* @param int $status
* @param array $headers
* @param bool|null $secure
* @return \Illuminate\Http\RedirectResponse
*/
public function redirectTo($path, $status = 302, $headers = [], $secure = null)
{
return $this->redirector->to($path, $status, $headers, $secure);
}
/**
* Create a new redirect response to a named route.
*
* @param string $route
* @param mixed $parameters
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse
*/
public function redirectToRoute($route, $parameters = [], $status = 302, $headers = [])
{
return $this->redirector->route($route, $parameters, $status, $headers);
}
/**
* Create a new redirect response to a controller action.
*
* @param array|string $action
* @param mixed $parameters
* @param int $status
* @param array $headers
* @return \Illuminate\Http\RedirectResponse
*/
public function redirectToAction($action, $parameters = [], $status = 302, $headers = [])
{
return $this->redirector->action($action, $parameters, $status, $headers);
}
/**
* Create a new redirect response, while putting the current URL in the session.
*
* @param string $path
* @param int $status
* @param array $headers
* @param bool|null $secure
* @return \Illuminate\Http\RedirectResponse
*/
public function redirectGuest($path, $status = 302, $headers = [], $secure = null)
{
return $this->redirector->guest($path, $status, $headers, $secure);
}
/**
* Create a new redirect response to the previously intended location.
*
* @param string $default
* @param int $status
* @param array $headers
* @param bool|null $secure
* @return \Illuminate\Http\RedirectResponse
*/
public function redirectToIntended($default = '/', $status = 302, $headers = [], $secure = null)
{
return $this->redirector->intended($default, $status, $headers, $secure);
}
}
framework/src/Illuminate/Routing/RedirectController.php 0000644 00000002264 15242753746 0017423 0 ustar 00 route()->parameters());
$status = $parameters->get('status');
$destination = $parameters->get('destination');
$parameters->forget('status')->forget('destination');
$route = (new Route('GET', $destination, [
'as' => 'laravel_route_redirect_destination',
]))->bind($request);
$parameters = $parameters->only(
$route->getCompiled()->getPathVariables()
)->all();
$url = $url->toRoute($route, $parameters, false);
if (! str_starts_with($destination, '/') && str_starts_with($url, '/')) {
$url = Str::after($url, '/');
}
return new RedirectResponse($url, $status);
}
}
framework/src/Illuminate/Routing/RouteSignatureParameters.php 0000644 00000003146 15242753746 0020622 0 ustar 00 getClosure()
: $action['uses'];
$parameters = is_string($callback)
? static::fromClassMethodString($callback)
: (new ReflectionFunction($callback))->getParameters();
return match (true) {
! empty($conditions['subClass']) => array_filter($parameters, fn ($p) => Reflector::isParameterSubclassOf($p, $conditions['subClass'])),
! empty($conditions['backedEnum']) => array_filter($parameters, fn ($p) => Reflector::isParameterBackedEnumWithStringBackingType($p)),
default => $parameters,
};
}
/**
* Get the parameters for the given class / method by string.
*
* @param string $uses
* @return array
*/
protected static function fromClassMethodString($uses)
{
[$class, $method] = Str::parseCallback($uses);
if (! method_exists($class, $method) && Reflector::isCallable($class, $method)) {
return [];
}
return (new ReflectionMethod($class, $method))->getParameters();
}
}
framework/src/Illuminate/Routing/RouteCollection.php 0000644 00000015613 15242753746 0016732 0 ustar 00 addToCollections($route);
$this->addLookups($route);
return $route;
}
/**
* Add the given route to the arrays of routes.
*
* @param \Illuminate\Routing\Route $route
* @return void
*/
protected function addToCollections($route)
{
$domainAndUri = $route->getDomain().$route->uri();
foreach ($route->methods() as $method) {
$this->routes[$method][$domainAndUri] = $route;
}
$this->allRoutes[$method.$domainAndUri] = $route;
}
/**
* Add the route to any look-up tables if necessary.
*
* @param \Illuminate\Routing\Route $route
* @return void
*/
protected function addLookups($route)
{
// If the route has a name, we will add it to the name look-up table so that we
// will quickly be able to find any route associate with a name and not have
// to iterate through every route every time we need to perform a look-up.
if ($name = $route->getName()) {
$this->nameList[$name] = $route;
}
// When the route is routing to a controller we will also store the action that
// is used by the route. This will let us reverse route to controllers while
// processing a request and easily generate URLs to the given controllers.
$action = $route->getAction();
if (isset($action['controller'])) {
$this->addToActionList($action, $route);
}
}
/**
* Add a route to the controller action dictionary.
*
* @param array $action
* @param \Illuminate\Routing\Route $route
* @return void
*/
protected function addToActionList($action, $route)
{
$this->actionList[trim($action['controller'], '\\')] = $route;
}
/**
* Refresh the name look-up table.
*
* This is done in case any names are fluently defined or if routes are overwritten.
*
* @return void
*/
public function refreshNameLookups()
{
$this->nameList = [];
foreach ($this->allRoutes as $route) {
if ($route->getName()) {
$this->nameList[$route->getName()] = $route;
}
}
}
/**
* Refresh the action look-up table.
*
* This is done in case any actions are overwritten with new controllers.
*
* @return void
*/
public function refreshActionLookups()
{
$this->actionList = [];
foreach ($this->allRoutes as $route) {
if (isset($route->getAction()['controller'])) {
$this->addToActionList($route->getAction(), $route);
}
}
}
/**
* Find the first route matching a given request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Routing\Route
*
* @throws \Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
public function match(Request $request)
{
$routes = $this->get($request->getMethod());
// First, we will see if we can find a matching route for this current request
// method. If we can, great, we can just return it so that it can be called
// by the consumer. Otherwise we will check for routes with another verb.
$route = $this->matchAgainstRoutes($routes, $request);
return $this->handleMatchedRoute($request, $route);
}
/**
* Get routes from the collection by method.
*
* @param string|null $method
* @return \Illuminate\Routing\Route[]
*/
public function get($method = null)
{
return is_null($method) ? $this->getRoutes() : Arr::get($this->routes, $method, []);
}
/**
* Determine if the route collection contains a given named route.
*
* @param string $name
* @return bool
*/
public function hasNamedRoute($name)
{
return ! is_null($this->getByName($name));
}
/**
* Get a route instance by its name.
*
* @param string $name
* @return \Illuminate\Routing\Route|null
*/
public function getByName($name)
{
return $this->nameList[$name] ?? null;
}
/**
* Get a route instance by its controller action.
*
* @param string $action
* @return \Illuminate\Routing\Route|null
*/
public function getByAction($action)
{
return $this->actionList[$action] ?? null;
}
/**
* Get all of the routes in the collection.
*
* @return \Illuminate\Routing\Route[]
*/
public function getRoutes()
{
return array_values($this->allRoutes);
}
/**
* Get all of the routes keyed by their HTTP verb / method.
*
* @return array
*/
public function getRoutesByMethod()
{
return $this->routes;
}
/**
* Get all of the routes keyed by their name.
*
* @return \Illuminate\Routing\Route[]
*/
public function getRoutesByName()
{
return $this->nameList;
}
/**
* Convert the collection to a Symfony RouteCollection instance.
*
* @return \Symfony\Component\Routing\RouteCollection
*/
public function toSymfonyRouteCollection()
{
$symfonyRoutes = parent::toSymfonyRouteCollection();
$this->refreshNameLookups();
return $symfonyRoutes;
}
/**
* Convert the collection to a CompiledRouteCollection instance.
*
* @param \Illuminate\Routing\Router $router
* @param \Illuminate\Container\Container $container
* @return \Illuminate\Routing\CompiledRouteCollection
*/
public function toCompiledRouteCollection(Router $router, Container $container)
{
['compiled' => $compiled, 'attributes' => $attributes] = $this->compile();
return (new CompiledRouteCollection($compiled, $attributes))
->setRouter($router)
->setContainer($container);
}
}
framework/src/Illuminate/Routing/RouteParameterBinder.php 0000644 00000006056 15242753746 0017704 0 ustar 00 route = $route;
}
/**
* Get the parameters for the route.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function parameters($request)
{
$parameters = $this->bindPathParameters($request);
// If the route has a regular expression for the host part of the URI, we will
// compile that and get the parameter matches for this domain. We will then
// merge them into this parameters array so that this array is completed.
if (! is_null($this->route->compiled->getHostRegex())) {
$parameters = $this->bindHostParameters(
$request, $parameters
);
}
return $this->replaceDefaults($parameters);
}
/**
* Get the parameter matches for the path portion of the URI.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
protected function bindPathParameters($request)
{
$path = '/'.ltrim($request->decodedPath(), '/');
preg_match($this->route->compiled->getRegex(), $path, $matches);
return $this->matchToKeys(array_slice($matches, 1));
}
/**
* Extract the parameter list from the host part of the request.
*
* @param \Illuminate\Http\Request $request
* @param array $parameters
* @return array
*/
protected function bindHostParameters($request, $parameters)
{
preg_match($this->route->compiled->getHostRegex(), $request->getHost(), $matches);
return array_merge($this->matchToKeys(array_slice($matches, 1)), $parameters);
}
/**
* Combine a set of parameter matches with the route's keys.
*
* @param array $matches
* @return array
*/
protected function matchToKeys(array $matches)
{
if (empty($parameterNames = $this->route->parameterNames())) {
return [];
}
$parameters = array_intersect_key($matches, array_flip($parameterNames));
return array_filter($parameters, function ($value) {
return is_string($value) && strlen($value) > 0;
});
}
/**
* Replace null parameters with their defaults.
*
* @param array $parameters
* @return array
*/
protected function replaceDefaults(array $parameters)
{
foreach ($parameters as $key => $value) {
$parameters[$key] = $value ?? Arr::get($this->route->defaults, $key);
}
foreach ($this->route->defaults as $key => $value) {
if (! isset($parameters[$key])) {
$parameters[$key] = $value;
}
}
return $parameters;
}
}
framework/src/Illuminate/Routing/Pipeline.php 0000644 00000002763 15242753746 0015367 0 ustar 00 toResponse($this->getContainer()->make(Request::class))
: $carry;
}
/**
* Handle the given exception.
*
* @param mixed $passable
* @param \Throwable $e
* @return mixed
*
* @throws \Throwable
*/
protected function handleException($passable, Throwable $e)
{
if (! $this->container->bound(ExceptionHandler::class) ||
! $passable instanceof Request) {
throw $e;
}
$handler = $this->container->make(ExceptionHandler::class);
$handler->report($e);
$response = $handler->render($passable, $e);
if (is_object($response) && method_exists($response, 'withException')) {
$response->withException($e);
}
return $this->handleCarry($response);
}
}
framework/src/Illuminate/Routing/UrlGenerator.php 0000644 00000053717 15242753746 0016240 0 ustar 00 routes = $routes;
$this->assetRoot = $assetRoot;
$this->setRequest($request);
}
/**
* Get the full URL for the current request.
*
* @return string
*/
public function full()
{
return $this->request->fullUrl();
}
/**
* Get the current URL for the request.
*
* @return string
*/
public function current()
{
return $this->to($this->request->getPathInfo());
}
/**
* Get the URL for the previous request.
*
* @param mixed $fallback
* @return string
*/
public function previous($fallback = false)
{
$referrer = $this->request->headers->get('referer');
$url = $referrer ? $this->to($referrer) : $this->getPreviousUrlFromSession();
if ($url) {
return $url;
} elseif ($fallback) {
return $this->to($fallback);
}
return $this->to('/');
}
/**
* Get the previous path info for the request.
*
* @param mixed $fallback
* @return string
*/
public function previousPath($fallback = false)
{
$previousPath = str_replace($this->to('/'), '', rtrim(preg_replace('/\?.*/', '', $this->previous($fallback)), '/'));
return $previousPath === '' ? '/' : $previousPath;
}
/**
* Get the previous URL from the session if possible.
*
* @return string|null
*/
protected function getPreviousUrlFromSession()
{
return $this->getSession()?->previousUrl();
}
/**
* Generate an absolute URL to the given path.
*
* @param string $path
* @param mixed $extra
* @param bool|null $secure
* @return string
*/
public function to($path, $extra = [], $secure = null)
{
// First we will check if the URL is already a valid URL. If it is we will not
// try to generate a new one but will simply return the URL as is, which is
// convenient since developers do not always have to check if it's valid.
if ($this->isValidUrl($path)) {
return $path;
}
$tail = implode('/', array_map(
'rawurlencode', (array) $this->formatParameters($extra))
);
// Once we have the scheme we will compile the "tail" by collapsing the values
// into a single string delimited by slashes. This just makes it convenient
// for passing the array of parameters to this URL as a list of segments.
$root = $this->formatRoot($this->formatScheme($secure));
[$path, $query] = $this->extractQueryString($path);
return $this->format(
$root, '/'.trim($path.'/'.$tail, '/')
).$query;
}
/**
* Generate a secure, absolute URL to the given path.
*
* @param string $path
* @param array $parameters
* @return string
*/
public function secure($path, $parameters = [])
{
return $this->to($path, $parameters, true);
}
/**
* Generate the URL to an application asset.
*
* @param string $path
* @param bool|null $secure
* @return string
*/
public function asset($path, $secure = null)
{
if ($this->isValidUrl($path)) {
return $path;
}
// Once we get the root URL, we will check to see if it contains an index.php
// file in the paths. If it does, we will remove it since it is not needed
// for asset paths, but only for routes to endpoints in the application.
$root = $this->assetRoot ?: $this->formatRoot($this->formatScheme($secure));
return Str::finish($this->removeIndex($root), '/').trim($path, '/');
}
/**
* Generate the URL to a secure asset.
*
* @param string $path
* @return string
*/
public function secureAsset($path)
{
return $this->asset($path, true);
}
/**
* Generate the URL to an asset from a custom root domain such as CDN, etc.
*
* @param string $root
* @param string $path
* @param bool|null $secure
* @return string
*/
public function assetFrom($root, $path, $secure = null)
{
// Once we get the root URL, we will check to see if it contains an index.php
// file in the paths. If it does, we will remove it since it is not needed
// for asset paths, but only for routes to endpoints in the application.
$root = $this->formatRoot($this->formatScheme($secure), $root);
return $this->removeIndex($root).'/'.trim($path, '/');
}
/**
* Remove the index.php file from a path.
*
* @param string $root
* @return string
*/
protected function removeIndex($root)
{
$i = 'index.php';
return str_contains($root, $i) ? str_replace('/'.$i, '', $root) : $root;
}
/**
* Get the default scheme for a raw URL.
*
* @param bool|null $secure
* @return string
*/
public function formatScheme($secure = null)
{
if (! is_null($secure)) {
return $secure ? 'https://' : 'http://';
}
if (is_null($this->cachedScheme)) {
$this->cachedScheme = $this->forceScheme ?: $this->request->getScheme().'://';
}
return $this->cachedScheme;
}
/**
* Create a signed route URL for a named route.
*
* @param string $name
* @param mixed $parameters
* @param \DateTimeInterface|\DateInterval|int|null $expiration
* @param bool $absolute
* @return string
*
* @throws \InvalidArgumentException
*/
public function signedRoute($name, $parameters = [], $expiration = null, $absolute = true)
{
$this->ensureSignedRouteParametersAreNotReserved(
$parameters = Arr::wrap($parameters)
);
if ($expiration) {
$parameters = $parameters + ['expires' => $this->availableAt($expiration)];
}
ksort($parameters);
$key = call_user_func($this->keyResolver);
return $this->route($name, $parameters + [
'signature' => hash_hmac('sha256', $this->route($name, $parameters, $absolute), $key),
], $absolute);
}
/**
* Ensure the given signed route parameters are not reserved.
*
* @param mixed $parameters
* @return void
*/
protected function ensureSignedRouteParametersAreNotReserved($parameters)
{
if (array_key_exists('signature', $parameters)) {
throw new InvalidArgumentException(
'"Signature" is a reserved parameter when generating signed routes. Please rename your route parameter.'
);
}
if (array_key_exists('expires', $parameters)) {
throw new InvalidArgumentException(
'"Expires" is a reserved parameter when generating signed routes. Please rename your route parameter.'
);
}
}
/**
* Create a temporary signed route URL for a named route.
*
* @param string $name
* @param \DateTimeInterface|\DateInterval|int $expiration
* @param array $parameters
* @param bool $absolute
* @return string
*/
public function temporarySignedRoute($name, $expiration, $parameters = [], $absolute = true)
{
return $this->signedRoute($name, $parameters, $expiration, $absolute);
}
/**
* Determine if the given request has a valid signature.
*
* @param \Illuminate\Http\Request $request
* @param bool $absolute
* @param array $ignoreQuery
* @return bool
*/
public function hasValidSignature(Request $request, $absolute = true, array $ignoreQuery = [])
{
return $this->hasCorrectSignature($request, $absolute, $ignoreQuery)
&& $this->signatureHasNotExpired($request);
}
/**
* Determine if the given request has a valid signature for a relative URL.
*
* @param \Illuminate\Http\Request $request
* @param array $ignoreQuery
* @return bool
*/
public function hasValidRelativeSignature(Request $request, array $ignoreQuery = [])
{
return $this->hasValidSignature($request, false, $ignoreQuery);
}
/**
* Determine if the signature from the given request matches the URL.
*
* @param \Illuminate\Http\Request $request
* @param bool $absolute
* @param array $ignoreQuery
* @return bool
*/
public function hasCorrectSignature(Request $request, $absolute = true, array $ignoreQuery = [])
{
$ignoreQuery[] = 'signature';
$url = $absolute ? $request->url() : '/'.$request->path();
$queryString = collect(explode('&', (string) $request->server->get('QUERY_STRING')))
->reject(fn ($parameter) => in_array(Str::before($parameter, '='), $ignoreQuery))
->join('&');
$original = rtrim($url.'?'.$queryString, '?');
$signature = hash_hmac('sha256', $original, call_user_func($this->keyResolver));
return hash_equals($signature, (string) $request->query('signature', ''));
}
/**
* Determine if the expires timestamp from the given request is not from the past.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
public function signatureHasNotExpired(Request $request)
{
$expires = $request->query('expires');
return ! ($expires && Carbon::now()->getTimestamp() > $expires);
}
/**
* Get the URL to a named route.
*
* @param string $name
* @param mixed $parameters
* @param bool $absolute
* @return string
*
* @throws \Symfony\Component\Routing\Exception\RouteNotFoundException
*/
public function route($name, $parameters = [], $absolute = true)
{
if (! is_null($route = $this->routes->getByName($name))) {
return $this->toRoute($route, $parameters, $absolute);
}
if (! is_null($this->missingNamedRouteResolver) &&
! is_null($url = call_user_func($this->missingNamedRouteResolver, $name, $parameters, $absolute))) {
return $url;
}
throw new RouteNotFoundException("Route [{$name}] not defined.");
}
/**
* Get the URL for a given route instance.
*
* @param \Illuminate\Routing\Route $route
* @param mixed $parameters
* @param bool $absolute
* @return string
*
* @throws \Illuminate\Routing\Exceptions\UrlGenerationException
*/
public function toRoute($route, $parameters, $absolute)
{
$parameters = collect(Arr::wrap($parameters))->map(function ($value, $key) use ($route) {
$value = $value instanceof UrlRoutable && $route->bindingFieldFor($key)
? $value->{$route->bindingFieldFor($key)}
: $value;
return $value instanceof BackedEnum ? $value->value : $value;
})->all();
return $this->routeUrl()->to(
$route, $this->formatParameters($parameters), $absolute
);
}
/**
* Get the URL to a controller action.
*
* @param string|array $action
* @param mixed $parameters
* @param bool $absolute
* @return string
*
* @throws \InvalidArgumentException
*/
public function action($action, $parameters = [], $absolute = true)
{
if (is_null($route = $this->routes->getByAction($action = $this->formatAction($action)))) {
throw new InvalidArgumentException("Action {$action} not defined.");
}
return $this->toRoute($route, $parameters, $absolute);
}
/**
* Format the given controller action.
*
* @param string|array $action
* @return string
*/
protected function formatAction($action)
{
if (is_array($action)) {
$action = '\\'.implode('@', $action);
}
if ($this->rootNamespace && ! str_starts_with($action, '\\')) {
return $this->rootNamespace.'\\'.$action;
}
return trim($action, '\\');
}
/**
* Format the array of URL parameters.
*
* @param mixed|array $parameters
* @return array
*/
public function formatParameters($parameters)
{
$parameters = Arr::wrap($parameters);
foreach ($parameters as $key => $parameter) {
if ($parameter instanceof UrlRoutable) {
$parameters[$key] = $parameter->getRouteKey();
}
}
return $parameters;
}
/**
* Extract the query string from the given path.
*
* @param string $path
* @return array
*/
protected function extractQueryString($path)
{
if (($queryPosition = strpos($path, '?')) !== false) {
return [
substr($path, 0, $queryPosition),
substr($path, $queryPosition),
];
}
return [$path, ''];
}
/**
* Get the base URL for the request.
*
* @param string $scheme
* @param string|null $root
* @return string
*/
public function formatRoot($scheme, $root = null)
{
if (is_null($root)) {
if (is_null($this->cachedRoot)) {
$this->cachedRoot = $this->forcedRoot ?: $this->request->root();
}
$root = $this->cachedRoot;
}
$start = str_starts_with($root, 'http://') ? 'http://' : 'https://';
return preg_replace('~'.$start.'~', $scheme, $root, 1);
}
/**
* Format the given URL segments into a single URL.
*
* @param string $root
* @param string $path
* @param \Illuminate\Routing\Route|null $route
* @return string
*/
public function format($root, $path, $route = null)
{
$path = '/'.trim($path, '/');
if ($this->formatHostUsing) {
$root = call_user_func($this->formatHostUsing, $root, $route);
}
if ($this->formatPathUsing) {
$path = call_user_func($this->formatPathUsing, $path, $route);
}
return trim($root.$path, '/');
}
/**
* Determine if the given path is a valid URL.
*
* @param string $path
* @return bool
*/
public function isValidUrl($path)
{
if (! preg_match('~^(#|//|https?://|(mailto|tel|sms):)~', $path)) {
return filter_var($path, FILTER_VALIDATE_URL) !== false;
}
return true;
}
/**
* Get the Route URL generator instance.
*
* @return \Illuminate\Routing\RouteUrlGenerator
*/
protected function routeUrl()
{
if (! $this->routeGenerator) {
$this->routeGenerator = new RouteUrlGenerator($this, $this->request);
}
return $this->routeGenerator;
}
/**
* Set the default named parameters used by the URL generator.
*
* @param array $defaults
* @return void
*/
public function defaults(array $defaults)
{
$this->routeUrl()->defaults($defaults);
}
/**
* Get the default named parameters used by the URL generator.
*
* @return array
*/
public function getDefaultParameters()
{
return $this->routeUrl()->defaultParameters;
}
/**
* Force the scheme for URLs.
*
* @param string|null $scheme
* @return void
*/
public function forceScheme($scheme)
{
$this->cachedScheme = null;
$this->forceScheme = $scheme ? $scheme.'://' : null;
}
/**
* Set the forced root URL.
*
* @param string|null $root
* @return void
*/
public function forceRootUrl($root)
{
$this->forcedRoot = $root ? rtrim($root, '/') : null;
$this->cachedRoot = null;
}
/**
* Set a callback to be used to format the host of generated URLs.
*
* @param \Closure $callback
* @return $this
*/
public function formatHostUsing(Closure $callback)
{
$this->formatHostUsing = $callback;
return $this;
}
/**
* Set a callback to be used to format the path of generated URLs.
*
* @param \Closure $callback
* @return $this
*/
public function formatPathUsing(Closure $callback)
{
$this->formatPathUsing = $callback;
return $this;
}
/**
* Get the path formatter being used by the URL generator.
*
* @return \Closure
*/
public function pathFormatter()
{
return $this->formatPathUsing ?: function ($path) {
return $path;
};
}
/**
* Get the request instance.
*
* @return \Illuminate\Http\Request
*/
public function getRequest()
{
return $this->request;
}
/**
* Set the current request instance.
*
* @param \Illuminate\Http\Request $request
* @return void
*/
public function setRequest(Request $request)
{
$this->request = $request;
$this->cachedRoot = null;
$this->cachedScheme = null;
tap(optional($this->routeGenerator)->defaultParameters ?: [], function ($defaults) {
$this->routeGenerator = null;
if (! empty($defaults)) {
$this->defaults($defaults);
}
});
}
/**
* Set the route collection.
*
* @param \Illuminate\Routing\RouteCollectionInterface $routes
* @return $this
*/
public function setRoutes(RouteCollectionInterface $routes)
{
$this->routes = $routes;
return $this;
}
/**
* Get the session implementation from the resolver.
*
* @return \Illuminate\Session\Store|null
*/
protected function getSession()
{
if ($this->sessionResolver) {
return call_user_func($this->sessionResolver);
}
}
/**
* Set the session resolver for the generator.
*
* @param callable $sessionResolver
* @return $this
*/
public function setSessionResolver(callable $sessionResolver)
{
$this->sessionResolver = $sessionResolver;
return $this;
}
/**
* Set the encryption key resolver.
*
* @param callable $keyResolver
* @return $this
*/
public function setKeyResolver(callable $keyResolver)
{
$this->keyResolver = $keyResolver;
return $this;
}
/**
* Clone a new instance of the URL generator with a different encryption key resolver.
*
* @param callable $keyResolver
* @return \Illuminate\Routing\UrlGenerator
*/
public function withKeyResolver(callable $keyResolver)
{
return (clone $this)->setKeyResolver($keyResolver);
}
/**
* Set the callback that should be used to attempt to resolve missing named routes.
*
* @param callable $missingNamedRouteResolver
* @return $this
*/
public function resolveMissingNamedRoutesUsing(callable $missingNamedRouteResolver)
{
$this->missingNamedRouteResolver = $missingNamedRouteResolver;
return $this;
}
/**
* Get the root controller namespace.
*
* @return string
*/
public function getRootControllerNamespace()
{
return $this->rootNamespace;
}
/**
* Set the root controller namespace.
*
* @param string $rootNamespace
* @return $this
*/
public function setRootControllerNamespace($rootNamespace)
{
$this->rootNamespace = $rootNamespace;
return $this;
}
}
framework/src/Illuminate/Routing/composer.json 0000644 00000002731 15242753746 0015626 0 ustar 00 {
"name": "illuminate/routing",
"description": "The Illuminate Routing package.",
"license": "MIT",
"homepage": "https://laravel.com",
"support": {
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"require": {
"php": "^8.1",
"ext-filter": "*",
"ext-hash": "*",
"illuminate/collections": "^10.0",
"illuminate/container": "^10.0",
"illuminate/contracts": "^10.0",
"illuminate/http": "^10.0",
"illuminate/macroable": "^10.0",
"illuminate/pipeline": "^10.0",
"illuminate/session": "^10.0",
"illuminate/support": "^10.0",
"symfony/http-foundation": "^6.4",
"symfony/http-kernel": "^6.2",
"symfony/routing": "^6.2"
},
"autoload": {
"psr-4": {
"Illuminate\\Routing\\": ""
}
},
"extra": {
"branch-alias": {
"dev-master": "10.x-dev"
}
},
"suggest": {
"illuminate/console": "Required to use the make commands (^10.0).",
"nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).",
"symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^2.0)."
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev"
}
framework/src/Illuminate/Routing/RouteFileRegistrar.php 0000644 00000001201 15242753746 0017365 0 ustar 00 router = $router;
}
/**
* Require the given routes file.
*
* @param string $routes
* @return void
*/
public function register($routes)
{
$router = $this->router;
require $routes;
}
}
framework/src/Illuminate/Routing/AbstractRouteCollection.php 0000644 00000021031 15242753746 0020405 0 ustar 00 bind($request);
}
// If no route was found we will now check if a matching route is specified by
// another HTTP verb. If it is we will need to throw a MethodNotAllowed and
// inform the user agent of which HTTP verb it should use for this route.
$others = $this->checkForAlternateVerbs($request);
if (count($others) > 0) {
return $this->getRouteForMethods($request, $others);
}
throw new NotFoundHttpException(sprintf(
'The route %s could not be found.',
$request->path()
));
}
/**
* Determine if any routes match on another HTTP verb.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
protected function checkForAlternateVerbs($request)
{
$methods = array_diff(Router::$verbs, [$request->getMethod()]);
// Here we will spin through all verbs except for the current request verb and
// check to see if any routes respond to them. If they do, we will return a
// proper error response with the correct headers on the response string.
return array_values(array_filter(
$methods,
function ($method) use ($request) {
return ! is_null($this->matchAgainstRoutes($this->get($method), $request, false));
}
));
}
/**
* Determine if a route in the array matches the request.
*
* @param \Illuminate\Routing\Route[] $routes
* @param \Illuminate\Http\Request $request
* @param bool $includingMethod
* @return \Illuminate\Routing\Route|null
*/
protected function matchAgainstRoutes(array $routes, $request, $includingMethod = true)
{
[$fallbacks, $routes] = collect($routes)->partition(function ($route) {
return $route->isFallback;
});
return $routes->merge($fallbacks)->first(
fn (Route $route) => $route->matches($request, $includingMethod)
);
}
/**
* Get a route (if necessary) that responds when other available methods are present.
*
* @param \Illuminate\Http\Request $request
* @param string[] $methods
* @return \Illuminate\Routing\Route
*
* @throws \Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
*/
protected function getRouteForMethods($request, array $methods)
{
if ($request->isMethod('OPTIONS')) {
return (new Route('OPTIONS', $request->path(), function () use ($methods) {
return new Response('', 200, ['Allow' => implode(',', $methods)]);
}))->bind($request);
}
$this->requestMethodNotAllowed($request, $methods, $request->method());
}
/**
* Throw a method not allowed HTTP exception.
*
* @param \Illuminate\Http\Request $request
* @param array $others
* @param string $method
* @return void
*
* @throws \Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
*/
protected function requestMethodNotAllowed($request, array $others, $method)
{
throw new MethodNotAllowedHttpException(
$others,
sprintf(
'The %s method is not supported for route %s. Supported methods: %s.',
$method,
$request->path(),
implode(', ', $others)
)
);
}
/**
* Throw a method not allowed HTTP exception.
*
* @param array $others
* @param string $method
* @return void
*
* @deprecated use requestMethodNotAllowed
*
* @throws \Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
*/
protected function methodNotAllowed(array $others, $method)
{
throw new MethodNotAllowedHttpException(
$others,
sprintf(
'The %s method is not supported for this route. Supported methods: %s.',
$method,
implode(', ', $others)
)
);
}
/**
* Compile the routes for caching.
*
* @return array
*/
public function compile()
{
$compiled = $this->dumper()->getCompiledRoutes();
$attributes = [];
foreach ($this->getRoutes() as $route) {
$attributes[$route->getName()] = [
'methods' => $route->methods(),
'uri' => $route->uri(),
'action' => $route->getAction(),
'fallback' => $route->isFallback,
'defaults' => $route->defaults,
'wheres' => $route->wheres,
'bindingFields' => $route->bindingFields(),
'lockSeconds' => $route->locksFor(),
'waitSeconds' => $route->waitsFor(),
'withTrashed' => $route->allowsTrashedBindings(),
];
}
return compact('compiled', 'attributes');
}
/**
* Return the CompiledUrlMatcherDumper instance for the route collection.
*
* @return \Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherDumper
*/
public function dumper()
{
return new CompiledUrlMatcherDumper($this->toSymfonyRouteCollection());
}
/**
* Convert the collection to a Symfony RouteCollection instance.
*
* @return \Symfony\Component\Routing\RouteCollection
*/
public function toSymfonyRouteCollection()
{
$symfonyRoutes = new SymfonyRouteCollection;
$routes = $this->getRoutes();
foreach ($routes as $route) {
if (! $route->isFallback) {
$symfonyRoutes = $this->addToSymfonyRoutesCollection($symfonyRoutes, $route);
}
}
foreach ($routes as $route) {
if ($route->isFallback) {
$symfonyRoutes = $this->addToSymfonyRoutesCollection($symfonyRoutes, $route);
}
}
return $symfonyRoutes;
}
/**
* Add a route to the SymfonyRouteCollection instance.
*
* @param \Symfony\Component\Routing\RouteCollection $symfonyRoutes
* @param \Illuminate\Routing\Route $route
* @return \Symfony\Component\Routing\RouteCollection
*
* @throws \LogicException
*/
protected function addToSymfonyRoutesCollection(SymfonyRouteCollection $symfonyRoutes, Route $route)
{
$name = $route->getName();
if (
! is_null($name)
&& str_ends_with($name, '.')
&& ! is_null($symfonyRoutes->get($name))
) {
$name = null;
}
if (! $name) {
$route->name($this->generateRouteName());
$this->add($route);
} elseif (! is_null($symfonyRoutes->get($name))) {
throw new LogicException("Unable to prepare route [{$route->uri}] for serialization. Another route has already been assigned name [{$name}].");
}
$symfonyRoutes->add($route->getName(), $route->toSymfonyRoute());
return $symfonyRoutes;
}
/**
* Get a randomly generated route name.
*
* @return string
*/
protected function generateRouteName()
{
return 'generated::'.Str::random();
}
/**
* Get an iterator for the items.
*
* @return \ArrayIterator
*/
public function getIterator(): Traversable
{
return new ArrayIterator($this->getRoutes());
}
/**
* Count the number of items in the collection.
*
* @return int
*/
public function count(): int
{
return count($this->getRoutes());
}
}
framework/src/Illuminate/Routing/CreatesRegularExpressionRouteConstraints.php 0000644 00000004611 15242753746 0024053 0 ustar 00 assignExpressionToParameters($parameters, '[a-zA-Z]+');
}
/**
* Specify that the given route parameters must be alphanumeric.
*
* @param array|string $parameters
* @return $this
*/
public function whereAlphaNumeric($parameters)
{
return $this->assignExpressionToParameters($parameters, '[a-zA-Z0-9]+');
}
/**
* Specify that the given route parameters must be numeric.
*
* @param array|string $parameters
* @return $this
*/
public function whereNumber($parameters)
{
return $this->assignExpressionToParameters($parameters, '[0-9]+');
}
/**
* Specify that the given route parameters must be ULIDs.
*
* @param array|string $parameters
* @return $this
*/
public function whereUlid($parameters)
{
return $this->assignExpressionToParameters($parameters, '[0-7][0-9a-hjkmnp-tv-zA-HJKMNP-TV-Z]{25}');
}
/**
* Specify that the given route parameters must be UUIDs.
*
* @param array|string $parameters
* @return $this
*/
public function whereUuid($parameters)
{
return $this->assignExpressionToParameters($parameters, '[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}');
}
/**
* Specify that the given route parameters must be one of the given values.
*
* @param array|string $parameters
* @param array $values
* @return $this
*/
public function whereIn($parameters, array $values)
{
return $this->assignExpressionToParameters($parameters, implode('|', $values));
}
/**
* Apply the given regular expression to the given parameters.
*
* @param array|string $parameters
* @param string $expression
* @return $this
*/
protected function assignExpressionToParameters($parameters, $expression)
{
return $this->where(collect(Arr::wrap($parameters))
->mapWithKeys(fn ($parameter) => [$parameter => $expression])
->all());
}
}
framework/src/Illuminate/Routing/RoutingServiceProvider.php 0000644 00000015562 15242753746 0020306 0 ustar 00 registerRouter();
$this->registerUrlGenerator();
$this->registerRedirector();
$this->registerPsrRequest();
$this->registerPsrResponse();
$this->registerResponseFactory();
$this->registerCallableDispatcher();
$this->registerControllerDispatcher();
}
/**
* Register the router instance.
*
* @return void
*/
protected function registerRouter()
{
$this->app->singleton('router', function ($app) {
return new Router($app['events'], $app);
});
}
/**
* Register the URL generator service.
*
* @return void
*/
protected function registerUrlGenerator()
{
$this->app->singleton('url', function ($app) {
$routes = $app['router']->getRoutes();
// The URL generator needs the route collection that exists on the router.
// Keep in mind this is an object, so we're passing by references here
// and all the registered routes will be available to the generator.
$app->instance('routes', $routes);
return new UrlGenerator(
$routes, $app->rebinding(
'request', $this->requestRebinder()
), $app['config']['app.asset_url']
);
});
$this->app->extend('url', function (UrlGeneratorContract $url, $app) {
// Next we will set a few service resolvers on the URL generator so it can
// get the information it needs to function. This just provides some of
// the convenience features to this URL generator like "signed" URLs.
$url->setSessionResolver(function () {
return $this->app['session'] ?? null;
});
$url->setKeyResolver(function () {
return $this->app->make('config')->get('app.key');
});
// If the route collection is "rebound", for example, when the routes stay
// cached for the application, we will need to rebind the routes on the
// URL generator instance so it has the latest version of the routes.
$app->rebinding('routes', function ($app, $routes) {
$app['url']->setRoutes($routes);
});
return $url;
});
}
/**
* Get the URL generator request rebinder.
*
* @return \Closure
*/
protected function requestRebinder()
{
return function ($app, $request) {
$app['url']->setRequest($request);
};
}
/**
* Register the Redirector service.
*
* @return void
*/
protected function registerRedirector()
{
$this->app->singleton('redirect', function ($app) {
$redirector = new Redirector($app['url']);
// If the session is set on the application instance, we'll inject it into
// the redirector instance. This allows the redirect responses to allow
// for the quite convenient "with" methods that flash to the session.
if (isset($app['session.store'])) {
$redirector->setSession($app['session.store']);
}
return $redirector;
});
}
/**
* Register a binding for the PSR-7 request implementation.
*
* @return void
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
protected function registerPsrRequest()
{
$this->app->bind(ServerRequestInterface::class, function ($app) {
if (class_exists(Psr17Factory::class) && class_exists(PsrHttpFactory::class)) {
$psr17Factory = new Psr17Factory;
return with((new PsrHttpFactory($psr17Factory, $psr17Factory, $psr17Factory, $psr17Factory))
->createRequest($illuminateRequest = $app->make('request')), function (ServerRequestInterface $request) use ($illuminateRequest) {
if ($illuminateRequest->getContentTypeFormat() !== 'json' && $illuminateRequest->request->count() === 0) {
return $request;
}
return $request->withParsedBody(
array_merge($request->getParsedBody() ?? [], $illuminateRequest->getPayload()->all())
);
});
}
throw new BindingResolutionException('Unable to resolve PSR request. Please install the symfony/psr-http-message-bridge and nyholm/psr7 packages.');
});
}
/**
* Register a binding for the PSR-7 response implementation.
*
* @return void
*
* @throws \Illuminate\Contracts\Container\BindingResolutionException
*/
protected function registerPsrResponse()
{
$this->app->bind(ResponseInterface::class, function () {
if (class_exists(PsrResponse::class)) {
return new PsrResponse;
}
throw new BindingResolutionException('Unable to resolve PSR response. Please install the nyholm/psr7 package.');
});
}
/**
* Register the response factory implementation.
*
* @return void
*/
protected function registerResponseFactory()
{
$this->app->singleton(ResponseFactoryContract::class, function ($app) {
return new ResponseFactory($app[ViewFactoryContract::class], $app['redirect']);
});
}
/**
* Register the callable dispatcher.
*
* @return void
*/
protected function registerCallableDispatcher()
{
$this->app->singleton(CallableDispatcherContract::class, function ($app) {
return new CallableDispatcher($app);
});
}
/**
* Register the controller dispatcher.
*
* @return void
*/
protected function registerControllerDispatcher()
{
$this->app->singleton(ControllerDispatcherContract::class, function ($app) {
return new ControllerDispatcher($app);
});
}
}
framework/src/Illuminate/Conditionable/Traits/Conditionable.php 0000644 00000004314 15242753746 0020757 0 ustar 00 condition($value);
}
if ($value) {
return $callback($this, $value) ?? $this;
} elseif ($default) {
return $default($this, $value) ?? $this;
}
return $this;
}
/**
* Apply the callback if the given "value" is (or resolves to) falsy.
*
* @template TUnlessParameter
* @template TUnlessReturnType
*
* @param (\Closure($this): TUnlessParameter)|TUnlessParameter|null $value
* @param (callable($this, TUnlessParameter): TUnlessReturnType)|null $callback
* @param (callable($this, TUnlessParameter): TUnlessReturnType)|null $default
* @return $this|TUnlessReturnType
*/
public function unless($value = null, callable $callback = null, callable $default = null)
{
$value = $value instanceof Closure ? $value($this) : $value;
if (func_num_args() === 0) {
return (new HigherOrderWhenProxy($this))->negateConditionOnCapture();
}
if (func_num_args() === 1) {
return (new HigherOrderWhenProxy($this))->condition(! $value);
}
if (! $value) {
return $callback($this, $value) ?? $this;
} elseif ($default) {
return $default($this, $value) ?? $this;
}
return $this;
}
}
framework/src/Illuminate/Conditionable/composer.json 0000644 00000001363 15242753746 0016751 0 ustar 00 {
"name": "illuminate/conditionable",
"description": "The Illuminate Conditionable package.",
"license": "MIT",
"homepage": "https://laravel.com",
"support": {
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"require": {
"php": "^8.0.2"
},
"autoload": {
"psr-4": {
"Illuminate\\Support\\": ""
}
},
"extra": {
"branch-alias": {
"dev-master": "10.x-dev"
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev"
}
framework/src/Illuminate/Conditionable/HigherOrderWhenProxy.php 0000644 00000004311 15242753746 0021022 0 ustar 00 target = $target;
}
/**
* Set the condition on the proxy.
*
* @param bool $condition
* @return $this
*/
public function condition($condition)
{
[$this->condition, $this->hasCondition] = [$condition, true];
return $this;
}
/**
* Indicate that the condition should be negated.
*
* @return $this
*/
public function negateConditionOnCapture()
{
$this->negateConditionOnCapture = true;
return $this;
}
/**
* Proxy accessing an attribute onto the target.
*
* @param string $key
* @return mixed
*/
public function __get($key)
{
if (! $this->hasCondition) {
$condition = $this->target->{$key};
return $this->condition($this->negateConditionOnCapture ? ! $condition : $condition);
}
return $this->condition
? $this->target->{$key}
: $this->target;
}
/**
* Proxy a method call on the target.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
if (! $this->hasCondition) {
$condition = $this->target->{$method}(...$parameters);
return $this->condition($this->negateConditionOnCapture ? ! $condition : $condition);
}
return $this->condition
? $this->target->{$method}(...$parameters)
: $this->target;
}
}
framework/src/Illuminate/Conditionable/LICENSE.md 0000644 00000002063 15242753746 0015631 0 ustar 00 The MIT License (MIT)
Copyright (c) Taylor Otwell
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.
framework/src/Illuminate/Translation/lang/en/pagination.php 0000644 00000001026 15242753746 0020134 0 ustar 00 '« Previous',
'next' => 'Next »',
];
framework/src/Illuminate/Translation/lang/en/auth.php 0000644 00000001242 15242753746 0016744 0 ustar 00 'These credentials do not match our records.',
'password' => 'The provided password is incorrect.',
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
];
framework/src/Illuminate/Translation/lang/en/passwords.php 0000644 00000001350 15242753746 0020030 0 ustar 00 'Your password has been reset.',
'sent' => 'We have emailed your password reset link.',
'throttled' => 'Please wait before retrying.',
'token' => 'This password reset token is invalid.',
'user' => "We can't find a user with that email address.",
];
framework/src/Illuminate/Translation/lang/en/validation.php 0000644 00000025741 15242753746 0020147 0 ustar 00 'The :attribute field must be accepted.',
'accepted_if' => 'The :attribute field must be accepted when :other is :value.',
'active_url' => 'The :attribute field must be a valid URL.',
'after' => 'The :attribute field must be a date after :date.',
'after_or_equal' => 'The :attribute field must be a date after or equal to :date.',
'alpha' => 'The :attribute field must only contain letters.',
'alpha_dash' => 'The :attribute field must only contain letters, numbers, dashes, and underscores.',
'alpha_num' => 'The :attribute field must only contain letters and numbers.',
'array' => 'The :attribute field must be an array.',
'ascii' => 'The :attribute field must only contain single-byte alphanumeric characters and symbols.',
'before' => 'The :attribute field must be a date before :date.',
'before_or_equal' => 'The :attribute field must be a date before or equal to :date.',
'between' => [
'array' => 'The :attribute field must have between :min and :max items.',
'file' => 'The :attribute field must be between :min and :max kilobytes.',
'numeric' => 'The :attribute field must be between :min and :max.',
'string' => 'The :attribute field must be between :min and :max characters.',
],
'boolean' => 'The :attribute field must be true or false.',
'can' => 'The :attribute field contains an unauthorized value.',
'confirmed' => 'The :attribute field confirmation does not match.',
'current_password' => 'The password is incorrect.',
'date' => 'The :attribute field must be a valid date.',
'date_equals' => 'The :attribute field must be a date equal to :date.',
'date_format' => 'The :attribute field must match the format :format.',
'decimal' => 'The :attribute field must have :decimal decimal places.',
'declined' => 'The :attribute field must be declined.',
'declined_if' => 'The :attribute field must be declined when :other is :value.',
'different' => 'The :attribute field and :other must be different.',
'digits' => 'The :attribute field must be :digits digits.',
'digits_between' => 'The :attribute field must be between :min and :max digits.',
'dimensions' => 'The :attribute field has invalid image dimensions.',
'distinct' => 'The :attribute field has a duplicate value.',
'doesnt_end_with' => 'The :attribute field must not end with one of the following: :values.',
'doesnt_start_with' => 'The :attribute field must not start with one of the following: :values.',
'email' => 'The :attribute field must be a valid email address.',
'ends_with' => 'The :attribute field must end with one of the following: :values.',
'enum' => 'The selected :attribute is invalid.',
'exists' => 'The selected :attribute is invalid.',
'extensions' => 'The :attribute field must have one of the following extensions: :values.',
'file' => 'The :attribute field must be a file.',
'filled' => 'The :attribute field must have a value.',
'gt' => [
'array' => 'The :attribute field must have more than :value items.',
'file' => 'The :attribute field must be greater than :value kilobytes.',
'numeric' => 'The :attribute field must be greater than :value.',
'string' => 'The :attribute field must be greater than :value characters.',
],
'gte' => [
'array' => 'The :attribute field must have :value items or more.',
'file' => 'The :attribute field must be greater than or equal to :value kilobytes.',
'numeric' => 'The :attribute field must be greater than or equal to :value.',
'string' => 'The :attribute field must be greater than or equal to :value characters.',
],
'hex_color' => 'The :attribute field must be a valid hexadecimal color.',
'image' => 'The :attribute field must be an image.',
'in' => 'The selected :attribute is invalid.',
'in_array' => 'The :attribute field must exist in :other.',
'integer' => 'The :attribute field must be an integer.',
'ip' => 'The :attribute field must be a valid IP address.',
'ipv4' => 'The :attribute field must be a valid IPv4 address.',
'ipv6' => 'The :attribute field must be a valid IPv6 address.',
'json' => 'The :attribute field must be a valid JSON string.',
'lowercase' => 'The :attribute field must be lowercase.',
'lt' => [
'array' => 'The :attribute field must have less than :value items.',
'file' => 'The :attribute field must be less than :value kilobytes.',
'numeric' => 'The :attribute field must be less than :value.',
'string' => 'The :attribute field must be less than :value characters.',
],
'lte' => [
'array' => 'The :attribute field must not have more than :value items.',
'file' => 'The :attribute field must be less than or equal to :value kilobytes.',
'numeric' => 'The :attribute field must be less than or equal to :value.',
'string' => 'The :attribute field must be less than or equal to :value characters.',
],
'mac_address' => 'The :attribute field must be a valid MAC address.',
'max' => [
'array' => 'The :attribute field must not have more than :max items.',
'file' => 'The :attribute field must not be greater than :max kilobytes.',
'numeric' => 'The :attribute field must not be greater than :max.',
'string' => 'The :attribute field must not be greater than :max characters.',
],
'max_digits' => 'The :attribute field must not have more than :max digits.',
'mimes' => 'The :attribute field must be a file of type: :values.',
'mimetypes' => 'The :attribute field must be a file of type: :values.',
'min' => [
'array' => 'The :attribute field must have at least :min items.',
'file' => 'The :attribute field must be at least :min kilobytes.',
'numeric' => 'The :attribute field must be at least :min.',
'string' => 'The :attribute field must be at least :min characters.',
],
'min_digits' => 'The :attribute field must have at least :min digits.',
'missing' => 'The :attribute field must be missing.',
'missing_if' => 'The :attribute field must be missing when :other is :value.',
'missing_unless' => 'The :attribute field must be missing unless :other is :value.',
'missing_with' => 'The :attribute field must be missing when :values is present.',
'missing_with_all' => 'The :attribute field must be missing when :values are present.',
'multiple_of' => 'The :attribute field must be a multiple of :value.',
'not_in' => 'The selected :attribute is invalid.',
'not_regex' => 'The :attribute field format is invalid.',
'numeric' => 'The :attribute field must be a number.',
'password' => [
'letters' => 'The :attribute field must contain at least one letter.',
'mixed' => 'The :attribute field must contain at least one uppercase and one lowercase letter.',
'numbers' => 'The :attribute field must contain at least one number.',
'symbols' => 'The :attribute field must contain at least one symbol.',
'uncompromised' => 'The given :attribute has appeared in a data leak. Please choose a different :attribute.',
],
'present' => 'The :attribute field must be present.',
'present_if' => 'The :attribute field must be present when :other is :value.',
'present_unless' => 'The :attribute field must be present unless :other is :value.',
'present_with' => 'The :attribute field must be present when :values is present.',
'present_with_all' => 'The :attribute field must be present when :values are present.',
'prohibited' => 'The :attribute field is prohibited.',
'prohibited_if' => 'The :attribute field is prohibited when :other is :value.',
'prohibited_unless' => 'The :attribute field is prohibited unless :other is in :values.',
'prohibits' => 'The :attribute field prohibits :other from being present.',
'regex' => 'The :attribute field format is invalid.',
'required' => 'The :attribute field is required.',
'required_array_keys' => 'The :attribute field must contain entries for: :values.',
'required_if' => 'The :attribute field is required when :other is :value.',
'required_if_accepted' => 'The :attribute field is required when :other is accepted.',
'required_unless' => 'The :attribute field is required unless :other is in :values.',
'required_with' => 'The :attribute field is required when :values is present.',
'required_with_all' => 'The :attribute field is required when :values are present.',
'required_without' => 'The :attribute field is required when :values is not present.',
'required_without_all' => 'The :attribute field is required when none of :values are present.',
'same' => 'The :attribute field must match :other.',
'size' => [
'array' => 'The :attribute field must contain :size items.',
'file' => 'The :attribute field must be :size kilobytes.',
'numeric' => 'The :attribute field must be :size.',
'string' => 'The :attribute field must be :size characters.',
],
'starts_with' => 'The :attribute field must start with one of the following: :values.',
'string' => 'The :attribute field must be a string.',
'timezone' => 'The :attribute field must be a valid timezone.',
'unique' => 'The :attribute has already been taken.',
'uploaded' => 'The :attribute failed to upload.',
'uppercase' => 'The :attribute field must be uppercase.',
'url' => 'The :attribute field must be a valid URL.',
'ulid' => 'The :attribute field must be a valid ULID.',
'uuid' => 'The :attribute field must be a valid UUID.',
/*
|--------------------------------------------------------------------------
| Custom Validation Language Lines
|--------------------------------------------------------------------------
|
| Here you may specify custom validation messages for attributes using the
| convention "attribute.rule" to name the lines. This makes it quick to
| specify a specific custom language line for a given attribute rule.
|
*/
'custom' => [
'attribute-name' => [
'rule-name' => 'custom-message',
],
],
/*
|--------------------------------------------------------------------------
| Custom Validation Attributes
|--------------------------------------------------------------------------
|
| The following language lines are used to swap our attribute placeholder
| with something more reader friendly such as "E-Mail Address" instead
| of "email". This simply helps us make our message more expressive.
|
*/
'attributes' => [],
];
framework/src/Illuminate/Translation/CreatesPotentiallyTranslatedStrings.php 0000644 00000003150 15242753746 0023667 0 ustar 00 $this->messages[] = $message
: fn ($message) => $this->messages[$attribute] = $message;
return new class($message ?? $attribute, $this->validator->getTranslator(), $destructor) extends PotentiallyTranslatedString
{
/**
* The callback to call when the object destructs.
*
* @var \Closure
*/
protected $destructor;
/**
* Create a new pending potentially translated string.
*
* @param string $message
* @param \Illuminate\Contracts\Translation\Translator $translator
* @param \Closure $destructor
*/
public function __construct($message, $translator, $destructor)
{
parent::__construct($message, $translator);
$this->destructor = $destructor;
}
/**
* Handle the object's destruction.
*
* @return void
*/
public function __destruct()
{
($this->destructor)($this->toString());
}
};
}
}
framework/src/Illuminate/Translation/TranslationServiceProvider.php 0000644 00000002656 15242753746 0022024 0 ustar 00 registerLoader();
$this->app->singleton('translator', function ($app) {
$loader = $app['translation.loader'];
// When registering the translator component, we'll need to set the default
// locale as well as the fallback locale. So, we'll grab the application
// configuration so we can easily get both of these values from there.
$locale = $app->getLocale();
$trans = new Translator($loader, $locale);
$trans->setFallback($app->getFallbackLocale());
return $trans;
});
}
/**
* Register the translation line loader.
*
* @return void
*/
protected function registerLoader()
{
$this->app->singleton('translation.loader', function ($app) {
return new FileLoader($app['files'], [__DIR__.'/lang', $app['path.lang']]);
});
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return ['translator', 'translation.loader'];
}
}
framework/src/Illuminate/Translation/ArrayLoader.php 0000644 00000003117 15242753746 0016670 0 ustar 00 messages[$namespace][$locale][$group] ?? [];
}
/**
* Add a new namespace to the loader.
*
* @param string $namespace
* @param string $hint
* @return void
*/
public function addNamespace($namespace, $hint)
{
//
}
/**
* Add a new JSON path to the loader.
*
* @param string $path
* @return void
*/
public function addJsonPath($path)
{
//
}
/**
* Add messages to the loader.
*
* @param string $locale
* @param string $group
* @param array $messages
* @param string|null $namespace
* @return $this
*/
public function addMessages($locale, $group, array $messages, $namespace = null)
{
$namespace = $namespace ?: '*';
$this->messages[$namespace][$locale][$group] = $messages;
return $this;
}
/**
* Get an array of all the registered namespaces.
*
* @return array
*/
public function namespaces()
{
return [];
}
}
framework/src/Illuminate/Translation/PotentiallyTranslatedString.php 0000644 00000004027 15242753746 0022201 0 ustar 00 string = $string;
$this->translator = $translator;
}
/**
* Translate the string.
*
* @param array $replace
* @param string|null $locale
* @return $this
*/
public function translate($replace = [], $locale = null)
{
$this->translation = $this->translator->get($this->string, $replace, $locale);
return $this;
}
/**
* Translates the string based on a count.
*
* @param \Countable|int|float|array $number
* @param array $replace
* @param string|null $locale
* @return $this
*/
public function translateChoice($number, array $replace = [], $locale = null)
{
$this->translation = $this->translator->choice($this->string, $number, $replace, $locale);
return $this;
}
/**
* Get the original string.
*
* @return string
*/
public function original()
{
return $this->string;
}
/**
* Get the potentially translated string.
*
* @return string
*/
public function __toString()
{
return $this->translation ?? $this->string;
}
/**
* Get the potentially translated string.
*
* @return string
*/
public function toString()
{
return (string) $this;
}
}
framework/src/Illuminate/Translation/composer.json 0000644 00000001677 15242753746 0016505 0 ustar 00 {
"name": "illuminate/translation",
"description": "The Illuminate Translation package.",
"license": "MIT",
"homepage": "https://laravel.com",
"support": {
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
}
],
"require": {
"php": "^8.1",
"illuminate/collections": "^10.0",
"illuminate/contracts": "^10.0",
"illuminate/macroable": "^10.0",
"illuminate/filesystem": "^10.0",
"illuminate/support": "^10.0"
},
"autoload": {
"psr-4": {
"Illuminate\\Translation\\": ""
}
},
"extra": {
"branch-alias": {
"dev-master": "10.x-dev"
}
},
"config": {
"sort-packages": true
},
"minimum-stability": "dev"
}
framework/src/Illuminate/Translation/MessageSelector.php 0000644 00000026627 15242753746 0017563 0 ustar 00 extract($segments, $number)) !== null) {
return trim($value);
}
$segments = $this->stripConditions($segments);
$pluralIndex = $this->getPluralIndex($locale, $number);
if (count($segments) === 1 || ! isset($segments[$pluralIndex])) {
return $segments[0];
}
return $segments[$pluralIndex];
}
/**
* Extract a translation string using inline conditions.
*
* @param array $segments
* @param int $number
* @return mixed
*/
private function extract($segments, $number)
{
foreach ($segments as $part) {
if (! is_null($line = $this->extractFromString($part, $number))) {
return $line;
}
}
}
/**
* Get the translation string if the condition matches.
*
* @param string $part
* @param int $number
* @return mixed
*/
private function extractFromString($part, $number)
{
preg_match('/^[\{\[]([^\[\]\{\}]*)[\}\]](.*)/s', $part, $matches);
if (count($matches) !== 3) {
return null;
}
$condition = $matches[1];
$value = $matches[2];
if (str_contains($condition, ',')) {
[$from, $to] = explode(',', $condition, 2);
if ($to === '*' && $number >= $from) {
return $value;
} elseif ($from === '*' && $number <= $to) {
return $value;
} elseif ($number >= $from && $number <= $to) {
return $value;
}
}
return $condition == $number ? $value : null;
}
/**
* Strip the inline conditions from each segment, just leaving the text.
*
* @param array $segments
* @return array
*/
private function stripConditions($segments)
{
return collect($segments)
->map(fn ($part) => preg_replace('/^[\{\[]([^\[\]\{\}]*)[\}\]]/', '', $part))
->all();
}
/**
* Get the index to use for pluralization.
*
* The plural rules are derived from code of the Zend Framework (2010-09-25), which
* is subject to the new BSD license (https://framework.zend.com/license)
* Copyright (c) 2005-2010 - Zend Technologies USA Inc. (http://www.zend.com)
*
* @param string $locale
* @param int $number
* @return int
*/
public function getPluralIndex($locale, $number)
{
switch ($locale) {
case 'az':
case 'az_AZ':
case 'bo':
case 'bo_CN':
case 'bo_IN':
case 'dz':
case 'dz_BT':
case 'id':
case 'id_ID':
case 'ja':
case 'ja_JP':
case 'jv':
case 'ka':
case 'ka_GE':
case 'km':
case 'km_KH':
case 'kn':
case 'kn_IN':
case 'ko':
case 'ko_KR':
case 'ms':
case 'ms_MY':
case 'th':
case 'th_TH':
case 'tr':
case 'tr_CY':
case 'tr_TR':
case 'vi':
case 'vi_VN':
case 'zh':
case 'zh_CN':
case 'zh_HK':
case 'zh_SG':
case 'zh_TW':
return 0;
case 'af':
case 'af_ZA':
case 'bn':
case 'bn_BD':
case 'bn_IN':
case 'bg':
case 'bg_BG':
case 'ca':
case 'ca_AD':
case 'ca_ES':
case 'ca_FR':
case 'ca_IT':
case 'da':
case 'da_DK':
case 'de':
case 'de_AT':
case 'de_BE':
case 'de_CH':
case 'de_DE':
case 'de_LI':
case 'de_LU':
case 'el':
case 'el_CY':
case 'el_GR':
case 'en':
case 'en_AG':
case 'en_AU':
case 'en_BW':
case 'en_CA':
case 'en_DK':
case 'en_GB':
case 'en_HK':
case 'en_IE':
case 'en_IN':
case 'en_NG':
case 'en_NZ':
case 'en_PH':
case 'en_SG':
case 'en_US':
case 'en_ZA':
case 'en_ZM':
case 'en_ZW':
case 'eo':
case 'eo_US':
case 'es':
case 'es_AR':
case 'es_BO':
case 'es_CL':
case 'es_CO':
case 'es_CR':
case 'es_CU':
case 'es_DO':
case 'es_EC':
case 'es_ES':
case 'es_GT':
case 'es_HN':
case 'es_MX':
case 'es_NI':
case 'es_PA':
case 'es_PE':
case 'es_PR':
case 'es_PY':
case 'es_SV':
case 'es_US':
case 'es_UY':
case 'es_VE':
case 'et':
case 'et_EE':
case 'eu':
case 'eu_ES':
case 'eu_FR':
case 'fa':
case 'fa_IR':
case 'fi':
case 'fi_FI':
case 'fo':
case 'fo_FO':
case 'fur':
case 'fur_IT':
case 'fy':
case 'fy_DE':
case 'fy_NL':
case 'gl':
case 'gl_ES':
case 'gu':
case 'gu_IN':
case 'ha':
case 'ha_NG':
case 'he':
case 'he_IL':
case 'hu':
case 'hu_HU':
case 'is':
case 'is_IS':
case 'it':
case 'it_CH':
case 'it_IT':
case 'ku':
case 'ku_TR':
case 'lb':
case 'lb_LU':
case 'ml':
case 'ml_IN':
case 'mn':
case 'mn_MN':
case 'mr':
case 'mr_IN':
case 'nah':
case 'nb':
case 'nb_NO':
case 'ne':
case 'ne_NP':
case 'nl':
case 'nl_AW':
case 'nl_BE':
case 'nl_NL':
case 'nn':
case 'nn_NO':
case 'no':
case 'om':
case 'om_ET':
case 'om_KE':
case 'or':
case 'or_IN':
case 'pa':
case 'pa_IN':
case 'pa_PK':
case 'pap':
case 'pap_AN':
case 'pap_AW':
case 'pap_CW':
case 'ps':
case 'ps_AF':
case 'pt':
case 'pt_BR':
case 'pt_PT':
case 'so':
case 'so_DJ':
case 'so_ET':
case 'so_KE':
case 'so_SO':
case 'sq':
case 'sq_AL':
case 'sq_MK':
case 'sv':
case 'sv_FI':
case 'sv_SE':
case 'sw':
case 'sw_KE':
case 'sw_TZ':
case 'ta':
case 'ta_IN':
case 'ta_LK':
case 'te':
case 'te_IN':
case 'tk':
case 'tk_TM':
case 'ur':
case 'ur_IN':
case 'ur_PK':
case 'zu':
case 'zu_ZA':
return ($number == 1) ? 0 : 1;
case 'am':
case 'am_ET':
case 'bh':
case 'fil':
case 'fil_PH':
case 'fr':
case 'fr_BE':
case 'fr_CA':
case 'fr_CH':
case 'fr_FR':
case 'fr_LU':
case 'gun':
case 'hi':
case 'hi_IN':
case 'hy':
case 'hy_AM':
case 'ln':
case 'ln_CD':
case 'mg':
case 'mg_MG':
case 'nso':
case 'nso_ZA':
case 'ti':
case 'ti_ER':
case 'ti_ET':
case 'wa':
case 'wa_BE':
case 'xbr':
return (($number == 0) || ($number == 1)) ? 0 : 1;
case 'be':
case 'be_BY':
case 'bs':
case 'bs_BA':
case 'hr':
case 'hr_HR':
case 'ru':
case 'ru_RU':
case 'ru_UA':
case 'sr':
case 'sr_ME':
case 'sr_RS':
case 'uk':
case 'uk_UA':
return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
case 'cs':
case 'cs_CZ':
case 'sk':
case 'sk_SK':
return ($number == 1) ? 0 : ((($number >= 2) && ($number <= 4)) ? 1 : 2);
case 'ga':
case 'ga_IE':
return ($number == 1) ? 0 : (($number == 2) ? 1 : 2);
case 'lt':
case 'lt_LT':
return (($number % 10 == 1) && ($number % 100 != 11)) ? 0 : ((($number % 10 >= 2) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 1 : 2);
case 'sl':
case 'sl_SI':
return ($number % 100 == 1) ? 0 : (($number % 100 == 2) ? 1 : ((($number % 100 == 3) || ($number % 100 == 4)) ? 2 : 3));
case 'mk':
case 'mk_MK':
return ($number % 10 == 1) ? 0 : 1;
case 'mt':
case 'mt_MT':
return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 1) && ($number % 100 < 11))) ? 1 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 2 : 3));
case 'lv':
case 'lv_LV':
return ($number == 0) ? 0 : ((($number % 10 == 1) && ($number % 100 != 11)) ? 1 : 2);
case 'pl':
case 'pl_PL':
return ($number == 1) ? 0 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 1 : 2);
case 'cy':
case 'cy_GB':
return ($number == 1) ? 0 : (($number == 2) ? 1 : ((($number == 8) || ($number == 11)) ? 2 : 3));
case 'ro':
case 'ro_RO':
return ($number == 1) ? 0 : ((($number == 0) || (($number % 100 > 0) && ($number % 100 < 20))) ? 1 : 2);
case 'ar':
case 'ar_AE':
case 'ar_BH':
case 'ar_DZ':
case 'ar_EG':
case 'ar_IN':
case 'ar_IQ':
case 'ar_JO':
case 'ar_KW':
case 'ar_LB':
case 'ar_LY':
case 'ar_MA':
case 'ar_OM':
case 'ar_QA':
case 'ar_SA':
case 'ar_SD':
case 'ar_SS':
case 'ar_SY':
case 'ar_TN':
case 'ar_YE':
return ($number == 0) ? 0 : (($number == 1) ? 1 : (($number == 2) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : 5))));
default:
return 0;
}
}
}
framework/src/Illuminate/Translation/FileLoader.php 0000644 00000012052 15242753746 0016467 0 ustar 00 files = $files;
$this->paths = is_string($path) ? [$path] : $path;
}
/**
* Load the messages for the given locale.
*
* @param string $locale
* @param string $group
* @param string|null $namespace
* @return array
*/
public function load($locale, $group, $namespace = null)
{
if ($group === '*' && $namespace === '*') {
return $this->loadJsonPaths($locale);
}
if (is_null($namespace) || $namespace === '*') {
return $this->loadPaths($this->paths, $locale, $group);
}
return $this->loadNamespaced($locale, $group, $namespace);
}
/**
* Load a namespaced translation group.
*
* @param string $locale
* @param string $group
* @param string $namespace
* @return array
*/
protected function loadNamespaced($locale, $group, $namespace)
{
if (isset($this->hints[$namespace])) {
$lines = $this->loadPaths([$this->hints[$namespace]], $locale, $group);
return $this->loadNamespaceOverrides($lines, $locale, $group, $namespace);
}
return [];
}
/**
* Load a local namespaced translation group for overrides.
*
* @param array $lines
* @param string $locale
* @param string $group
* @param string $namespace
* @return array
*/
protected function loadNamespaceOverrides(array $lines, $locale, $group, $namespace)
{
return collect($this->paths)
->reduce(function ($output, $path) use ($lines, $locale, $group, $namespace) {
$file = "{$path}/vendor/{$namespace}/{$locale}/{$group}.php";
if ($this->files->exists($file)) {
$lines = array_replace_recursive($lines, $this->files->getRequire($file));
}
return $lines;
}, []);
}
/**
* Load a locale from a given path.
*
* @param array $paths
* @param string $locale
* @param string $group
* @return array
*/
protected function loadPaths(array $paths, $locale, $group)
{
return collect($paths)
->reduce(function ($output, $path) use ($locale, $group) {
if ($this->files->exists($full = "{$path}/{$locale}/{$group}.php")) {
$output = array_replace_recursive($output, $this->files->getRequire($full));
}
return $output;
}, []);
}
/**
* Load a locale from the given JSON file path.
*
* @param string $locale
* @return array
*
* @throws \RuntimeException
*/
protected function loadJsonPaths($locale)
{
return collect(array_merge($this->jsonPaths, $this->paths))
->reduce(function ($output, $path) use ($locale) {
if ($this->files->exists($full = "{$path}/{$locale}.json")) {
$decoded = json_decode($this->files->get($full), true);
if (is_null($decoded) || json_last_error() !== JSON_ERROR_NONE) {
throw new RuntimeException("Translation file [{$full}] contains an invalid JSON structure.");
}
$output = array_merge($output, $decoded);
}
return $output;
}, []);
}
/**
* Add a new namespace to the loader.
*
* @param string $namespace
* @param string $hint
* @return void
*/
public function addNamespace($namespace, $hint)
{
$this->hints[$namespace] = $hint;
}
/**
* Get an array of all the registered namespaces.
*
* @return array
*/
public function namespaces()
{
return $this->hints;
}
/**
* Add a new JSON path to the loader.
*
* @param string $path
* @return void
*/
public function addJsonPath($path)
{
$this->jsonPaths[] = $path;
}
/**
* Get an array of all the registered paths to JSON translation files.
*
* @return array
*/
public function jsonPaths()
{
return $this->jsonPaths;
}
}
framework/src/Illuminate/Translation/Translator.php 0000644 00000035045 15242753746 0016621 0 ustar 00 loader = $loader;
$this->setLocale($locale);
}
/**
* Determine if a translation exists for a given locale.
*
* @param string $key
* @param string|null $locale
* @return bool
*/
public function hasForLocale($key, $locale = null)
{
return $this->has($key, $locale, false);
}
/**
* Determine if a translation exists.
*
* @param string $key
* @param string|null $locale
* @param bool $fallback
* @return bool
*/
public function has($key, $locale = null, $fallback = true)
{
$locale = $locale ?: $this->locale;
$line = $this->get($key, [], $locale, $fallback);
// For JSON translations, the loaded files will contain the correct line.
// Otherwise, we must assume we are handling typical translation file
// and check if the returned line is not the same as the given key.
if (! is_null($this->loaded['*']['*'][$locale][$key] ?? null)) {
return true;
}
return $line !== $key;
}
/**
* Get the translation for the given key.
*
* @param string $key
* @param array $replace
* @param string|null $locale
* @param bool $fallback
* @return string|array
*/
public function get($key, array $replace = [], $locale = null, $fallback = true)
{
$locale = $locale ?: $this->locale;
// For JSON translations, there is only one file per locale, so we will simply load
// that file and then we will be ready to check the array for the key. These are
// only one level deep so we do not need to do any fancy searching through it.
$this->load('*', '*', $locale);
$line = $this->loaded['*']['*'][$locale][$key] ?? null;
// If we can't find a translation for the JSON key, we will attempt to translate it
// using the typical translation file. This way developers can always just use a
// helper such as __ instead of having to pick between trans or __ with views.
if (! isset($line)) {
[$namespace, $group, $item] = $this->parseKey($key);
// Here we will get the locale that should be used for the language line. If one
// was not passed, we will use the default locales which was given to us when
// the translator was instantiated. Then, we can load the lines and return.
$locales = $fallback ? $this->localeArray($locale) : [$locale];
foreach ($locales as $languageLineLocale) {
if (! is_null($line = $this->getLine(
$namespace, $group, $languageLineLocale, $item, $replace
))) {
return $line;
}
}
$key = $this->handleMissingTranslationKey(
$key, $replace, $locale, $fallback
);
}
// If the line doesn't exist, we will return back the key which was requested as
// that will be quick to spot in the UI if language keys are wrong or missing
// from the application's language files. Otherwise we can return the line.
return $this->makeReplacements($line ?: $key, $replace);
}
/**
* Get a translation according to an integer value.
*
* @param string $key
* @param \Countable|int|float|array $number
* @param array $replace
* @param string|null $locale
* @return string
*/
public function choice($key, $number, array $replace = [], $locale = null)
{
$line = $this->get(
$key, $replace, $locale = $this->localeForChoice($locale)
);
// If the given "number" is actually an array or countable we will simply count the
// number of elements in an instance. This allows developers to pass an array of
// items without having to count it on their end first which gives bad syntax.
if (is_countable($number)) {
$number = count($number);
}
$replace['count'] = $number;
return $this->makeReplacements(
$this->getSelector()->choose($line, $number, $locale), $replace
);
}
/**
* Get the proper locale for a choice operation.
*
* @param string|null $locale
* @return string
*/
protected function localeForChoice($locale)
{
return $locale ?: $this->locale ?: $this->fallback;
}
/**
* Retrieve a language line out the loaded array.
*
* @param string $namespace
* @param string $group
* @param string $locale
* @param string $item
* @param array $replace
* @return string|array|null
*/
protected function getLine($namespace, $group, $locale, $item, array $replace)
{
$this->load($namespace, $group, $locale);
$line = Arr::get($this->loaded[$namespace][$group][$locale], $item);
if (is_string($line)) {
return $this->makeReplacements($line, $replace);
} elseif (is_array($line) && count($line) > 0) {
array_walk_recursive($line, function (&$value, $key) use ($replace) {
$value = $this->makeReplacements($value, $replace);
});
return $line;
}
}
/**
* Make the place-holder replacements on a line.
*
* @param string $line
* @param array $replace
* @return string
*/
protected function makeReplacements($line, array $replace)
{
if (empty($replace)) {
return $line;
}
$shouldReplace = [];
foreach ($replace as $key => $value) {
if (is_object($value) && isset($this->stringableHandlers[get_class($value)])) {
$value = call_user_func($this->stringableHandlers[get_class($value)], $value);
}
$shouldReplace[':'.Str::ucfirst($key ?? '')] = Str::ucfirst($value ?? '');
$shouldReplace[':'.Str::upper($key ?? '')] = Str::upper($value ?? '');
$shouldReplace[':'.$key] = $value;
}
return strtr($line, $shouldReplace);
}
/**
* Add translation lines to the given locale.
*
* @param array $lines
* @param string $locale
* @param string $namespace
* @return void
*/
public function addLines(array $lines, $locale, $namespace = '*')
{
foreach ($lines as $key => $value) {
[$group, $item] = explode('.', $key, 2);
Arr::set($this->loaded, "$namespace.$group.$locale.$item", $value);
}
}
/**
* Load the specified language group.
*
* @param string $namespace
* @param string $group
* @param string $locale
* @return void
*/
public function load($namespace, $group, $locale)
{
if ($this->isLoaded($namespace, $group, $locale)) {
return;
}
// The loader is responsible for returning the array of language lines for the
// given namespace, group, and locale. We'll set the lines in this array of
// lines that have already been loaded so that we can easily access them.
$lines = $this->loader->load($locale, $group, $namespace);
$this->loaded[$namespace][$group][$locale] = $lines;
}
/**
* Determine if the given group has been loaded.
*
* @param string $namespace
* @param string $group
* @param string $locale
* @return bool
*/
protected function isLoaded($namespace, $group, $locale)
{
return isset($this->loaded[$namespace][$group][$locale]);
}
/**
* Handle a missing translation key.
*
* @param string $key
* @param array $replace
* @param string|null $locale
* @param bool $fallback
* @return string
*/
protected function handleMissingTranslationKey($key, $replace, $locale, $fallback)
{
if (! $this->handleMissingTranslationKeys ||
! isset($this->missingTranslationKeyCallback)) {
return $key;
}
// Prevent infinite loops...
$this->handleMissingTranslationKeys = false;
$key = call_user_func(
$this->missingTranslationKeyCallback,
$key, $replace, $locale, $fallback
) ?? $key;
$this->handleMissingTranslationKeys = true;
return $key;
}
/**
* Register a callback that is responsible for handling missing translation keys.
*
* @param callable|null $callback
* @return static
*/
public function handleMissingKeysUsing(?callable $callback)
{
$this->missingTranslationKeyCallback = $callback;
return $this;
}
/**
* Add a new namespace to the loader.
*
* @param string $namespace
* @param string $hint
* @return void
*/
public function addNamespace($namespace, $hint)
{
$this->loader->addNamespace($namespace, $hint);
}
/**
* Add a new JSON path to the loader.
*
* @param string $path
* @return void
*/
public function addJsonPath($path)
{
$this->loader->addJsonPath($path);
}
/**
* Parse a key into namespace, group, and item.
*
* @param string $key
* @return array
*/
public function parseKey($key)
{
$segments = parent::parseKey($key);
if (is_null($segments[0])) {
$segments[0] = '*';
}
return $segments;
}
/**
* Get the array of locales to be checked.
*
* @param string|null $locale
* @return array
*/
protected function localeArray($locale)
{
$locales = array_filter([$locale ?: $this->locale, $this->fallback]);
return call_user_func($this->determineLocalesUsing ?: fn () => $locales, $locales);
}
/**
* Specify a callback that should be invoked to determined the applicable locale array.
*
* @param callable $callback
* @return void
*/
public function determineLocalesUsing($callback)
{
$this->determineLocalesUsing = $callback;
}
/**
* Get the message selector instance.
*
* @return \Illuminate\Translation\MessageSelector
*/
public function getSelector()
{
if (! isset($this->selector)) {
$this->selector = new MessageSelector;
}
return $this->selector;
}
/**
* Set the message selector instance.
*
* @param \Illuminate\Translation\MessageSelector $selector
* @return void
*/
public function setSelector(MessageSelector $selector)
{
$this->selector = $selector;
}
/**
* Get the language line loader implementation.
*
* @return \Illuminate\Contracts\Translation\Loader
*/
public function getLoader()
{
return $this->loader;
}
/**
* Get the default locale being used.
*
* @return string
*/
public function locale()
{
return $this->getLocale();
}
/**
* Get the default locale being used.
*
* @return string
*/
public function getLocale()
{
return $this->locale;
}
/**
* Set the default locale.
*
* @param string $locale
* @return void
*
* @throws \InvalidArgumentException
*/
public function setLocale($locale)
{
if (Str::contains($locale, ['/', '\\'])) {
throw new InvalidArgumentException('Invalid characters present in locale.');
}
$this->locale = $locale;
}
/**
* Get the fallback locale being used.
*
* @return string
*/
public function getFallback()
{
return $this->fallback;
}
/**
* Set the fallback locale being used.
*
* @param string $fallback
* @return void
*/
public function setFallback($fallback)
{
$this->fallback = $fallback;
}
/**
* Set the loaded translation groups.
*
* @param array $loaded
* @return void
*/
public function setLoaded(array $loaded)
{
$this->loaded = $loaded;
}
/**
* Add a handler to be executed in order to format a given class to a string during translation replacements.
*
* @param callable|string $class
* @param callable|null $handler
* @return void
*/
public function stringable($class, $handler = null)
{
if ($class instanceof Closure) {
[$class, $handler] = [
$this->firstClosureParameterType($class),
$class,
];
}
$this->stringableHandlers[$class] = $handler;
}
}
framework/src/Illuminate/Translation/LICENSE.md 0000644 00000002063 15242753746 0015355 0 ustar 00 The MIT License (MIT)
Copyright (c) Taylor Otwell
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.
framework/src/Illuminate/Mail/Events/MessageSent.php 0000644 00000003527 15242753746 0016536 0 ustar 00 sent = $message;
$this->data = $data;
}
/**
* Get the serializable representation of the object.
*
* @return array
*/
public function __serialize()
{
$hasAttachments = collect($this->message->getAttachments())->isNotEmpty();
return [
'sent' => $this->sent,
'data' => $hasAttachments ? base64_encode(serialize($this->data)) : $this->data,
'hasAttachments' => $hasAttachments,
];
}
/**
* Marshal the object from its serialized data.
*
* @param array $data
* @return void
*/
public function __unserialize(array $data)
{
$this->sent = $data['sent'];
$this->data = (($data['hasAttachments'] ?? false) === true)
? unserialize(base64_decode($data['data']))
: $data['data'];
}
/**
* Dynamically get the original message.
*
* @param string $key
* @return mixed
*
* @throws \Exception
*/
public function __get($key)
{
if ($key === 'message') {
return $this->sent->getOriginalMessage();
}
throw new Exception('Unable to access undefined property on '.__CLASS__.': '.$key);
}
}
framework/src/Illuminate/Mail/Events/MessageSending.php 0000644 00000001145 15242753746 0017206 0 ustar 00 data = $data;
$this->message = $message;
}
}
framework/src/Illuminate/Mail/Transport/ArrayTransport.php 0000644 00000002655 15242753746 0020044 0 ustar 00 messages = new Collection;
}
/**
* {@inheritdoc}
*/
public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage
{
return $this->messages[] = new SentMessage($message, $envelope ?? Envelope::create($message));
}
/**
* Retrieve the collection of messages.
*
* @return \Illuminate\Support\Collection
*/
public function messages()
{
return $this->messages;
}
/**
* Clear all of the messages from the local collection.
*
* @return \Illuminate\Support\Collection
*/
public function flush()
{
return $this->messages = new Collection;
}
/**
* Get the string representation of the transport.
*
* @return string
*/
public function __toString(): string
{
return 'array';
}
}
framework/src/Illuminate/Mail/Transport/SesTransport.php 0000644 00000006603 15242753746 0017515 0 ustar 00 ses = $ses;
$this->options = $options;
parent::__construct();
}
/**
* {@inheritDoc}
*/
protected function doSend(SentMessage $message): void
{
$options = $this->options;
if ($message->getOriginalMessage() instanceof Message) {
foreach ($message->getOriginalMessage()->getHeaders()->all() as $header) {
if ($header instanceof MetadataHeader) {
$options['Tags'][] = ['Name' => $header->getKey(), 'Value' => $header->getValue()];
}
}
}
try {
$result = $this->ses->sendRawEmail(
array_merge(
$options, [
'Source' => $message->getEnvelope()->getSender()->toString(),
'Destinations' => collect($message->getEnvelope()->getRecipients())
->map
->toString()
->values()
->all(),
'RawMessage' => [
'Data' => $message->toString(),
],
]
)
);
} catch (AwsException $e) {
$reason = $e->getAwsErrorMessage() ?? $e->getMessage();
throw new TransportException(
sprintf('Request to AWS SES API failed. Reason: %s.', $reason),
is_int($e->getCode()) ? $e->getCode() : 0,
$e
);
}
$messageId = $result->get('MessageId');
$message->getOriginalMessage()->getHeaders()->addHeader('X-Message-ID', $messageId);
$message->getOriginalMessage()->getHeaders()->addHeader('X-SES-Message-ID', $messageId);
}
/**
* Get the Amazon SES client for the SesTransport instance.
*
* @return \Aws\Ses\SesClient
*/
public function ses()
{
return $this->ses;
}
/**
* Get the transmission options being used by the transport.
*
* @return array
*/
public function getOptions()
{
return $this->options;
}
/**
* Set the transmission options being used by the transport.
*
* @param array $options
* @return array
*/
public function setOptions(array $options)
{
return $this->options = $options;
}
/**
* Get the string representation of the transport.
*
* @return string
*/
public function __toString(): string
{
return 'ses';
}
}
framework/src/Illuminate/Mail/Transport/LogTransport.php 0000644 00000004605 15242753746 0017504 0 ustar 00 logger = $logger;
}
/**
* {@inheritdoc}
*/
public function send(RawMessage $message, Envelope $envelope = null): ?SentMessage
{
$string = Str::of($message->toString());
if ($string->contains('Content-Type: multipart/')) {
$boundary = $string
->after('boundary=')
->before("\r\n")
->prepend('--')
->append("\r\n");
$string = $string
->explode($boundary)
->map($this->decodeQuotedPrintableContent(...))
->implode($boundary);
} elseif ($string->contains('Content-Transfer-Encoding: quoted-printable')) {
$string = $this->decodeQuotedPrintableContent($string);
}
$this->logger->debug((string) $string);
return new SentMessage($message, $envelope ?? Envelope::create($message));
}
/**
* Decode the given quoted printable content.
*
* @param string $part
* @return string
*/
protected function decodeQuotedPrintableContent(string $part)
{
if (! str_contains($part, 'Content-Transfer-Encoding: quoted-printable')) {
return $part;
}
[$headers, $content] = explode("\r\n\r\n", $part, 2);
return implode("\r\n\r\n", [
$headers,
quoted_printable_decode($content),
]);
}
/**
* Get the logger for the LogTransport instance.
*
* @return \Psr\Log\LoggerInterface
*/
public function logger()
{
return $this->logger;
}
/**
* Get the string representation of the transport.
*
* @return string
*/
public function __toString(): string
{
return 'log';
}
}
framework/src/Illuminate/Mail/Transport/SesV2Transport.php 0000644 00000007112 15242753746 0017721 0 ustar 00 ses = $ses;
$this->options = $options;
parent::__construct();
}
/**
* {@inheritDoc}
*/
protected function doSend(SentMessage $message): void
{
$options = $this->options;
if ($message->getOriginalMessage() instanceof Message) {
foreach ($message->getOriginalMessage()->getHeaders()->all() as $header) {
if ($header instanceof MetadataHeader) {
$options['EmailTags'][] = ['Name' => $header->getKey(), 'Value' => $header->getValue()];
}
}
}
try {
$result = $this->ses->sendEmail(
array_merge(
$options, [
'Source' => $message->getEnvelope()->getSender()->toString(),
'Destination' => [
'ToAddresses' => collect($message->getEnvelope()->getRecipients())
->map
->toString()
->values()
->all(),
],
'Content' => [
'Raw' => [
'Data' => $message->toString(),
],
],
]
)
);
} catch (AwsException $e) {
$reason = $e->getAwsErrorMessage() ?? $e->getMessage();
throw new TransportException(
sprintf('Request to AWS SES V2 API failed. Reason: %s.', $reason),
is_int($e->getCode()) ? $e->getCode() : 0,
$e
);
}
$messageId = $result->get('MessageId');
$message->getOriginalMessage()->getHeaders()->addHeader('X-Message-ID', $messageId);
$message->getOriginalMessage()->getHeaders()->addHeader('X-SES-Message-ID', $messageId);
}
/**
* Get the Amazon SES V2 client for the SesV2Transport instance.
*
* @return \Aws\SesV2\SesV2Client
*/
public function ses()
{
return $this->ses;
}
/**
* Get the transmission options being used by the transport.
*
* @return array
*/
public function getOptions()
{
return $this->options;
}
/**
* Set the transmission options being used by the transport.
*
* @param array $options
* @return array
*/
public function setOptions(array $options)
{
return $this->options = $options;
}
/**
* Get the string representation of the transport.
*
* @return string
*/
public function __toString(): string
{
return 'ses-v2';
}
}
framework/src/Illuminate/Mail/resources/views/html/themes/default.css 0000644 00000011013 15242753746 0022066 0 ustar 00 /* Base */
body,
body *:not(html):not(style):not(br):not(tr):not(code) {
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
position: relative;
}
body {
-webkit-text-size-adjust: none;
background-color: #ffffff;
color: #718096;
height: 100%;
line-height: 1.4;
margin: 0;
padding: 0;
width: 100% !important;
}
p,
ul,
ol,
blockquote {
line-height: 1.4;
text-align: left;
}
a {
color: #3869d4;
}
a img {
border: none;
}
/* Typography */
h1 {
color: #3d4852;
font-size: 18px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
h2 {
font-size: 16px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
h3 {
font-size: 14px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
p {
font-size: 16px;
line-height: 1.5em;
margin-top: 0;
text-align: left;
}
p.sub {
font-size: 12px;
}
img {
max-width: 100%;
}
/* Layout */
.wrapper {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
background-color: #edf2f7;
margin: 0;
padding: 0;
width: 100%;
}
.content {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
margin: 0;
padding: 0;
width: 100%;
}
/* Header */
.header {
padding: 25px 0;
text-align: center;
}
.header a {
color: #3d4852;
font-size: 19px;
font-weight: bold;
text-decoration: none;
}
/* Logo */
.logo {
height: 75px;
max-height: 75px;
width: 75px;
}
/* Body */
.body {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
background-color: #edf2f7;
border-bottom: 1px solid #edf2f7;
border-top: 1px solid #edf2f7;
margin: 0;
padding: 0;
width: 100%;
}
.inner-body {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 570px;
background-color: #ffffff;
border-color: #e8e5ef;
border-radius: 2px;
border-width: 1px;
box-shadow: 0 2px 0 rgba(0, 0, 150, 0.025), 2px 4px 0 rgba(0, 0, 150, 0.015);
margin: 0 auto;
padding: 0;
width: 570px;
}
/* Subcopy */
.subcopy {
border-top: 1px solid #e8e5ef;
margin-top: 25px;
padding-top: 25px;
}
.subcopy p {
font-size: 14px;
}
/* Footer */
.footer {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 570px;
margin: 0 auto;
padding: 0;
text-align: center;
width: 570px;
}
.footer p {
color: #b0adc5;
font-size: 12px;
text-align: center;
}
.footer a {
color: #b0adc5;
text-decoration: underline;
}
/* Tables */
.table table {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
margin: 30px auto;
width: 100%;
}
.table th {
border-bottom: 1px solid #edeff2;
margin: 0;
padding-bottom: 8px;
}
.table td {
color: #74787e;
font-size: 15px;
line-height: 18px;
margin: 0;
padding: 10px 0;
}
.content-cell {
max-width: 100vw;
padding: 32px;
}
/* Buttons */
.action {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
margin: 30px auto;
padding: 0;
text-align: center;
width: 100%;
}
.button {
-webkit-text-size-adjust: none;
border-radius: 4px;
color: #fff;
display: inline-block;
overflow: hidden;
text-decoration: none;
}
.button-blue,
.button-primary {
background-color: #2d3748;
border-bottom: 8px solid #2d3748;
border-left: 18px solid #2d3748;
border-right: 18px solid #2d3748;
border-top: 8px solid #2d3748;
}
.button-green,
.button-success {
background-color: #48bb78;
border-bottom: 8px solid #48bb78;
border-left: 18px solid #48bb78;
border-right: 18px solid #48bb78;
border-top: 8px solid #48bb78;
}
.button-red,
.button-error {
background-color: #e53e3e;
border-bottom: 8px solid #e53e3e;
border-left: 18px solid #e53e3e;
border-right: 18px solid #e53e3e;
border-top: 8px solid #e53e3e;
}
/* Panels */
.panel {
border-left: #2d3748 solid 4px;
margin: 21px 0;
}
.panel-content {
background-color: #edf2f7;
color: #718096;
padding: 16px;
}
.panel-content p {
color: #718096;
}
.panel-item {
padding: 0;
}
.panel-item p:last-of-type {
margin-bottom: 0;
padding-bottom: 0;
}
/* Utilities */
.break-all {
word-break: break-all;
}
framework/src/Illuminate/Mail/resources/views/html/header.blade.php 0000644 00000000402 15242753746 0021452 0 ustar 00 @props(['url'])