New contact form with friendlycaptcha #18
@@ -37,3 +37,7 @@ DB_PASSWORD=dbpass
|
||||
|
||||
CACHE_DRIVER=file
|
||||
QUEUE_DRIVER=sync
|
||||
|
||||
FRIENDLY_CAPTCHA_SITEKEY=
|
||||
FRIENDLY_CAPTCHA_SECRET=
|
||||
FRIENDLY_CAPTCHA_DEBUG=true
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM php:8.1-apache
|
||||
FROM php:8.2-apache
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get -y install \
|
||||
|
||||
74
app/Http/Controllers/ContactController.php
Normal file
74
app/Http/Controllers/ContactController.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Validation\Rule;
|
||||
use App\Mail\ContactFormSubmitted;
|
||||
|
||||
class ContactController extends Controller
|
||||
{
|
||||
public function show()
|
||||
{
|
||||
return view('contact');
|
||||
}
|
||||
|
||||
public function submit(Request $request)
|
||||
{
|
||||
$rules = [
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|email|max:255',
|
||||
'message' => 'required|string',
|
||||
'image' => 'nullable|image|max:10240',
|
||||
'video' => 'nullable|mimetypes:video/avi,video/mpeg,video/quicktime,video/mp4|max:512000',
|
||||
];
|
||||
|
||||
$debugCaptcha = env('FRIENDLY_CAPTCHA_DEBUG', false)
|
||||
|| (app()->environment('local')
|
||||
&& (!env('FRIENDLY_CAPTCHA_SITEKEY') || !env('FRIENDLY_CAPTCHA_SECRET')));
|
||||
|
||||
if ($debugCaptcha) {
|
||||
$rules['frc-captcha-solution'] = 'nullable';
|
||||
} else {
|
||||
$rules['frc-captcha-solution'] = ['required', Rule::friendlycaptcha()];
|
||||
}
|
||||
|
||||
$request->validate($rules);
|
||||
|
||||
$data = $request->only(['name', 'email', 'message']);
|
||||
$attachments = [];
|
||||
|
||||
// Handle Image
|
||||
if ($request->hasFile('image')) {
|
||||
// Store temporarily to attach
|
||||
$imagePath = $request->file('image')->store('contact_images');
|
||||
$attachments['image'] = storage_path('app/' . $imagePath);
|
||||
}
|
||||
|
||||
// Handle Video
|
||||
if ($request->hasFile('video')) {
|
||||
$videoPath = $request->file('video')->store('contact_videos', 'local');
|
||||
$data['video_link'] = route('contact.video', ['filename' => basename($videoPath)]);
|
||||
}
|
||||
|
||||
// Send Email
|
||||
Mail::to('info@nhgooi.nl')->send(new ContactFormSubmitted($data, $attachments));
|
||||
|
||||
return redirect()->route('contact')->with('success', 'Bedankt voor uw bericht. We nemen zo snel mogelijk contact met u op.');
|
||||
}
|
||||
|
||||
public function video(string $filename)
|
||||
{
|
||||
$path = 'contact_videos/' . basename($filename);
|
||||
|
||||
if (!Storage::disk('local')->exists($path)) {
|
||||
abort(404);
|
||||
}
|
||||
|
||||
$fullPath = Storage::disk('local')->path($path);
|
||||
|
||||
return response()->file($fullPath);
|
||||
}
|
||||
}
|
||||
45
app/Mail/ContactFormSubmitted.php
Normal file
45
app/Mail/ContactFormSubmitted.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
||||
class ContactFormSubmitted extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public $data;
|
||||
public $attachments_files;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($data, $attachments_files = [])
|
||||
{
|
||||
$this->data = $data;
|
||||
$this->attachments_files = $attachments_files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
$email = $this->view('emails.contact_submitted')
|
||||
->subject('Nieuw contactformulier bericht van NH Gooi')
|
||||
->replyTo($this->data['email'], $this->data['name']);
|
||||
|
||||
if (isset($this->attachments_files['image'])) {
|
||||
$email->attach($this->attachments_files['image']);
|
||||
}
|
||||
|
||||
return $email;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Ossycodes\FriendlyCaptcha\Rules\FriendlyCaptcha as FriendlyCaptchaRule;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
@@ -14,6 +16,10 @@ class AppServiceProvider extends ServiceProvider
|
||||
public function boot()
|
||||
{
|
||||
\Illuminate\Support\Facades\URL::forceScheme('https');
|
||||
|
||||
Rule::macro('friendlycaptcha', function () {
|
||||
return app(FriendlyCaptchaRule::class);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
11
bootstrap/cache/packages.php
vendored
11
bootstrap/cache/packages.php
vendored
@@ -53,6 +53,17 @@
|
||||
0 => 'Termwind\\Laravel\\TermwindServiceProvider',
|
||||
),
|
||||
),
|
||||
'ossycodes/friendlycaptcha' =>
|
||||
array (
|
||||
'aliases' =>
|
||||
array (
|
||||
'FriendlyCaptcha' => 'Ossycodes\\FriendlyCaptcha\\Facades\\FriendlyCaptcha',
|
||||
),
|
||||
'providers' =>
|
||||
array (
|
||||
0 => 'Ossycodes\\FriendlyCaptcha\\FriendlyCaptchaServiceProvider',
|
||||
),
|
||||
),
|
||||
'spatie/laravel-ignition' =>
|
||||
array (
|
||||
'providers' =>
|
||||
|
||||
26
bootstrap/cache/services.php
vendored
26
bootstrap/cache/services.php
vendored
@@ -30,13 +30,14 @@
|
||||
26 => 'Carbon\\Laravel\\ServiceProvider',
|
||||
27 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
|
||||
28 => 'Termwind\\Laravel\\TermwindServiceProvider',
|
||||
29 => 'Spatie\\LaravelIgnition\\IgnitionServiceProvider',
|
||||
30 => 'Collective\\Html\\HtmlServiceProvider',
|
||||
31 => 'Laravel\\Tinker\\TinkerServiceProvider',
|
||||
32 => 'App\\Providers\\AppServiceProvider',
|
||||
33 => 'App\\Providers\\AuthServiceProvider',
|
||||
34 => 'App\\Providers\\EventServiceProvider',
|
||||
35 => 'App\\Providers\\RouteServiceProvider',
|
||||
29 => 'Ossycodes\\FriendlyCaptcha\\FriendlyCaptchaServiceProvider',
|
||||
30 => 'Spatie\\LaravelIgnition\\IgnitionServiceProvider',
|
||||
31 => 'Collective\\Html\\HtmlServiceProvider',
|
||||
32 => 'Laravel\\Tinker\\TinkerServiceProvider',
|
||||
33 => 'App\\Providers\\AppServiceProvider',
|
||||
34 => 'App\\Providers\\AuthServiceProvider',
|
||||
35 => 'App\\Providers\\EventServiceProvider',
|
||||
36 => 'App\\Providers\\RouteServiceProvider',
|
||||
),
|
||||
'eager' =>
|
||||
array (
|
||||
@@ -54,11 +55,12 @@
|
||||
11 => 'Carbon\\Laravel\\ServiceProvider',
|
||||
12 => 'NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider',
|
||||
13 => 'Termwind\\Laravel\\TermwindServiceProvider',
|
||||
14 => 'Spatie\\LaravelIgnition\\IgnitionServiceProvider',
|
||||
15 => 'App\\Providers\\AppServiceProvider',
|
||||
16 => 'App\\Providers\\AuthServiceProvider',
|
||||
17 => 'App\\Providers\\EventServiceProvider',
|
||||
18 => 'App\\Providers\\RouteServiceProvider',
|
||||
14 => 'Ossycodes\\FriendlyCaptcha\\FriendlyCaptchaServiceProvider',
|
||||
15 => 'Spatie\\LaravelIgnition\\IgnitionServiceProvider',
|
||||
16 => 'App\\Providers\\AppServiceProvider',
|
||||
17 => 'App\\Providers\\AuthServiceProvider',
|
||||
18 => 'App\\Providers\\EventServiceProvider',
|
||||
19 => 'App\\Providers\\RouteServiceProvider',
|
||||
),
|
||||
'deferred' =>
|
||||
array (
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
"laravel/framework": "^9.19",
|
||||
"laravel/sanctum": "^3.0",
|
||||
"laravel/tinker": "^2.7",
|
||||
"laravelcollective/html": "^6.3"
|
||||
"laravelcollective/html": "^6.3",
|
||||
"ossycodes/friendlycaptcha": "^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
|
||||
132
composer.lock
generated
132
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "f70c81adf2990f185a8632535bd08631",
|
||||
"content-hash": "f9a3eebc09010b2b187541a3e59a0eff",
|
||||
"packages": [
|
||||
{
|
||||
"name": "brick/math",
|
||||
@@ -1934,34 +1934,37 @@
|
||||
},
|
||||
{
|
||||
"name": "nette/schema",
|
||||
"version": "v1.2.3",
|
||||
"version": "v1.3.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nette/schema.git",
|
||||
"reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f"
|
||||
"reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nette/schema/zipball/abbdbb70e0245d5f3bf77874cea1dfb0c930d06f",
|
||||
"reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f",
|
||||
"url": "https://api.github.com/repos/nette/schema/zipball/2befc2f42d7c715fd9d95efc31b1081e5d765004",
|
||||
"reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"nette/utils": "^2.5.7 || ^3.1.5 || ^4.0",
|
||||
"php": ">=7.1 <8.3"
|
||||
"nette/utils": "^4.0",
|
||||
"php": "8.1 - 8.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "^2.3 || ^2.4",
|
||||
"phpstan/phpstan-nette": "^1.0",
|
||||
"tracy/tracy": "^2.7"
|
||||
"nette/tester": "^2.5.2",
|
||||
"phpstan/phpstan-nette": "^2.0@stable",
|
||||
"tracy/tracy": "^2.8"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.2-dev"
|
||||
"dev-master": "1.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Nette\\": "src"
|
||||
},
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
@@ -1990,34 +1993,36 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nette/schema/issues",
|
||||
"source": "https://github.com/nette/schema/tree/v1.2.3"
|
||||
"source": "https://github.com/nette/schema/tree/v1.3.3"
|
||||
},
|
||||
"time": "2022-10-13T01:24:26+00:00"
|
||||
"time": "2025-10-30T22:57:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nette/utils",
|
||||
"version": "v3.2.8",
|
||||
"version": "v4.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nette/utils.git",
|
||||
"reference": "02a54c4c872b99e4ec05c4aec54b5a06eb0f6368"
|
||||
"reference": "c99059c0315591f1a0db7ad6002000288ab8dc72"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/02a54c4c872b99e4ec05c4aec54b5a06eb0f6368",
|
||||
"reference": "02a54c4c872b99e4ec05c4aec54b5a06eb0f6368",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/c99059c0315591f1a0db7ad6002000288ab8dc72",
|
||||
"reference": "c99059c0315591f1a0db7ad6002000288ab8dc72",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2 <8.3"
|
||||
"php": "8.2 - 8.5"
|
||||
},
|
||||
"conflict": {
|
||||
"nette/di": "<3.0.6"
|
||||
"nette/finder": "<3",
|
||||
"nette/schema": "<1.2.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "~2.0",
|
||||
"phpstan/phpstan": "^1.0",
|
||||
"tracy/tracy": "^2.3"
|
||||
"jetbrains/phpstorm-attributes": "^1.2",
|
||||
"nette/tester": "^2.5",
|
||||
"phpstan/phpstan-nette": "^2.0@stable",
|
||||
"tracy/tracy": "^2.9"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-gd": "to use Image",
|
||||
@@ -2025,16 +2030,18 @@
|
||||
"ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
|
||||
"ext-json": "to use Nette\\Utils\\Json",
|
||||
"ext-mbstring": "to use Strings::lower() etc...",
|
||||
"ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()",
|
||||
"ext-xml": "to use Strings::length() etc. when mbstring is not available"
|
||||
"ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
"dev-master": "4.1-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Nette\\": "src"
|
||||
},
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
@@ -2075,9 +2082,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nette/utils/issues",
|
||||
"source": "https://github.com/nette/utils/tree/v3.2.8"
|
||||
"source": "https://github.com/nette/utils/tree/v4.1.1"
|
||||
},
|
||||
"time": "2022-09-12T23:36:20+00:00"
|
||||
"time": "2025-12-22T12:14:32+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
@@ -2221,6 +2228,69 @@
|
||||
],
|
||||
"time": "2022-12-20T19:00:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ossycodes/friendlycaptcha",
|
||||
"version": "v3.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ossycodes/friendlycaptcha.git",
|
||||
"reference": "b18dfab44ee5fff7d75412232eb6f834864efde6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ossycodes/friendlycaptcha/zipball/b18dfab44ee5fff7d75412232eb6f834864efde6",
|
||||
"reference": "b18dfab44ee5fff7d75412232eb6f834864efde6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"guzzlehttp/guzzle": "^7.0",
|
||||
"illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0",
|
||||
"php": "^7.4|^8.0|^8.1|^8.2|^8.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"orchestra/testbench": "^6.0|^7.0|^8.0|^9.0",
|
||||
"phpunit/phpunit": "^8.0 || ^9.5 || ^10.5 || ^11.0 || ^12.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"aliases": {
|
||||
"FriendlyCaptcha": "Ossycodes\\FriendlyCaptcha\\Facades\\FriendlyCaptcha"
|
||||
},
|
||||
"providers": [
|
||||
"Ossycodes\\FriendlyCaptcha\\FriendlyCaptchaServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Ossycodes\\FriendlyCaptcha\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "ossycodes",
|
||||
"email": "osaigbovoemmanuel1@gmail.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "A simple package to help integrate FriendlyCaptcha in your Laravel applications.",
|
||||
"homepage": "https://github.com/ossycodes/friendlycaptcha",
|
||||
"keywords": [
|
||||
"captcha",
|
||||
"friendlycaptcha",
|
||||
"laravel"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/ossycodes/friendlycaptcha/issues",
|
||||
"source": "https://github.com/ossycodes/friendlycaptcha/tree/v3.0.0"
|
||||
},
|
||||
"time": "2025-05-08T13:30:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoption/phpoption",
|
||||
"version": "1.9.0",
|
||||
@@ -7931,12 +8001,12 @@
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "dev",
|
||||
"stability-flags": [],
|
||||
"stability-flags": {},
|
||||
"prefer-stable": true,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": "^8.0.2"
|
||||
"php": "^8.0"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.3.0"
|
||||
"platform-dev": {},
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
||||
@@ -22,3 +22,8 @@ services:
|
||||
MYSQL_DATABASE: forge
|
||||
MYSQL_USER: forge
|
||||
MYSQL_PASSWORD: secret
|
||||
mailpit:
|
||||
image: axllent/mailpit:latest
|
||||
ports:
|
||||
- "8025:8025"
|
||||
- "1025:1025"
|
||||
|
||||
@@ -23,7 +23,7 @@ ServerTokens Prod
|
||||
<VirtualHost *:443>
|
||||
ServerName localhost
|
||||
ServerAdmin support@websight.nl
|
||||
DocumentRoot /var/www/html
|
||||
DocumentRoot /var/www/html/public
|
||||
|
||||
SSLEngine on
|
||||
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||
@@ -32,15 +32,15 @@ ServerTokens Prod
|
||||
SSLProtocol All -SSLv2 -SSLv3
|
||||
SSLCipherSuite EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH
|
||||
|
||||
<Directory /var/www/html/>
|
||||
<Directory /var/www/html/public>
|
||||
Options -Indexes +FollowSymLinks +MultiViews
|
||||
AllowOverride All
|
||||
Order deny,allow
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
ErrorLog /var/log/apache2/ssl-vhost-error.log
|
||||
CustomLog /var/log/apache2/ssl-vhost-access.log combined
|
||||
ErrorLog /var/log/apache2/error.log
|
||||
CustomLog /var/log/apache2/access.log combined
|
||||
|
||||
</VirtualHost>
|
||||
</IfModule>
|
||||
|
||||
@@ -37,3 +37,7 @@ DB_PASSWORD=dbpass
|
||||
|
||||
CACHE_DRIVER=file
|
||||
QUEUE_DRIVER=sync
|
||||
|
||||
FRIENDLY_CAPTCHA_SITEKEY=
|
||||
FRIENDLY_CAPTCHA_SECRET=
|
||||
FRIENDLY_CAPTCHA_DEBUG=true
|
||||
|
||||
72
public/css/style.css
vendored
72
public/css/style.css
vendored
@@ -58,7 +58,7 @@ body {
|
||||
.btn {
|
||||
display: block;
|
||||
width: calc(100% - 78px);
|
||||
padding: 10px 39px 10px 39px;
|
||||
padding: 10px 39px;
|
||||
border-radius: 3px;
|
||||
background-image: linear-gradient(to right, #0f259d, #5ba8f4);
|
||||
font-family: Nunito, serif;
|
||||
@@ -74,21 +74,6 @@ body {
|
||||
.btn:hover, .btn:active {
|
||||
color: #fff;
|
||||
}
|
||||
.btn.auto_width {
|
||||
width: fit-content;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.btn.fit_content {
|
||||
width: fit-content;
|
||||
}
|
||||
.btn.btn_facebook_share {
|
||||
background-image: none;
|
||||
background-color: #0f259d;
|
||||
}
|
||||
.btn.btn_twitter_x_share {
|
||||
background-image: none;
|
||||
background-color: #5ba8f4;
|
||||
}
|
||||
|
||||
div.pp_default .pp_content_container .pp_left,
|
||||
div.pp_default .pp_content_container .pp_right,
|
||||
@@ -172,6 +157,43 @@ div.pp_default .pp_close:hover {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.contant-form .form-group {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.contant-form .form-group label {
|
||||
width: 160px;
|
||||
}
|
||||
.contant-form .form-group input, .contant-form .form-group textarea {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.contant-form .action_button {
|
||||
margin-top: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.contant-form .form-control-file::file-selector-button {
|
||||
display: block;
|
||||
width: calc(100% - 78px);
|
||||
padding: 10px 39px;
|
||||
border-radius: 3px;
|
||||
background-image: linear-gradient(to right, #0f259d, #5ba8f4);
|
||||
font-family: Nunito, serif;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
line-height: 0.93;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
margin-bottom: 10px;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
width: fit-content;
|
||||
margin-bottom: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.contant-form .form-control-file::file-selector-button:hover, .contant-form .form-control-file::file-selector-button:active {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.header {
|
||||
height: 111px;
|
||||
}
|
||||
@@ -303,18 +325,6 @@ div.pp_default .pp_close:hover {
|
||||
margin-left: -10px;
|
||||
}
|
||||
|
||||
.submenu li.selected a {
|
||||
color: #0f259d !important;
|
||||
}
|
||||
|
||||
.mobile-menu .submenu li.selected a,
|
||||
.mobile-menu .submenu li:hover a,
|
||||
.mobile-menu .submenu li.hover a {
|
||||
color: white !important;
|
||||
background: linear-gradient(to right, #0f259d, #5ba8f4);
|
||||
}
|
||||
|
||||
|
||||
.top_menu_container, .menu_mobile_container {
|
||||
height: 50px;
|
||||
max-width: 1170px;
|
||||
@@ -1282,6 +1292,9 @@ div.pp_default .pp_close:hover {
|
||||
.post_container .post_body h3 {
|
||||
font-size: 15px;
|
||||
}
|
||||
.post_container .post_body div.text {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.post_container .post_body blockquote {
|
||||
border-left: 3px solid #5ba8f4;
|
||||
margin-left: 0;
|
||||
@@ -1299,9 +1312,6 @@ div.pp_default .pp_close:hover {
|
||||
line-height: 3.17;
|
||||
color: #585858;
|
||||
}
|
||||
.post_container .post_body div.text {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.post_container .post_body .post_details {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
File diff suppressed because one or more lines are too long
2
public/css/style.min.css
vendored
2
public/css/style.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
22
resources/assets/sass/abstracts/_mixin.scss
vendored
22
resources/assets/sass/abstracts/_mixin.scss
vendored
@@ -63,3 +63,25 @@
|
||||
@include post_date;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin btn-base {
|
||||
display: block;
|
||||
width: calc(100% - 78px);
|
||||
padding: 10px 39px;
|
||||
border-radius: 3px;
|
||||
background-image: linear-gradient(to right, #0f259d, #5ba8f4);
|
||||
font-family: Nunito, serif;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
line-height: 0.93;
|
||||
text-align: center;
|
||||
color: $text-inverted-color;
|
||||
margin-bottom: 10px;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
color: $text-inverted-color;
|
||||
}
|
||||
}
|
||||
|
||||
1
resources/assets/sass/base/_base.scss
vendored
1
resources/assets/sass/base/_base.scss
vendored
@@ -6,6 +6,7 @@
|
||||
@use "../components/cookie";
|
||||
@use "../components/list";
|
||||
@use "../components/banners";
|
||||
@use "../components/contact_form";
|
||||
|
||||
@use "../layout";
|
||||
|
||||
|
||||
39
resources/assets/sass/components/_button.scss
vendored
39
resources/assets/sass/components/_button.scss
vendored
@@ -1,40 +1,5 @@
|
||||
@use "../abstracts/variables" as *;
|
||||
@use "../abstracts/mixin" as *;
|
||||
|
||||
.btn {
|
||||
display: block;
|
||||
width: CALC(100% - 78px);
|
||||
padding: 10px 39px 10px 39px;
|
||||
border-radius: 3px;
|
||||
background-image: linear-gradient(to right, #0f259d, #5ba8f4);
|
||||
font-family: Nunito, serif;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
line-height: 0.93;
|
||||
text-align: center;
|
||||
color: $text-inverted-color;
|
||||
margin-bottom: 10px;
|
||||
text-decoration: none;
|
||||
text-transform: uppercase;
|
||||
|
||||
&:hover, &:active {
|
||||
color: $text-inverted-color;
|
||||
}
|
||||
|
||||
&.auto_width {
|
||||
width: fit-content;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
&.fit_content {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
&.btn_facebook_share {
|
||||
background-image: none;
|
||||
background-color: #0f259d;
|
||||
}
|
||||
&.btn_twitter_x_share {
|
||||
background-image: none;
|
||||
background-color: #5ba8f4;
|
||||
}
|
||||
@include btn-base;
|
||||
}
|
||||
|
||||
24
resources/assets/sass/components/_contact_form.scss
vendored
Normal file
24
resources/assets/sass/components/_contact_form.scss
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
@use "../abstracts/mixin" as *;
|
||||
|
||||
.contant-form {
|
||||
.form-group {
|
||||
display: flex;
|
||||
margin-bottom: 10px;
|
||||
label {
|
||||
width: 160px;
|
||||
}
|
||||
input, textarea {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
.action_button {
|
||||
margin-top: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.form-control-file::file-selector-button {
|
||||
@include btn-base;
|
||||
width: fit-content;
|
||||
margin-bottom: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
116
resources/views/contact.blade.php
Normal file
116
resources/views/contact.blade.php
Normal file
@@ -0,0 +1,116 @@
|
||||
@extends('layouts/full')
|
||||
|
||||
@section('title')
|
||||
Neem contact op
|
||||
@endsection
|
||||
|
||||
@section('breadcrumb')
|
||||
<ul class="bread_crumb">
|
||||
<li><a title="Home" href="/">Home</a></li>
|
||||
<li class="separator"><i class="fa-solid fa-chevron-right"></i></li>
|
||||
<li><a title="NH Gooi" href="{{route('contact')}}">NH Gooi</a></li>
|
||||
<li class="separator"><i class="fa-solid fa-chevron-right"></i></li>
|
||||
<li>Neem contact op</li>
|
||||
</ul>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="page_body">
|
||||
<div class="row ">
|
||||
<div class="col-12 col-md-6">
|
||||
<p>NH Gooi is de publieke streekomroep van Gooi en Vechtstreek. We houden je 24 uur per dag
|
||||
op de hoogte van al het nieuws, betrouwbaar en snel. Dat doen we via een eigen nieuws-
|
||||
app, onze website en social media, maar ook op radio en televisie. Daarnaast brengen we
|
||||
een gevarieerd aanbod van podcasts, radio- en televisieprogramma's.</p>
|
||||
|
||||
<p>Ons team van journalisten en programmamakers bestaat uit betaalde krachten, vrijwilligers
|
||||
en stagiaires. We vinden het belangrijk mensen op te leiden en een goede plek te bieden
|
||||
voor talent.</p>
|
||||
|
||||
<p>De redactie van NH Gooi is journalistiek onafhankelijk en wordt geleid door de chef redactie.</p>
|
||||
|
||||
<h3>Contactinformatie</h3>
|
||||
|
||||
<p>Neem contact op met NH Gooi, de streekomroep voor Gooi & Vechtstreek.</p>
|
||||
|
||||
<p>Wij zijn te ontvangen in heel Gooi en Eemland. <br>
|
||||
<a href="{{url('frequenties')}}" class="action_button">
|
||||
<span class="fa fa-list"></span>
|
||||
<span>Frequenties</span>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p class="page_margin_top">
|
||||
<b>NHGooi</b><br>
|
||||
Postbus 83<br>
|
||||
1270 AB Huizen<br>
|
||||
Tel: <a href="tel:+31356424776">035-6424774</a><br>
|
||||
KvK: 41194132<br>
|
||||
<br>
|
||||
|
||||
<b>Bezoekadres / Studio:</b><br>
|
||||
IJsselmeerstraat 3B<br>
|
||||
1271 AA Huizen<br><br>
|
||||
|
||||
<p>
|
||||
<b>NHGooi Radio</b><br>
|
||||
IJsselmeerstraat 3B<br/>
|
||||
1271 AA, Huizen<br/>
|
||||
studio: <a href="tel:+31356424776">035-6424776</a><br/>
|
||||
KvK: 41194132<br>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<h3>Contactformulier</h3>
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-danger">
|
||||
<ul>
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form class="contant-form" action="{{ route('contact.submit') }}" method="POST" enctype="multipart/form-data">
|
||||
{{ csrf_field() }}
|
||||
<div class="form-group">
|
||||
<label for="name">Naam</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="{{ old('name') }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="email">Email</label>
|
||||
<input type="email" class="form-control" id="email" name="email" value="{{ old('email') }}" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="message">Bericht</label>
|
||||
<textarea class="form-control" id="message" name="message" rows="5" required>{{ old('message') }}</textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="image">Afbeelding (optioneel)</label>
|
||||
<input type="file" class="form-control-file" id="image" name="image" accept="image/*">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="video">Video (optioneel)</label>
|
||||
<input type="file" class="form-control-file" id="video" name="video" accept="video/*">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
{!! FriendlyCaptcha::renderWidget() !!}
|
||||
</div>
|
||||
<button type="submit" class="btn action_button">Verstuur</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
{!! FriendlyCaptcha::renderWidgetScripts() !!}
|
||||
@endpush
|
||||
16
resources/views/emails/contact_submitted.blade.php
Normal file
16
resources/views/emails/contact_submitted.blade.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Contactformulier Bericht</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Nieuw bericht van {{ $data['name'] }}</h1>
|
||||
<p><strong>Email:</strong> {{ $data['email'] }}</p>
|
||||
<p><strong>Bericht:</strong></p>
|
||||
<p>{!! nl2br(e($data['message'])) !!}</p>
|
||||
|
||||
@if(isset($data['video_link']))
|
||||
<p><strong>Video Link:</strong> <a href="{{ $data['video_link'] }}">{{ $data['video_link'] }}</a></p>
|
||||
@endif
|
||||
</body>
|
||||
</html>
|
||||
@@ -112,5 +112,12 @@ Route::get('/cookie-statement', 'Controller@view_cookie_statement')->name('cooki
|
||||
Route::get('/special/stmaarten', function() { return file_get_contents('http://api-dev.6fm.nl/special/stmaarten'); });
|
||||
Route::get('/kabelkrant', function() { return view('kabelkrant'); });
|
||||
|
||||
// New contact form
|
||||
Route::get('/contact', 'ContactController@show')->name('contact');
|
||||
Route::post('/contact', 'ContactController@submit')->name('contact.submit');
|
||||
Route::get('/contact/video/{filename}', 'ContactController@video')
|
||||
->where(['filename' => '[A-Za-z0-9_\-\.]+'])
|
||||
->name('contact.video');
|
||||
|
||||
// Catch all route for API-based static routes
|
||||
Route::get('{slug}', 'Controller@static_page')->where('slug', '^.*')->name('static_page');
|
||||
|
||||
61
tests/Feature/ContactPageTest.php
Normal file
61
tests/Feature/ContactPageTest.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use Tests\TestCase;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Validation\Rule as ValidationRule;
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
use App\Mail\ContactFormSubmitted;
|
||||
|
||||
class ContactPageTest extends TestCase
|
||||
{
|
||||
public function test_contact_page_is_accessible()
|
||||
{
|
||||
$response = $this->get(route('contact'));
|
||||
|
||||
$response->assertStatus(200);
|
||||
$response->assertSee('Contactformulier');
|
||||
}
|
||||
|
||||
public function test_contact_form_can_be_submitted()
|
||||
{
|
||||
ValidationRule::macro('friendlycaptcha', function () {
|
||||
return new class implements Rule {
|
||||
public function passes($attribute, $value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function message()
|
||||
{
|
||||
return 'The :attribute is invalid.';
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
Mail::fake();
|
||||
Storage::fake('public');
|
||||
|
||||
$image = UploadedFile::fake()->image('example.jpg');
|
||||
$video = UploadedFile::fake()->create('example.mp4', 1000, 'video/mp4');
|
||||
|
||||
$response = $this->post(route('contact.submit'), [
|
||||
'name' => 'Test User',
|
||||
'email' => 'test@example.com',
|
||||
'message' => 'Dit is een testbericht.',
|
||||
'image' => $image,
|
||||
'video' => $video,
|
||||
'frc-captcha-solution' => 'dummy-solution',
|
||||
]);
|
||||
|
||||
$response->assertRedirect(route('contact'));
|
||||
$response->assertSessionHas('success');
|
||||
|
||||
Mail::assertSent(function (ContactFormSubmitted $mail) {
|
||||
return $mail->hasTo('info@nhgooi.nl');
|
||||
});
|
||||
}
|
||||
}
|
||||
5
vendor/autoload.php
vendored
5
vendor/autoload.php
vendored
@@ -14,10 +14,7 @@ if (PHP_VERSION_ID < 50600) {
|
||||
echo $err;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
$err,
|
||||
E_USER_ERROR
|
||||
);
|
||||
throw new RuntimeException($err);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
58
vendor/composer/InstalledVersions.php
vendored
58
vendor/composer/InstalledVersions.php
vendored
@@ -26,12 +26,23 @@ use Composer\Semver\VersionParser;
|
||||
*/
|
||||
class InstalledVersions
|
||||
{
|
||||
/**
|
||||
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
|
||||
* @internal
|
||||
*/
|
||||
private static $selfDir = null;
|
||||
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||
*/
|
||||
private static $installed;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private static $installedIsLocalDir;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
@@ -98,7 +109,7 @@ class InstalledVersions
|
||||
{
|
||||
foreach (self::getInstalled() as $installed) {
|
||||
if (isset($installed['versions'][$packageName])) {
|
||||
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
|
||||
return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +130,7 @@ class InstalledVersions
|
||||
*/
|
||||
public static function satisfies(VersionParser $parser, $packageName, $constraint)
|
||||
{
|
||||
$constraint = $parser->parseConstraints($constraint);
|
||||
$constraint = $parser->parseConstraints((string) $constraint);
|
||||
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
|
||||
|
||||
return $provided->matches($constraint);
|
||||
@@ -309,6 +320,24 @@ class InstalledVersions
|
||||
{
|
||||
self::$installed = $data;
|
||||
self::$installedByVendor = array();
|
||||
|
||||
// when using reload, we disable the duplicate protection to ensure that self::$installed data is
|
||||
// always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
|
||||
// so we have to assume it does not, and that may result in duplicate data being returned when listing
|
||||
// all installed packages for example
|
||||
self::$installedIsLocalDir = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private static function getSelfDir()
|
||||
{
|
||||
if (self::$selfDir === null) {
|
||||
self::$selfDir = strtr(__DIR__, '\\', '/');
|
||||
}
|
||||
|
||||
return self::$selfDir;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -322,17 +351,27 @@ class InstalledVersions
|
||||
}
|
||||
|
||||
$installed = array();
|
||||
$copiedLocalDir = false;
|
||||
|
||||
if (self::$canGetVendors) {
|
||||
$selfDir = self::getSelfDir();
|
||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||
$vendorDir = strtr($vendorDir, '\\', '/');
|
||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir];
|
||||
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
|
||||
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
||||
self::$installed = $installed[count($installed) - 1];
|
||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||
$required = require $vendorDir.'/composer/installed.php';
|
||||
self::$installedByVendor[$vendorDir] = $required;
|
||||
$installed[] = $required;
|
||||
if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
|
||||
self::$installed = $required;
|
||||
self::$installedIsLocalDir = true;
|
||||
}
|
||||
}
|
||||
if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
|
||||
$copiedLocalDir = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,12 +379,17 @@ class InstalledVersions
|
||||
// only require the installed.php file if this file is loaded from its dumped location,
|
||||
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
|
||||
if (substr(__DIR__, -8, 1) !== 'C') {
|
||||
self::$installed = require __DIR__ . '/installed.php';
|
||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||
$required = require __DIR__ . '/installed.php';
|
||||
self::$installed = $required;
|
||||
} else {
|
||||
self::$installed = array();
|
||||
}
|
||||
}
|
||||
$installed[] = self::$installed;
|
||||
|
||||
if (self::$installed !== array() && !$copiedLocalDir) {
|
||||
$installed[] = self::$installed;
|
||||
}
|
||||
|
||||
return $installed;
|
||||
}
|
||||
|
||||
15
vendor/composer/autoload_classmap.php
vendored
15
vendor/composer/autoload_classmap.php
vendored
@@ -14,6 +14,7 @@ return array(
|
||||
'App\\Http\\Controllers\\Auth\\RegisterController' => $baseDir . '/app/Http/Controllers/Auth/RegisterController.php',
|
||||
'App\\Http\\Controllers\\Auth\\ResetPasswordController' => $baseDir . '/app/Http/Controllers/Auth/ResetPasswordController.php',
|
||||
'App\\Http\\Controllers\\CalendarController' => $baseDir . '/app/Http/Controllers/CalendarController.php',
|
||||
'App\\Http\\Controllers\\ContactController' => $baseDir . '/app/Http/Controllers/ContactController.php',
|
||||
'App\\Http\\Controllers\\Controller' => $baseDir . '/app/Http/Controllers/Controller.php',
|
||||
'App\\Http\\Controllers\\HomeController' => $baseDir . '/app/Http/Controllers/HomeController.php',
|
||||
'App\\Http\\Controllers\\ImagesController' => $baseDir . '/app/Http/Controllers/ImagesController.php',
|
||||
@@ -29,6 +30,7 @@ return array(
|
||||
'App\\Http\\Middleware\\RedirectIfAuthenticated' => $baseDir . '/app/Http/Middleware/RedirectIfAuthenticated.php',
|
||||
'App\\Http\\Middleware\\TrimStrings' => $baseDir . '/app/Http/Middleware/TrimStrings.php',
|
||||
'App\\Http\\Middleware\\VerifyCsrfToken' => $baseDir . '/app/Http/Middleware/VerifyCsrfToken.php',
|
||||
'App\\Mail\\ContactFormSubmitted' => $baseDir . '/app/Mail/ContactFormSubmitted.php',
|
||||
'App\\Providers\\AppServiceProvider' => $baseDir . '/app/Providers/AppServiceProvider.php',
|
||||
'App\\Providers\\AuthServiceProvider' => $baseDir . '/app/Providers/AuthServiceProvider.php',
|
||||
'App\\Providers\\BroadcastServiceProvider' => $baseDir . '/app/Providers/BroadcastServiceProvider.php',
|
||||
@@ -2802,6 +2804,7 @@ return array(
|
||||
'Nette\\Schema\\Processor' => $vendorDir . '/nette/schema/src/Schema/Processor.php',
|
||||
'Nette\\Schema\\Schema' => $vendorDir . '/nette/schema/src/Schema/Schema.php',
|
||||
'Nette\\Schema\\ValidationException' => $vendorDir . '/nette/schema/src/Schema/ValidationException.php',
|
||||
'Nette\\ShouldNotHappenException' => $vendorDir . '/nette/utils/src/exceptions.php',
|
||||
'Nette\\SmartObject' => $vendorDir . '/nette/utils/src/SmartObject.php',
|
||||
'Nette\\StaticClass' => $vendorDir . '/nette/utils/src/StaticClass.php',
|
||||
'Nette\\UnexpectedValueException' => $vendorDir . '/nette/utils/src/exceptions.php',
|
||||
@@ -2811,20 +2814,25 @@ return array(
|
||||
'Nette\\Utils\\AssertionException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\Callback' => $vendorDir . '/nette/utils/src/Utils/Callback.php',
|
||||
'Nette\\Utils\\DateTime' => $vendorDir . '/nette/utils/src/Utils/DateTime.php',
|
||||
'Nette\\Utils\\FileInfo' => $vendorDir . '/nette/utils/src/Utils/FileInfo.php',
|
||||
'Nette\\Utils\\FileSystem' => $vendorDir . '/nette/utils/src/Utils/FileSystem.php',
|
||||
'Nette\\Utils\\Finder' => $vendorDir . '/nette/utils/src/Utils/Finder.php',
|
||||
'Nette\\Utils\\Floats' => $vendorDir . '/nette/utils/src/Utils/Floats.php',
|
||||
'Nette\\Utils\\Helpers' => $vendorDir . '/nette/utils/src/Utils/Helpers.php',
|
||||
'Nette\\Utils\\Html' => $vendorDir . '/nette/utils/src/Utils/Html.php',
|
||||
'Nette\\Utils\\IHtmlString' => $vendorDir . '/nette/utils/src/compatibility.php',
|
||||
'Nette\\Utils\\Image' => $vendorDir . '/nette/utils/src/Utils/Image.php',
|
||||
'Nette\\Utils\\ImageColor' => $vendorDir . '/nette/utils/src/Utils/ImageColor.php',
|
||||
'Nette\\Utils\\ImageException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\ImageType' => $vendorDir . '/nette/utils/src/Utils/ImageType.php',
|
||||
'Nette\\Utils\\Iterables' => $vendorDir . '/nette/utils/src/Utils/Iterables.php',
|
||||
'Nette\\Utils\\Json' => $vendorDir . '/nette/utils/src/Utils/Json.php',
|
||||
'Nette\\Utils\\JsonException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\ObjectHelpers' => $vendorDir . '/nette/utils/src/Utils/ObjectHelpers.php',
|
||||
'Nette\\Utils\\ObjectMixin' => $vendorDir . '/nette/utils/src/Utils/ObjectMixin.php',
|
||||
'Nette\\Utils\\Paginator' => $vendorDir . '/nette/utils/src/Utils/Paginator.php',
|
||||
'Nette\\Utils\\Random' => $vendorDir . '/nette/utils/src/Utils/Random.php',
|
||||
'Nette\\Utils\\Reflection' => $vendorDir . '/nette/utils/src/Utils/Reflection.php',
|
||||
'Nette\\Utils\\ReflectionMethod' => $vendorDir . '/nette/utils/src/Utils/ReflectionMethod.php',
|
||||
'Nette\\Utils\\RegexpException' => $vendorDir . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\Strings' => $vendorDir . '/nette/utils/src/Utils/Strings.php',
|
||||
'Nette\\Utils\\Type' => $vendorDir . '/nette/utils/src/Utils/Type.php',
|
||||
@@ -2863,6 +2871,10 @@ return array(
|
||||
'NunoMaduro\\Collision\\Provider' => $vendorDir . '/nunomaduro/collision/src/Provider.php',
|
||||
'NunoMaduro\\Collision\\SolutionsRepositories\\NullSolutionsRepository' => $vendorDir . '/nunomaduro/collision/src/SolutionsRepositories/NullSolutionsRepository.php',
|
||||
'NunoMaduro\\Collision\\Writer' => $vendorDir . '/nunomaduro/collision/src/Writer.php',
|
||||
'Ossycodes\\FriendlyCaptcha\\Facades\\FriendlyCaptcha' => $vendorDir . '/ossycodes/friendlycaptcha/src/Facades/FriendlyCaptcha.php',
|
||||
'Ossycodes\\FriendlyCaptcha\\FriendlyCaptcha' => $vendorDir . '/ossycodes/friendlycaptcha/src/FriendlyCaptcha.php',
|
||||
'Ossycodes\\FriendlyCaptcha\\FriendlyCaptchaServiceProvider' => $vendorDir . '/ossycodes/friendlycaptcha/src/FriendlyCaptchaServiceProvider.php',
|
||||
'Ossycodes\\FriendlyCaptcha\\Rules\\FriendlyCaptcha' => $vendorDir . '/ossycodes/friendlycaptcha/src/Rules/FriendlyCaptcha.php',
|
||||
'PHPUnit\\Exception' => $vendorDir . '/phpunit/phpunit/src/Exception.php',
|
||||
'PHPUnit\\Framework\\ActualValueIsNotAnObjectException' => $vendorDir . '/phpunit/phpunit/src/Framework/Exception/ActualValueIsNotAnObjectException.php',
|
||||
'PHPUnit\\Framework\\Assert' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert.php',
|
||||
@@ -5149,6 +5161,7 @@ return array(
|
||||
'Termwind\\ValueObjects\\Style' => $vendorDir . '/nunomaduro/termwind/src/ValueObjects/Style.php',
|
||||
'Termwind\\ValueObjects\\Styles' => $vendorDir . '/nunomaduro/termwind/src/ValueObjects/Styles.php',
|
||||
'Tests\\CreatesApplication' => $baseDir . '/tests/CreatesApplication.php',
|
||||
'Tests\\Feature\\ContactPageTest' => $baseDir . '/tests/Feature/ContactPageTest.php',
|
||||
'Tests\\Feature\\ExampleTest' => $baseDir . '/tests/Feature/ExampleTest.php',
|
||||
'Tests\\TestCase' => $baseDir . '/tests/TestCase.php',
|
||||
'Tests\\Unit\\ExampleTest' => $baseDir . '/tests/Unit/ExampleTest.php',
|
||||
|
||||
8
vendor/composer/autoload_files.php
vendored
8
vendor/composer/autoload_files.php
vendored
@@ -8,25 +8,25 @@ $baseDir = dirname($vendorDir);
|
||||
return array(
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
|
||||
'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'3bd81c9b8fcc150b69d8b63b4d2ccf23' => $vendorDir . '/spatie/flare-client-php/src/helpers.php',
|
||||
'23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
|
||||
'09f6b20656683369174dd6fa83b7e5fb' => $vendorDir . '/symfony/polyfill-uuid/bootstrap.php',
|
||||
'a1105708a18b76903365ca1c4aa61b02' => $vendorDir . '/symfony/translation/Resources/functions.php',
|
||||
'c964ee0ededf28c96ebd9db5099ef910' => $vendorDir . '/guzzlehttp/promises/src/functions_include.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
'6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
|
||||
'35a6ad97d21e794e7e22a17d806652e4' => $vendorDir . '/nunomaduro/termwind/src/Functions.php',
|
||||
'801c31d8ed748cfa537fa45402288c95' => $vendorDir . '/psy/psysh/src/functions.php',
|
||||
'e39a8b23c42d4e1452234d762b03835a' => $vendorDir . '/ramsey/uuid/src/functions.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
'265b4faa2b3a9766332744949e83bf97' => $vendorDir . '/laravel/framework/src/Illuminate/Collections/helpers.php',
|
||||
'c7a3c339e7e14b60e06a2d7fcce9476b' => $vendorDir . '/laravel/framework/src/Illuminate/Events/functions.php',
|
||||
'f0906e6318348a765ffb6eb24e0d0938' => $vendorDir . '/laravel/framework/src/Illuminate/Foundation/helpers.php',
|
||||
|
||||
2
vendor/composer/autoload_psr4.php
vendored
2
vendor/composer/autoload_psr4.php
vendored
@@ -54,7 +54,9 @@ return array(
|
||||
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
|
||||
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
|
||||
'PhpOption\\' => array($vendorDir . '/phpoption/phpoption/src/PhpOption'),
|
||||
'Ossycodes\\FriendlyCaptcha\\' => array($vendorDir . '/ossycodes/friendlycaptcha/src'),
|
||||
'NunoMaduro\\Collision\\' => array($vendorDir . '/nunomaduro/collision/src'),
|
||||
'Nette\\' => array($vendorDir . '/nette/schema/src', $vendorDir . '/nette/utils/src'),
|
||||
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
|
||||
'Model\\' => array($baseDir . '/app/Models'),
|
||||
'League\\MimeTypeDetection\\' => array($vendorDir . '/league/mime-type-detection/src'),
|
||||
|
||||
37
vendor/composer/autoload_static.php
vendored
37
vendor/composer/autoload_static.php
vendored
@@ -9,25 +9,25 @@ class ComposerStaticInit5216a35d72a5119d2f4646cd700f802d
|
||||
public static $files = array (
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
'8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
|
||||
'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'3bd81c9b8fcc150b69d8b63b4d2ccf23' => __DIR__ . '/..' . '/spatie/flare-client-php/src/helpers.php',
|
||||
'23c18046f52bef3eea034657bafda50f' => __DIR__ . '/..' . '/symfony/polyfill-php81/bootstrap.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
|
||||
'09f6b20656683369174dd6fa83b7e5fb' => __DIR__ . '/..' . '/symfony/polyfill-uuid/bootstrap.php',
|
||||
'a1105708a18b76903365ca1c4aa61b02' => __DIR__ . '/..' . '/symfony/translation/Resources/functions.php',
|
||||
'c964ee0ededf28c96ebd9db5099ef910' => __DIR__ . '/..' . '/guzzlehttp/promises/src/functions_include.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
'6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
|
||||
'35a6ad97d21e794e7e22a17d806652e4' => __DIR__ . '/..' . '/nunomaduro/termwind/src/Functions.php',
|
||||
'801c31d8ed748cfa537fa45402288c95' => __DIR__ . '/..' . '/psy/psysh/src/functions.php',
|
||||
'e39a8b23c42d4e1452234d762b03835a' => __DIR__ . '/..' . '/ramsey/uuid/src/functions.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
'265b4faa2b3a9766332744949e83bf97' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/Collections/helpers.php',
|
||||
'c7a3c339e7e14b60e06a2d7fcce9476b' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/Events/functions.php',
|
||||
'f0906e6318348a765ffb6eb24e0d0938' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/Foundation/helpers.php',
|
||||
@@ -104,9 +104,14 @@ class ComposerStaticInit5216a35d72a5119d2f4646cd700f802d
|
||||
'PhpParser\\' => 10,
|
||||
'PhpOption\\' => 10,
|
||||
),
|
||||
'O' =>
|
||||
array (
|
||||
'Ossycodes\\FriendlyCaptcha\\' => 26,
|
||||
),
|
||||
'N' =>
|
||||
array (
|
||||
'NunoMaduro\\Collision\\' => 21,
|
||||
'Nette\\' => 6,
|
||||
),
|
||||
'M' =>
|
||||
array (
|
||||
@@ -371,10 +376,19 @@ class ComposerStaticInit5216a35d72a5119d2f4646cd700f802d
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/phpoption/phpoption/src/PhpOption',
|
||||
),
|
||||
'Ossycodes\\FriendlyCaptcha\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/ossycodes/friendlycaptcha/src',
|
||||
),
|
||||
'NunoMaduro\\Collision\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/nunomaduro/collision/src',
|
||||
),
|
||||
'Nette\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/nette/schema/src',
|
||||
1 => __DIR__ . '/..' . '/nette/utils/src',
|
||||
),
|
||||
'Monolog\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/monolog/monolog/src/Monolog',
|
||||
@@ -537,6 +551,7 @@ class ComposerStaticInit5216a35d72a5119d2f4646cd700f802d
|
||||
'App\\Http\\Controllers\\Auth\\RegisterController' => __DIR__ . '/../..' . '/app/Http/Controllers/Auth/RegisterController.php',
|
||||
'App\\Http\\Controllers\\Auth\\ResetPasswordController' => __DIR__ . '/../..' . '/app/Http/Controllers/Auth/ResetPasswordController.php',
|
||||
'App\\Http\\Controllers\\CalendarController' => __DIR__ . '/../..' . '/app/Http/Controllers/CalendarController.php',
|
||||
'App\\Http\\Controllers\\ContactController' => __DIR__ . '/../..' . '/app/Http/Controllers/ContactController.php',
|
||||
'App\\Http\\Controllers\\Controller' => __DIR__ . '/../..' . '/app/Http/Controllers/Controller.php',
|
||||
'App\\Http\\Controllers\\HomeController' => __DIR__ . '/../..' . '/app/Http/Controllers/HomeController.php',
|
||||
'App\\Http\\Controllers\\ImagesController' => __DIR__ . '/../..' . '/app/Http/Controllers/ImagesController.php',
|
||||
@@ -552,6 +567,7 @@ class ComposerStaticInit5216a35d72a5119d2f4646cd700f802d
|
||||
'App\\Http\\Middleware\\RedirectIfAuthenticated' => __DIR__ . '/../..' . '/app/Http/Middleware/RedirectIfAuthenticated.php',
|
||||
'App\\Http\\Middleware\\TrimStrings' => __DIR__ . '/../..' . '/app/Http/Middleware/TrimStrings.php',
|
||||
'App\\Http\\Middleware\\VerifyCsrfToken' => __DIR__ . '/../..' . '/app/Http/Middleware/VerifyCsrfToken.php',
|
||||
'App\\Mail\\ContactFormSubmitted' => __DIR__ . '/../..' . '/app/Mail/ContactFormSubmitted.php',
|
||||
'App\\Providers\\AppServiceProvider' => __DIR__ . '/../..' . '/app/Providers/AppServiceProvider.php',
|
||||
'App\\Providers\\AuthServiceProvider' => __DIR__ . '/../..' . '/app/Providers/AuthServiceProvider.php',
|
||||
'App\\Providers\\BroadcastServiceProvider' => __DIR__ . '/../..' . '/app/Providers/BroadcastServiceProvider.php',
|
||||
@@ -3325,6 +3341,7 @@ class ComposerStaticInit5216a35d72a5119d2f4646cd700f802d
|
||||
'Nette\\Schema\\Processor' => __DIR__ . '/..' . '/nette/schema/src/Schema/Processor.php',
|
||||
'Nette\\Schema\\Schema' => __DIR__ . '/..' . '/nette/schema/src/Schema/Schema.php',
|
||||
'Nette\\Schema\\ValidationException' => __DIR__ . '/..' . '/nette/schema/src/Schema/ValidationException.php',
|
||||
'Nette\\ShouldNotHappenException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
|
||||
'Nette\\SmartObject' => __DIR__ . '/..' . '/nette/utils/src/SmartObject.php',
|
||||
'Nette\\StaticClass' => __DIR__ . '/..' . '/nette/utils/src/StaticClass.php',
|
||||
'Nette\\UnexpectedValueException' => __DIR__ . '/..' . '/nette/utils/src/exceptions.php',
|
||||
@@ -3334,20 +3351,25 @@ class ComposerStaticInit5216a35d72a5119d2f4646cd700f802d
|
||||
'Nette\\Utils\\AssertionException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\Callback' => __DIR__ . '/..' . '/nette/utils/src/Utils/Callback.php',
|
||||
'Nette\\Utils\\DateTime' => __DIR__ . '/..' . '/nette/utils/src/Utils/DateTime.php',
|
||||
'Nette\\Utils\\FileInfo' => __DIR__ . '/..' . '/nette/utils/src/Utils/FileInfo.php',
|
||||
'Nette\\Utils\\FileSystem' => __DIR__ . '/..' . '/nette/utils/src/Utils/FileSystem.php',
|
||||
'Nette\\Utils\\Finder' => __DIR__ . '/..' . '/nette/utils/src/Utils/Finder.php',
|
||||
'Nette\\Utils\\Floats' => __DIR__ . '/..' . '/nette/utils/src/Utils/Floats.php',
|
||||
'Nette\\Utils\\Helpers' => __DIR__ . '/..' . '/nette/utils/src/Utils/Helpers.php',
|
||||
'Nette\\Utils\\Html' => __DIR__ . '/..' . '/nette/utils/src/Utils/Html.php',
|
||||
'Nette\\Utils\\IHtmlString' => __DIR__ . '/..' . '/nette/utils/src/compatibility.php',
|
||||
'Nette\\Utils\\Image' => __DIR__ . '/..' . '/nette/utils/src/Utils/Image.php',
|
||||
'Nette\\Utils\\ImageColor' => __DIR__ . '/..' . '/nette/utils/src/Utils/ImageColor.php',
|
||||
'Nette\\Utils\\ImageException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\ImageType' => __DIR__ . '/..' . '/nette/utils/src/Utils/ImageType.php',
|
||||
'Nette\\Utils\\Iterables' => __DIR__ . '/..' . '/nette/utils/src/Utils/Iterables.php',
|
||||
'Nette\\Utils\\Json' => __DIR__ . '/..' . '/nette/utils/src/Utils/Json.php',
|
||||
'Nette\\Utils\\JsonException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\ObjectHelpers' => __DIR__ . '/..' . '/nette/utils/src/Utils/ObjectHelpers.php',
|
||||
'Nette\\Utils\\ObjectMixin' => __DIR__ . '/..' . '/nette/utils/src/Utils/ObjectMixin.php',
|
||||
'Nette\\Utils\\Paginator' => __DIR__ . '/..' . '/nette/utils/src/Utils/Paginator.php',
|
||||
'Nette\\Utils\\Random' => __DIR__ . '/..' . '/nette/utils/src/Utils/Random.php',
|
||||
'Nette\\Utils\\Reflection' => __DIR__ . '/..' . '/nette/utils/src/Utils/Reflection.php',
|
||||
'Nette\\Utils\\ReflectionMethod' => __DIR__ . '/..' . '/nette/utils/src/Utils/ReflectionMethod.php',
|
||||
'Nette\\Utils\\RegexpException' => __DIR__ . '/..' . '/nette/utils/src/Utils/exceptions.php',
|
||||
'Nette\\Utils\\Strings' => __DIR__ . '/..' . '/nette/utils/src/Utils/Strings.php',
|
||||
'Nette\\Utils\\Type' => __DIR__ . '/..' . '/nette/utils/src/Utils/Type.php',
|
||||
@@ -3386,6 +3408,10 @@ class ComposerStaticInit5216a35d72a5119d2f4646cd700f802d
|
||||
'NunoMaduro\\Collision\\Provider' => __DIR__ . '/..' . '/nunomaduro/collision/src/Provider.php',
|
||||
'NunoMaduro\\Collision\\SolutionsRepositories\\NullSolutionsRepository' => __DIR__ . '/..' . '/nunomaduro/collision/src/SolutionsRepositories/NullSolutionsRepository.php',
|
||||
'NunoMaduro\\Collision\\Writer' => __DIR__ . '/..' . '/nunomaduro/collision/src/Writer.php',
|
||||
'Ossycodes\\FriendlyCaptcha\\Facades\\FriendlyCaptcha' => __DIR__ . '/..' . '/ossycodes/friendlycaptcha/src/Facades/FriendlyCaptcha.php',
|
||||
'Ossycodes\\FriendlyCaptcha\\FriendlyCaptcha' => __DIR__ . '/..' . '/ossycodes/friendlycaptcha/src/FriendlyCaptcha.php',
|
||||
'Ossycodes\\FriendlyCaptcha\\FriendlyCaptchaServiceProvider' => __DIR__ . '/..' . '/ossycodes/friendlycaptcha/src/FriendlyCaptchaServiceProvider.php',
|
||||
'Ossycodes\\FriendlyCaptcha\\Rules\\FriendlyCaptcha' => __DIR__ . '/..' . '/ossycodes/friendlycaptcha/src/Rules/FriendlyCaptcha.php',
|
||||
'PHPUnit\\Exception' => __DIR__ . '/..' . '/phpunit/phpunit/src/Exception.php',
|
||||
'PHPUnit\\Framework\\ActualValueIsNotAnObjectException' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Exception/ActualValueIsNotAnObjectException.php',
|
||||
'PHPUnit\\Framework\\Assert' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Assert.php',
|
||||
@@ -5672,6 +5698,7 @@ class ComposerStaticInit5216a35d72a5119d2f4646cd700f802d
|
||||
'Termwind\\ValueObjects\\Style' => __DIR__ . '/..' . '/nunomaduro/termwind/src/ValueObjects/Style.php',
|
||||
'Termwind\\ValueObjects\\Styles' => __DIR__ . '/..' . '/nunomaduro/termwind/src/ValueObjects/Styles.php',
|
||||
'Tests\\CreatesApplication' => __DIR__ . '/../..' . '/tests/CreatesApplication.php',
|
||||
'Tests\\Feature\\ContactPageTest' => __DIR__ . '/../..' . '/tests/Feature/ContactPageTest.php',
|
||||
'Tests\\Feature\\ExampleTest' => __DIR__ . '/../..' . '/tests/Feature/ExampleTest.php',
|
||||
'Tests\\TestCase' => __DIR__ . '/../..' . '/tests/TestCase.php',
|
||||
'Tests\\Unit\\ExampleTest' => __DIR__ . '/../..' . '/tests/Unit/ExampleTest.php',
|
||||
|
||||
129
vendor/composer/installed.json
vendored
129
vendor/composer/installed.json
vendored
@@ -2538,37 +2538,40 @@
|
||||
},
|
||||
{
|
||||
"name": "nette/schema",
|
||||
"version": "v1.2.3",
|
||||
"version_normalized": "1.2.3.0",
|
||||
"version": "v1.3.3",
|
||||
"version_normalized": "1.3.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nette/schema.git",
|
||||
"reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f"
|
||||
"reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nette/schema/zipball/abbdbb70e0245d5f3bf77874cea1dfb0c930d06f",
|
||||
"reference": "abbdbb70e0245d5f3bf77874cea1dfb0c930d06f",
|
||||
"url": "https://api.github.com/repos/nette/schema/zipball/2befc2f42d7c715fd9d95efc31b1081e5d765004",
|
||||
"reference": "2befc2f42d7c715fd9d95efc31b1081e5d765004",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"nette/utils": "^2.5.7 || ^3.1.5 || ^4.0",
|
||||
"php": ">=7.1 <8.3"
|
||||
"nette/utils": "^4.0",
|
||||
"php": "8.1 - 8.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "^2.3 || ^2.4",
|
||||
"phpstan/phpstan-nette": "^1.0",
|
||||
"tracy/tracy": "^2.7"
|
||||
"nette/tester": "^2.5.2",
|
||||
"phpstan/phpstan-nette": "^2.0@stable",
|
||||
"tracy/tracy": "^2.8"
|
||||
},
|
||||
"time": "2022-10-13T01:24:26+00:00",
|
||||
"time": "2025-10-30T22:57:59+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.2-dev"
|
||||
"dev-master": "1.3-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Nette\\": "src"
|
||||
},
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
@@ -2597,35 +2600,37 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nette/schema/issues",
|
||||
"source": "https://github.com/nette/schema/tree/v1.2.3"
|
||||
"source": "https://github.com/nette/schema/tree/v1.3.3"
|
||||
},
|
||||
"install-path": "../nette/schema"
|
||||
},
|
||||
{
|
||||
"name": "nette/utils",
|
||||
"version": "v3.2.8",
|
||||
"version_normalized": "3.2.8.0",
|
||||
"version": "v4.1.1",
|
||||
"version_normalized": "4.1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nette/utils.git",
|
||||
"reference": "02a54c4c872b99e4ec05c4aec54b5a06eb0f6368"
|
||||
"reference": "c99059c0315591f1a0db7ad6002000288ab8dc72"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/02a54c4c872b99e4ec05c4aec54b5a06eb0f6368",
|
||||
"reference": "02a54c4c872b99e4ec05c4aec54b5a06eb0f6368",
|
||||
"url": "https://api.github.com/repos/nette/utils/zipball/c99059c0315591f1a0db7ad6002000288ab8dc72",
|
||||
"reference": "c99059c0315591f1a0db7ad6002000288ab8dc72",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2 <8.3"
|
||||
"php": "8.2 - 8.5"
|
||||
},
|
||||
"conflict": {
|
||||
"nette/di": "<3.0.6"
|
||||
"nette/finder": "<3",
|
||||
"nette/schema": "<1.2.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "~2.0",
|
||||
"phpstan/phpstan": "^1.0",
|
||||
"tracy/tracy": "^2.3"
|
||||
"jetbrains/phpstorm-attributes": "^1.2",
|
||||
"nette/tester": "^2.5",
|
||||
"phpstan/phpstan-nette": "^2.0@stable",
|
||||
"tracy/tracy": "^2.9"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-gd": "to use Image",
|
||||
@@ -2633,18 +2638,20 @@
|
||||
"ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
|
||||
"ext-json": "to use Nette\\Utils\\Json",
|
||||
"ext-mbstring": "to use Strings::lower() etc...",
|
||||
"ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()",
|
||||
"ext-xml": "to use Strings::length() etc. when mbstring is not available"
|
||||
"ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
|
||||
},
|
||||
"time": "2022-09-12T23:36:20+00:00",
|
||||
"time": "2025-12-22T12:14:32+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
"dev-master": "4.1-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Nette\\": "src"
|
||||
},
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
@@ -2685,7 +2692,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nette/utils/issues",
|
||||
"source": "https://github.com/nette/utils/tree/v3.2.8"
|
||||
"source": "https://github.com/nette/utils/tree/v4.1.1"
|
||||
},
|
||||
"install-path": "../nette/utils"
|
||||
},
|
||||
@@ -2928,6 +2935,72 @@
|
||||
],
|
||||
"install-path": "../nunomaduro/termwind"
|
||||
},
|
||||
{
|
||||
"name": "ossycodes/friendlycaptcha",
|
||||
"version": "v3.0.0",
|
||||
"version_normalized": "3.0.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ossycodes/friendlycaptcha.git",
|
||||
"reference": "b18dfab44ee5fff7d75412232eb6f834864efde6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ossycodes/friendlycaptcha/zipball/b18dfab44ee5fff7d75412232eb6f834864efde6",
|
||||
"reference": "b18dfab44ee5fff7d75412232eb6f834864efde6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"guzzlehttp/guzzle": "^7.0",
|
||||
"illuminate/support": "^8.0|^9.0|^10.0|^11.0|^12.0",
|
||||
"php": "^7.4|^8.0|^8.1|^8.2|^8.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"orchestra/testbench": "^6.0|^7.0|^8.0|^9.0",
|
||||
"phpunit/phpunit": "^8.0 || ^9.5 || ^10.5 || ^11.0 || ^12.0"
|
||||
},
|
||||
"time": "2025-05-08T13:30:49+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"aliases": {
|
||||
"FriendlyCaptcha": "Ossycodes\\FriendlyCaptcha\\Facades\\FriendlyCaptcha"
|
||||
},
|
||||
"providers": [
|
||||
"Ossycodes\\FriendlyCaptcha\\FriendlyCaptchaServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Ossycodes\\FriendlyCaptcha\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "ossycodes",
|
||||
"email": "osaigbovoemmanuel1@gmail.com",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "A simple package to help integrate FriendlyCaptcha in your Laravel applications.",
|
||||
"homepage": "https://github.com/ossycodes/friendlycaptcha",
|
||||
"keywords": [
|
||||
"captcha",
|
||||
"friendlycaptcha",
|
||||
"laravel"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/ossycodes/friendlycaptcha/issues",
|
||||
"source": "https://github.com/ossycodes/friendlycaptcha/tree/v3.0.0"
|
||||
},
|
||||
"install-path": "../ossycodes/friendlycaptcha"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
"version": "2.0.3",
|
||||
|
||||
33
vendor/composer/installed.php
vendored
33
vendor/composer/installed.php
vendored
@@ -1,9 +1,9 @@
|
||||
<?php return array(
|
||||
'root' => array(
|
||||
'name' => 'laravel/laravel',
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => NULL,
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => '6786ac7ce14c02cbed9252a569ceecc3d9f4b38d',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
@@ -374,9 +374,9 @@
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'laravel/laravel' => array(
|
||||
'pretty_version' => '1.0.0+no-version-set',
|
||||
'version' => '1.0.0.0',
|
||||
'reference' => NULL,
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => '6786ac7ce14c02cbed9252a569ceecc3d9f4b38d',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
@@ -515,18 +515,18 @@
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'nette/schema' => array(
|
||||
'pretty_version' => 'v1.2.3',
|
||||
'version' => '1.2.3.0',
|
||||
'reference' => 'abbdbb70e0245d5f3bf77874cea1dfb0c930d06f',
|
||||
'pretty_version' => 'v1.3.3',
|
||||
'version' => '1.3.3.0',
|
||||
'reference' => '2befc2f42d7c715fd9d95efc31b1081e5d765004',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../nette/schema',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'nette/utils' => array(
|
||||
'pretty_version' => 'v3.2.8',
|
||||
'version' => '3.2.8.0',
|
||||
'reference' => '02a54c4c872b99e4ec05c4aec54b5a06eb0f6368',
|
||||
'pretty_version' => 'v4.1.1',
|
||||
'version' => '4.1.1.0',
|
||||
'reference' => 'c99059c0315591f1a0db7ad6002000288ab8dc72',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../nette/utils',
|
||||
'aliases' => array(),
|
||||
@@ -559,6 +559,15 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'ossycodes/friendlycaptcha' => array(
|
||||
'pretty_version' => 'v3.0.0',
|
||||
'version' => '3.0.0.0',
|
||||
'reference' => 'b18dfab44ee5fff7d75412232eb6f834864efde6',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../ossycodes/friendlycaptcha',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'phar-io/manifest' => array(
|
||||
'pretty_version' => '2.0.3',
|
||||
'version' => '2.0.3.0',
|
||||
|
||||
9
vendor/composer/platform_check.php
vendored
9
vendor/composer/platform_check.php
vendored
@@ -4,8 +4,8 @@
|
||||
|
||||
$issues = array();
|
||||
|
||||
if (!(PHP_VERSION_ID >= 80002)) {
|
||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.0.2". You are running ' . PHP_VERSION . '.';
|
||||
if (!(PHP_VERSION_ID >= 80200)) {
|
||||
$issues[] = 'Your Composer dependencies require a PHP version ">= 8.2.0". You are running ' . PHP_VERSION . '.';
|
||||
}
|
||||
|
||||
if ($issues) {
|
||||
@@ -19,8 +19,7 @@ if ($issues) {
|
||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||
E_USER_ERROR
|
||||
throw new \RuntimeException(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues)
|
||||
);
|
||||
}
|
||||
|
||||
17
vendor/nette/schema/composer.json
vendored
17
vendor/nette/schema/composer.json
vendored
@@ -15,16 +15,19 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.1 <8.3",
|
||||
"nette/utils": "^2.5.7 || ^3.1.5 || ^4.0"
|
||||
"php": "8.1 - 8.5",
|
||||
"nette/utils": "^4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "^2.3 || ^2.4",
|
||||
"tracy/tracy": "^2.7",
|
||||
"phpstan/phpstan-nette": "^1.0"
|
||||
"nette/tester": "^2.5.2",
|
||||
"tracy/tracy": "^2.8",
|
||||
"phpstan/phpstan-nette": "^2.0@stable"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["src/"]
|
||||
"classmap": ["src/"],
|
||||
"psr-4": {
|
||||
"Nette\\": "src"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"scripts": {
|
||||
@@ -33,7 +36,7 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.2-dev"
|
||||
"dev-master": "1.3-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
33
vendor/nette/schema/contributing.md
vendored
33
vendor/nette/schema/contributing.md
vendored
@@ -1,33 +0,0 @@
|
||||
How to contribute & use the issue tracker
|
||||
=========================================
|
||||
|
||||
Nette welcomes your contributions. There are several ways to help out:
|
||||
|
||||
* Create an issue on GitHub, if you have found a bug
|
||||
* Write test cases for open bug issues
|
||||
* Write fixes for open bug/feature issues, preferably with test cases included
|
||||
* Contribute to the [documentation](https://nette.org/en/writing)
|
||||
|
||||
Issues
|
||||
------
|
||||
|
||||
Please **do not use the issue tracker to ask questions**. We will be happy to help you
|
||||
on [Nette forum](https://forum.nette.org) or chat with us on [Gitter](https://gitter.im/nette/nette).
|
||||
|
||||
A good bug report shouldn't leave others needing to chase you up for more
|
||||
information. Please try to be as detailed as possible in your report.
|
||||
|
||||
**Feature requests** are welcome. But take a moment to find out whether your idea
|
||||
fits with the scope and aims of the project. It's up to *you* to make a strong
|
||||
case to convince the project's developers of the merits of this feature.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
If you'd like to contribute, please take a moment to read [the contributing guide](https://nette.org/en/contributing).
|
||||
|
||||
The best way to propose a feature is to discuss your ideas on [Nette forum](https://forum.nette.org) before implementing them.
|
||||
|
||||
Please do not fix whitespace, format code, or make a purely cosmetic patch.
|
||||
|
||||
Thanks! :heart:
|
||||
205
vendor/nette/schema/readme.md
vendored
205
vendor/nette/schema/readme.md
vendored
@@ -1,5 +1,4 @@
|
||||
Nette Schema
|
||||
************
|
||||
# Nette Schema
|
||||
|
||||
[](https://packagist.org/packages/nette/schema)
|
||||
[](https://github.com/nette/schema/actions)
|
||||
@@ -21,7 +20,7 @@ Installation:
|
||||
composer require nette/schema
|
||||
```
|
||||
|
||||
It requires PHP version 7.1 and supports PHP up to 8.2.
|
||||
It requires PHP version 8.1 and supports PHP up to 8.5.
|
||||
|
||||
|
||||
[Support Me](https://github.com/sponsors/dg)
|
||||
@@ -39,7 +38,7 @@ Basic Usage
|
||||
|
||||
In variable `$schema` we have a validation schema (what exactly this means and how to create it we will say later) and in variable `$data` we have a data structure that we want to validate and normalize. This can be, for example, data sent by the user through an API, configuration file, etc.
|
||||
|
||||
The task is handled by the [Nette\Schema\Processor](https://api.nette.org/3.0/Nette/Schema/Processor.html) class, which processes the input and either returns normalized data or throws an [Nette\Schema\ValidationException](https://api.nette.org/3.0/Nette/Schema/ValidationException.html) exception on error.
|
||||
The task is handled by the [Nette\Schema\Processor](https://api.nette.org/schema/master/Nette/Schema/Processor.html) class, which processes the input and either returns normalized data or throws an [Nette\Schema\ValidationException](https://api.nette.org/schema/master/Nette/Schema/ValidationException.html) exception on error.
|
||||
|
||||
```php
|
||||
$processor = new Nette\Schema\Processor;
|
||||
@@ -51,20 +50,20 @@ try {
|
||||
}
|
||||
```
|
||||
|
||||
Method `$e->getMessages()` returns array of all message strings and `$e->getMessageObjects()` return all messages as [Nette\Schema\Message](https://api.nette.org/3.1/Nette/Schema/Message.html) objects.
|
||||
Method `$e->getMessages()` returns array of all message strings and `$e->getMessageObjects()` return all messages as [Nette\Schema\Message](https://api.nette.org/schema/master/Nette/Schema/Message.html) objects.
|
||||
|
||||
|
||||
Defining Schema
|
||||
---------------
|
||||
|
||||
And now let's create a schema. The class [Nette\Schema\Expect](https://api.nette.org/3.0/Nette/Schema/Expect.html) is used to define it, we actually define expectations of what the data should look like. Let's say that the input data must be a structure (e.g. an array) containing elements `processRefund` of type bool and `refundAmount` of type int.
|
||||
And now let's create a schema. The class [Nette\Schema\Expect](https://api.nette.org/schema/master/Nette/Schema/Expect.html) is used to define it, we actually define expectations of what the data should look like. Let's say that the input data must be a structure (e.g. an array) containing elements `processRefund` of type bool and `refundAmount` of type int.
|
||||
|
||||
```php
|
||||
use Nette\Schema\Expect;
|
||||
|
||||
$schema = Expect::structure([
|
||||
'processRefund' => Expect::bool(),
|
||||
'refundAmount' => Expect::int(),
|
||||
'processRefund' => Expect::bool(),
|
||||
'refundAmount' => Expect::int(),
|
||||
]);
|
||||
```
|
||||
|
||||
@@ -74,8 +73,8 @@ Lets send the following data for validation:
|
||||
|
||||
```php
|
||||
$data = [
|
||||
'processRefund' => true,
|
||||
'refundAmount' => 17,
|
||||
'processRefund' => true,
|
||||
'refundAmount' => 17,
|
||||
];
|
||||
|
||||
$normalized = $processor->process($schema, $data); // OK, it passes
|
||||
@@ -87,7 +86,7 @@ All elements of the structure are optional and have a default value `null`. Exam
|
||||
|
||||
```php
|
||||
$data = [
|
||||
'refundAmount' => 17,
|
||||
'refundAmount' => 17,
|
||||
];
|
||||
|
||||
$normalized = $processor->process($schema, $data); // OK, it passes
|
||||
@@ -102,8 +101,8 @@ And what if we wanted to accept `1` and `0` besides booleans? Then we list the a
|
||||
|
||||
```php
|
||||
$schema = Expect::structure([
|
||||
'processRefund' => Expect::anyOf(true, false, 1, 0)->castTo('bool'),
|
||||
'refundAmount' => Expect::int(),
|
||||
'processRefund' => Expect::anyOf(true, false, 1, 0)->castTo('bool'),
|
||||
'refundAmount' => Expect::int(),
|
||||
]);
|
||||
|
||||
$normalized = $processor->process($schema, $data);
|
||||
@@ -113,7 +112,6 @@ is_bool($normalized->processRefund); // true
|
||||
Now you know the basics of how the schema is defined and how the individual elements of the structure behave. We will now show what all the other elements can be used in defining a schema.
|
||||
|
||||
|
||||
|
||||
Data Types: type()
|
||||
------------------
|
||||
|
||||
@@ -152,6 +150,15 @@ $processor->process($schema, ['a' => 'hello', 'b' => 'world']); // OK
|
||||
$processor->process($schema, ['key' => 123]); // ERROR: 123 is not a string
|
||||
```
|
||||
|
||||
The second parameter can be used to specify keys (since version 1.2):
|
||||
|
||||
```php
|
||||
$schema = Expect::arrayOf('string', 'int');
|
||||
|
||||
$processor->process($schema, ['hello', 'world']); // OK
|
||||
$processor->process($schema, ['a' => 'hello']); // ERROR: 'a' is not int
|
||||
```
|
||||
|
||||
The list is an indexed array:
|
||||
|
||||
```php
|
||||
@@ -169,7 +176,7 @@ The parameter can also be a schema, so we can write:
|
||||
Expect::arrayOf(Expect::bool())
|
||||
```
|
||||
|
||||
The default value is an empty array. If you specify default value, it will be merged with the passed data. This can be disabled using `mergeDefaults(false)`.
|
||||
The default value is an empty array. If you specify a default value, it will be merged with the passed data. This can be disabled using `mergeDefaults(false)`.
|
||||
|
||||
|
||||
Enumeration: anyOf()
|
||||
@@ -179,7 +186,7 @@ Enumeration: anyOf()
|
||||
|
||||
```php
|
||||
$schema = Expect::listOf(
|
||||
Expect::anyOf('a', true, null)
|
||||
Expect::anyOf('a', true, null),
|
||||
);
|
||||
|
||||
$processor->process($schema, ['a', true, null, 'a']); // OK
|
||||
@@ -190,14 +197,21 @@ The enumeration elements can also be schemas:
|
||||
|
||||
```php
|
||||
$schema = Expect::listOf(
|
||||
Expect::anyOf(Expect::string(), true, null)
|
||||
Expect::anyOf(Expect::string(), true, null),
|
||||
);
|
||||
|
||||
$processor->process($schema, ['foo', true, null, 'bar']); // OK
|
||||
$processor->process($schema, [123]); // ERROR
|
||||
```
|
||||
|
||||
The default value is `null`.
|
||||
The `anyOf()` method accepts variants as individual parameters, not as array. To pass it an array of values, use the unpacking operator `anyOf(...$variants)`.
|
||||
|
||||
The default value is `null`. Use the `firstIsDefault()` method to make the first element the default:
|
||||
|
||||
```php
|
||||
// default is 'hello'
|
||||
Expect::anyOf(Expect::string('hello'), true, null)->firstIsDefault();
|
||||
```
|
||||
|
||||
|
||||
Structures
|
||||
@@ -216,12 +230,24 @@ $schema = Expect::structure([
|
||||
]);
|
||||
|
||||
$processor->process($schema, ['optional' => '']);
|
||||
// ERROR: item 'required' is missing
|
||||
// ERROR: option 'required' is missing
|
||||
|
||||
$processor->process($schema, ['required' => 'foo']);
|
||||
// OK, returns {'required' => 'foo', 'optional' => null}
|
||||
```
|
||||
|
||||
If you do not want to output properties with only a default value, use `skipDefaults()`:
|
||||
|
||||
```php
|
||||
$schema = Expect::structure([
|
||||
'required' => Expect::string()->required(),
|
||||
'optional' => Expect::string(),
|
||||
])->skipDefaults();
|
||||
|
||||
$processor->process($schema, ['required' => 'foo']);
|
||||
// OK, returns {'required' => 'foo'}
|
||||
```
|
||||
|
||||
Although `null` is the default value of the `optional` property, it is not allowed in the input data (the value must be a string). Properties accepting `null` are defined using `nullable()`:
|
||||
|
||||
```php
|
||||
@@ -259,10 +285,11 @@ $processor->process($schema, ['additional' => 1]); // OK
|
||||
$processor->process($schema, ['additional' => true]); // ERROR
|
||||
```
|
||||
|
||||
|
||||
Deprecations
|
||||
------------
|
||||
|
||||
You can deprecate property using the `deprecated([string $message])` method. Deprecation notices are returned by `$processor->getWarnings()` (since v1.1):
|
||||
You can deprecate property using the `deprecated([string $message])` method. Deprecation notices are returned by `$processor->getWarnings()`:
|
||||
|
||||
```php
|
||||
$schema = Expect::structure([
|
||||
@@ -273,6 +300,7 @@ $processor->process($schema, ['old' => 1]); // OK
|
||||
$processor->getWarnings(); // ["The item 'old' is deprecated"]
|
||||
```
|
||||
|
||||
|
||||
Ranges: min() max()
|
||||
-------------------
|
||||
|
||||
@@ -322,7 +350,7 @@ Custom Assertions: assert()
|
||||
You can add any other restrictions using `assert(callable $fn)`.
|
||||
|
||||
```php
|
||||
$countIsEven = function ($v) { return count($v) % 2 === 0; };
|
||||
$countIsEven = fn($v) => count($v) % 2 === 0;
|
||||
|
||||
$schema = Expect::arrayOf('string')
|
||||
->assert($countIsEven); // the count must be even
|
||||
@@ -337,7 +365,7 @@ Or
|
||||
Expect::string()->assert('is_file'); // the file must exist
|
||||
```
|
||||
|
||||
You can add your own description for each assertions. It will be part of the error message.
|
||||
You can add your own description for each assertion. It will be part of the error message.
|
||||
|
||||
```php
|
||||
$schema = Expect::arrayOf('string')
|
||||
@@ -347,7 +375,106 @@ $processor->process($schema, ['a', 'b', 'c']);
|
||||
// Failed assertion "Even items in array" for item with value array.
|
||||
```
|
||||
|
||||
The method can be called repeatedly to add more assertions.
|
||||
The method can be called repeatedly to add multiple constraints. It can be intermixed with calls to `transform()` and `castTo()`.
|
||||
|
||||
|
||||
Transformation: transform()
|
||||
---------------------------
|
||||
|
||||
Successfully validated data can be modified using a custom function:
|
||||
|
||||
```php
|
||||
// conversion to uppercase:
|
||||
Expect::string()->transform(fn(string $s) => strtoupper($s));
|
||||
```
|
||||
|
||||
The method can be called repeatedly to add multiple transformations. It can be intermixed with calls to `assert()` and `castTo()`. The operations will be executed in the order in which they are declared:
|
||||
|
||||
```php
|
||||
Expect::type('string|int')
|
||||
->castTo('string')
|
||||
->assert('ctype_lower', 'All characters must be lowercased')
|
||||
->transform(fn(string $s) => strtoupper($s)); // conversion to uppercase
|
||||
```
|
||||
|
||||
The `transform()` method can both transform and validate the value simultaneously. This is often simpler and less redundant than chaining `transform()` and `assert()`. For this purpose, the function receives a [Nette\Schema\Context](https://api.nette.org/schema/master/Nette/Schema/Context.html) object with an `addError()` method, which can be used to add information about validation issues:
|
||||
|
||||
```php
|
||||
Expect::string()
|
||||
->transform(function (string $s, Nette\Schema\Context $context) {
|
||||
if (!ctype_lower($s)) {
|
||||
$context->addError('All characters must be lowercased', 'my.case.error');
|
||||
return null;
|
||||
}
|
||||
|
||||
return strtoupper($s);
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
Casting: castTo()
|
||||
-----------------
|
||||
|
||||
Successfully validated data can be cast:
|
||||
|
||||
```php
|
||||
Expect::scalar()->castTo('string');
|
||||
```
|
||||
|
||||
In addition to native PHP types, you can also cast to classes. It distinguishes whether it is a simple class without a constructor or a class with a constructor. If the class has no constructor, an instance of it is created and all elements of the structure are written to its properties:
|
||||
|
||||
```php
|
||||
class Info
|
||||
{
|
||||
public bool $processRefund;
|
||||
public int $refundAmount;
|
||||
}
|
||||
|
||||
Expect::structure([
|
||||
'processRefund' => Expect::bool(),
|
||||
'refundAmount' => Expect::int(),
|
||||
])->castTo(Info::class);
|
||||
|
||||
// creates '$obj = new Info' and writes to $obj->processRefund and $obj->refundAmount
|
||||
```
|
||||
|
||||
If the class has a constructor, the elements of the structure are passed as named parameters to the constructor:
|
||||
|
||||
```php
|
||||
class Info
|
||||
{
|
||||
public function __construct(
|
||||
public bool $processRefund,
|
||||
public int $refundAmount,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
// creates $obj = new Info(processRefund: ..., refundAmount: ...)
|
||||
```
|
||||
|
||||
Casting combined with a scalar parameter creates an object and passes the value as the sole parameter to the constructor:
|
||||
|
||||
```php
|
||||
Expect::string()->castTo(DateTime::class);
|
||||
// creates new DateTime(...)
|
||||
```
|
||||
|
||||
|
||||
Normalization: before()
|
||||
-----------------------
|
||||
|
||||
Prior to the validation itself, the data can be normalized using the method `before()`. As an example, let's have an element that must be an array of strings (eg `['a', 'b', 'c']`), but receives input in the form of a string `a b c`:
|
||||
|
||||
```php
|
||||
$explode = fn($v) => explode(' ', $v);
|
||||
|
||||
$schema = Expect::arrayOf('string')
|
||||
->before($explode);
|
||||
|
||||
$normalized = $processor->process($schema, 'a b c');
|
||||
// OK, returns ['a', 'b', 'c']
|
||||
```
|
||||
|
||||
|
||||
Mapping to Objects: from()
|
||||
@@ -407,35 +534,3 @@ $schema = Expect::from(new Config, [
|
||||
'name' => Expect::string()->pattern('\w:.*'),
|
||||
]);
|
||||
```
|
||||
|
||||
|
||||
Casting: castTo()
|
||||
-----------------
|
||||
|
||||
Successfully validated data can be cast:
|
||||
|
||||
```php
|
||||
Expect::scalar()->castTo('string');
|
||||
```
|
||||
|
||||
In addition to native PHP types, you can also cast to classes:
|
||||
|
||||
```php
|
||||
Expect::scalar()->castTo('AddressEntity');
|
||||
```
|
||||
|
||||
|
||||
Normalization: before()
|
||||
-----------------------
|
||||
|
||||
Prior to the validation itself, the data can be normalized using the method `before()`. As an example, let's have an element that must be an array of strings (eg `['a', 'b', 'c']`), but receives input in the form of a string `a b c`:
|
||||
|
||||
```php
|
||||
$explode = function ($v) { return explode(' ', $v); };
|
||||
|
||||
$schema = Expect::arrayOf('string')
|
||||
->before($explode);
|
||||
|
||||
$normalized = $processor->process($schema, 'a b c');
|
||||
// OK, returns ['a', 'b', 'c']
|
||||
```
|
||||
|
||||
26
vendor/nette/schema/src/Schema/Context.php
vendored
26
vendor/nette/schema/src/Schema/Context.php
vendored
@@ -9,30 +9,26 @@ declare(strict_types=1);
|
||||
|
||||
namespace Nette\Schema;
|
||||
|
||||
use Nette;
|
||||
use function count;
|
||||
|
||||
|
||||
final class Context
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var bool */
|
||||
public $skipDefaults = false;
|
||||
public bool $skipDefaults = false;
|
||||
|
||||
/** @var string[] */
|
||||
public $path = [];
|
||||
public array $path = [];
|
||||
|
||||
/** @var bool */
|
||||
public $isKey = false;
|
||||
public bool $isKey = false;
|
||||
|
||||
/** @var Message[] */
|
||||
public $errors = [];
|
||||
public array $errors = [];
|
||||
|
||||
/** @var Message[] */
|
||||
public $warnings = [];
|
||||
public array $warnings = [];
|
||||
|
||||
/** @var array[] */
|
||||
public $dynamics = [];
|
||||
public array $dynamics = [];
|
||||
|
||||
|
||||
public function addError(string $message, string $code, array $variables = []): Message
|
||||
@@ -46,4 +42,12 @@ final class Context
|
||||
{
|
||||
return $this->warnings[] = new Message($message, $code, $this->path, $variables);
|
||||
}
|
||||
|
||||
|
||||
/** @return \Closure(): bool */
|
||||
public function createChecker(): \Closure
|
||||
{
|
||||
$count = count($this->errors);
|
||||
return fn(): bool => $count === count($this->errors);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,21 +13,17 @@ use Nette;
|
||||
use Nette\Schema\Context;
|
||||
use Nette\Schema\Helpers;
|
||||
use Nette\Schema\Schema;
|
||||
use function array_merge, array_unique, implode, is_array;
|
||||
|
||||
|
||||
final class AnyOf implements Schema
|
||||
{
|
||||
use Base;
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var array */
|
||||
private $set;
|
||||
private array $set;
|
||||
|
||||
|
||||
/**
|
||||
* @param mixed|Schema ...$set
|
||||
*/
|
||||
public function __construct(...$set)
|
||||
public function __construct(mixed ...$set)
|
||||
{
|
||||
if (!$set) {
|
||||
throw new Nette\InvalidStateException('The enumeration must not be empty.');
|
||||
@@ -61,16 +57,16 @@ final class AnyOf implements Schema
|
||||
/********************* processing ****************d*g**/
|
||||
|
||||
|
||||
public function normalize($value, Context $context)
|
||||
public function normalize(mixed $value, Context $context): mixed
|
||||
{
|
||||
return $this->doNormalize($value, $context);
|
||||
}
|
||||
|
||||
|
||||
public function merge($value, $base)
|
||||
public function merge(mixed $value, mixed $base): mixed
|
||||
{
|
||||
if (is_array($value) && isset($value[Helpers::PREVENT_MERGING])) {
|
||||
unset($value[Helpers::PREVENT_MERGING]);
|
||||
if (is_array($value) && isset($value[Helpers::PreventMerging])) {
|
||||
unset($value[Helpers::PreventMerging]);
|
||||
return $value;
|
||||
}
|
||||
|
||||
@@ -78,7 +74,16 @@ final class AnyOf implements Schema
|
||||
}
|
||||
|
||||
|
||||
public function complete($value, Context $context)
|
||||
public function complete(mixed $value, Context $context): mixed
|
||||
{
|
||||
$isOk = $context->createChecker();
|
||||
$value = $this->findAlternative($value, $context);
|
||||
$isOk() && $value = $this->doTransform($value, $context);
|
||||
return $isOk() ? $value : null;
|
||||
}
|
||||
|
||||
|
||||
private function findAlternative(mixed $value, Context $context): mixed
|
||||
{
|
||||
$expecteds = $innerErrors = [];
|
||||
foreach ($this->set as $item) {
|
||||
@@ -88,7 +93,7 @@ final class AnyOf implements Schema
|
||||
$res = $item->complete($item->normalize($value, $dolly), $dolly);
|
||||
if (!$dolly->errors) {
|
||||
$context->warnings = array_merge($context->warnings, $dolly->warnings);
|
||||
return $this->doFinalize($res, $context);
|
||||
return $res;
|
||||
}
|
||||
|
||||
foreach ($dolly->errors as $error) {
|
||||
@@ -100,7 +105,7 @@ final class AnyOf implements Schema
|
||||
}
|
||||
} else {
|
||||
if ($item === $value) {
|
||||
return $this->doFinalize($value, $context);
|
||||
return $value;
|
||||
}
|
||||
|
||||
$expecteds[] = Nette\Schema\Helpers::formatValue($item);
|
||||
@@ -112,22 +117,24 @@ final class AnyOf implements Schema
|
||||
} else {
|
||||
$context->addError(
|
||||
'The %label% %path% expects to be %expected%, %value% given.',
|
||||
Nette\Schema\Message::TYPE_MISMATCH,
|
||||
Nette\Schema\Message::TypeMismatch,
|
||||
[
|
||||
'value' => $value,
|
||||
'expected' => implode('|', array_unique($expecteds)),
|
||||
]
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public function completeDefault(Context $context)
|
||||
public function completeDefault(Context $context): mixed
|
||||
{
|
||||
if ($this->required) {
|
||||
$context->addError(
|
||||
'The mandatory item %path% is missing.',
|
||||
Nette\Schema\Message::MISSING_ITEM
|
||||
Nette\Schema\Message::MissingItem,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
161
vendor/nette/schema/src/Schema/Elements/Base.php
vendored
161
vendor/nette/schema/src/Schema/Elements/Base.php
vendored
@@ -11,6 +11,8 @@ namespace Nette\Schema\Elements;
|
||||
|
||||
use Nette;
|
||||
use Nette\Schema\Context;
|
||||
use Nette\Schema\Helpers;
|
||||
use function count, is_string;
|
||||
|
||||
|
||||
/**
|
||||
@@ -18,26 +20,18 @@ use Nette\Schema\Context;
|
||||
*/
|
||||
trait Base
|
||||
{
|
||||
/** @var bool */
|
||||
private $required = false;
|
||||
private bool $required = false;
|
||||
private mixed $default = null;
|
||||
|
||||
/** @var mixed */
|
||||
private $default;
|
||||
|
||||
/** @var callable|null */
|
||||
/** @var ?callable */
|
||||
private $before;
|
||||
|
||||
/** @var array[] */
|
||||
private $asserts = [];
|
||||
|
||||
/** @var string|null */
|
||||
private $castTo;
|
||||
|
||||
/** @var string|null */
|
||||
private $deprecated;
|
||||
/** @var callable[] */
|
||||
private array $transforms = [];
|
||||
private ?string $deprecated = null;
|
||||
|
||||
|
||||
public function default($value): self
|
||||
public function default(mixed $value): self
|
||||
{
|
||||
$this->default = $value;
|
||||
return $this;
|
||||
@@ -60,15 +54,30 @@ trait Base
|
||||
|
||||
public function castTo(string $type): self
|
||||
{
|
||||
$this->castTo = $type;
|
||||
return $this->transform(Helpers::getCastStrategy($type));
|
||||
}
|
||||
|
||||
|
||||
public function transform(callable $handler): self
|
||||
{
|
||||
$this->transforms[] = $handler;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function assert(callable $handler, ?string $description = null): self
|
||||
{
|
||||
$this->asserts[] = [$handler, $description];
|
||||
return $this;
|
||||
$expected = $description ?: (is_string($handler) ? "$handler()" : '#' . count($this->transforms));
|
||||
return $this->transform(function ($value, Context $context) use ($handler, $description, $expected) {
|
||||
if ($handler($value)) {
|
||||
return $value;
|
||||
}
|
||||
$context->addError(
|
||||
'Failed assertion ' . ($description ? "'%assertion%'" : '%assertion%') . ' for %label% %path% with value %value%.',
|
||||
Nette\Schema\Message::FailedAssertion,
|
||||
['value' => $value, 'assertion' => $expected],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -80,12 +89,12 @@ trait Base
|
||||
}
|
||||
|
||||
|
||||
public function completeDefault(Context $context)
|
||||
public function completeDefault(Context $context): mixed
|
||||
{
|
||||
if ($this->required) {
|
||||
$context->addError(
|
||||
'The mandatory item %path% is missing.',
|
||||
Nette\Schema\Message::MISSING_ITEM
|
||||
Nette\Schema\Message::MissingItem,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
@@ -94,7 +103,7 @@ trait Base
|
||||
}
|
||||
|
||||
|
||||
public function doNormalize($value, Context $context)
|
||||
public function doNormalize(mixed $value, Context $context): mixed
|
||||
{
|
||||
if ($this->before) {
|
||||
$value = ($this->before)($value);
|
||||
@@ -109,92 +118,46 @@ trait Base
|
||||
if ($this->deprecated !== null) {
|
||||
$context->addWarning(
|
||||
$this->deprecated,
|
||||
Nette\Schema\Message::DEPRECATED
|
||||
Nette\Schema\Message::Deprecated,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private function doValidate($value, string $expected, Context $context): bool
|
||||
private function doTransform(mixed $value, Context $context): mixed
|
||||
{
|
||||
if (!Nette\Utils\Validators::is($value, $expected)) {
|
||||
$expected = str_replace(['|', ':'], [' or ', ' in range '], $expected);
|
||||
$context->addError(
|
||||
'The %label% %path% expects to be %expected%, %value% given.',
|
||||
Nette\Schema\Message::TYPE_MISMATCH,
|
||||
['value' => $value, 'expected' => $expected]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private function doValidateRange($value, array $range, Context $context, string $types = ''): bool
|
||||
{
|
||||
if (is_array($value) || is_string($value)) {
|
||||
[$length, $label] = is_array($value)
|
||||
? [count($value), 'items']
|
||||
: (in_array('unicode', explode('|', $types), true)
|
||||
? [Nette\Utils\Strings::length($value), 'characters']
|
||||
: [strlen($value), 'bytes']);
|
||||
|
||||
if (!self::isInRange($length, $range)) {
|
||||
$context->addError(
|
||||
"The length of %label% %path% expects to be in range %expected%, %length% $label given.",
|
||||
Nette\Schema\Message::LENGTH_OUT_OF_RANGE,
|
||||
['value' => $value, 'length' => $length, 'expected' => implode('..', $range)]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
} elseif ((is_int($value) || is_float($value)) && !self::isInRange($value, $range)) {
|
||||
$context->addError(
|
||||
'The %label% %path% expects to be in range %expected%, %value% given.',
|
||||
Nette\Schema\Message::VALUE_OUT_OF_RANGE,
|
||||
['value' => $value, 'expected' => implode('..', $range)]
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private function isInRange($value, array $range): bool
|
||||
{
|
||||
return ($range[0] === null || $value >= $range[0])
|
||||
&& ($range[1] === null || $value <= $range[1]);
|
||||
}
|
||||
|
||||
|
||||
private function doFinalize($value, Context $context)
|
||||
{
|
||||
if ($this->castTo) {
|
||||
if (Nette\Utils\Reflection::isBuiltinType($this->castTo)) {
|
||||
settype($value, $this->castTo);
|
||||
} else {
|
||||
$object = new $this->castTo;
|
||||
foreach ($value as $k => $v) {
|
||||
$object->$k = $v;
|
||||
}
|
||||
|
||||
$value = $object;
|
||||
$isOk = $context->createChecker();
|
||||
foreach ($this->transforms as $handler) {
|
||||
$value = $handler($value, $context);
|
||||
if (!$isOk()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->asserts as $i => [$handler, $description]) {
|
||||
if (!$handler($value)) {
|
||||
$expected = $description ?: (is_string($handler) ? "$handler()" : "#$i");
|
||||
$context->addError(
|
||||
'Failed assertion ' . ($description ? "'%assertion%'" : '%assertion%') . ' for %label% %path% with value %value%.',
|
||||
Nette\Schema\Message::FAILED_ASSERTION,
|
||||
['value' => $value, 'assertion' => $expected]
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
/** @deprecated use Nette\Schema\Validators::validateType() */
|
||||
private function doValidate(mixed $value, string $expected, Context $context): bool
|
||||
{
|
||||
$isOk = $context->createChecker();
|
||||
Helpers::validateType($value, $expected, $context);
|
||||
return $isOk();
|
||||
}
|
||||
|
||||
|
||||
/** @deprecated use Nette\Schema\Validators::validateRange() */
|
||||
private static function doValidateRange(mixed $value, array $range, Context $context, string $types = ''): bool
|
||||
{
|
||||
$isOk = $context->createChecker();
|
||||
Helpers::validateRange($value, $range, $context, $types);
|
||||
return $isOk();
|
||||
}
|
||||
|
||||
|
||||
/** @deprecated use doTransform() */
|
||||
private function doFinalize(mixed $value, Context $context): mixed
|
||||
{
|
||||
return $this->doTransform($value, $context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,39 +13,37 @@ use Nette;
|
||||
use Nette\Schema\Context;
|
||||
use Nette\Schema\Helpers;
|
||||
use Nette\Schema\Schema;
|
||||
use function array_diff_key, array_fill_keys, array_key_exists, array_keys, array_map, array_merge, array_pop, array_values, is_array, is_object;
|
||||
|
||||
|
||||
final class Structure implements Schema
|
||||
{
|
||||
use Base;
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var Schema[] */
|
||||
private $items;
|
||||
private array $items;
|
||||
|
||||
/** @var Schema|null for array|list */
|
||||
private $otherItems;
|
||||
/** for array|list */
|
||||
private ?Schema $otherItems = null;
|
||||
|
||||
/** @var array{?int, ?int} */
|
||||
private $range = [null, null];
|
||||
|
||||
/** @var bool */
|
||||
private $skipDefaults = false;
|
||||
private array $range = [null, null];
|
||||
private bool $skipDefaults = false;
|
||||
|
||||
|
||||
/**
|
||||
* @param Schema[] $items
|
||||
* @param Schema[] $shape
|
||||
*/
|
||||
public function __construct(array $items)
|
||||
public function __construct(array $shape)
|
||||
{
|
||||
(function (Schema ...$items) {})(...array_values($items));
|
||||
$this->items = $items;
|
||||
$this->castTo = 'object';
|
||||
(function (Schema ...$items) {})(...array_values($shape));
|
||||
$this->items = $shape;
|
||||
$this->castTo('object');
|
||||
$this->required = true;
|
||||
}
|
||||
|
||||
|
||||
public function default($value): self
|
||||
public function default(mixed $value): self
|
||||
{
|
||||
throw new Nette\InvalidStateException('Structure cannot have default value.');
|
||||
}
|
||||
@@ -65,10 +63,7 @@ final class Structure implements Schema
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string|Schema $type
|
||||
*/
|
||||
public function otherItems($type = 'mixed'): self
|
||||
public function otherItems(string|Schema $type = 'mixed'): self
|
||||
{
|
||||
$this->otherItems = $type instanceof Schema ? $type : new Type($type);
|
||||
return $this;
|
||||
@@ -82,13 +77,26 @@ final class Structure implements Schema
|
||||
}
|
||||
|
||||
|
||||
public function extend(array|self $shape): self
|
||||
{
|
||||
$shape = $shape instanceof self ? $shape->items : $shape;
|
||||
return new self(array_merge($this->items, $shape));
|
||||
}
|
||||
|
||||
|
||||
public function getShape(): array
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
|
||||
/********************* processing ****************d*g**/
|
||||
|
||||
|
||||
public function normalize($value, Context $context)
|
||||
public function normalize(mixed $value, Context $context): mixed
|
||||
{
|
||||
if ($prevent = (is_array($value) && isset($value[Helpers::PREVENT_MERGING]))) {
|
||||
unset($value[Helpers::PREVENT_MERGING]);
|
||||
if ($prevent = (is_array($value) && isset($value[Helpers::PreventMerging]))) {
|
||||
unset($value[Helpers::PreventMerging]);
|
||||
}
|
||||
|
||||
$value = $this->doNormalize($value, $context);
|
||||
@@ -107,7 +115,7 @@ final class Structure implements Schema
|
||||
}
|
||||
|
||||
if ($prevent) {
|
||||
$value[Helpers::PREVENT_MERGING] = true;
|
||||
$value[Helpers::PreventMerging] = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,37 +123,34 @@ final class Structure implements Schema
|
||||
}
|
||||
|
||||
|
||||
public function merge($value, $base)
|
||||
public function merge(mixed $value, mixed $base): mixed
|
||||
{
|
||||
if (is_array($value) && isset($value[Helpers::PREVENT_MERGING])) {
|
||||
unset($value[Helpers::PREVENT_MERGING]);
|
||||
if (is_array($value) && isset($value[Helpers::PreventMerging])) {
|
||||
unset($value[Helpers::PreventMerging]);
|
||||
$base = null;
|
||||
}
|
||||
|
||||
if (is_array($value) && is_array($base)) {
|
||||
$index = 0;
|
||||
$index = $this->otherItems === null ? null : 0;
|
||||
foreach ($value as $key => $val) {
|
||||
if ($key === $index) {
|
||||
$base[] = $val;
|
||||
$index++;
|
||||
} elseif (array_key_exists($key, $base)) {
|
||||
$itemSchema = $this->items[$key] ?? $this->otherItems;
|
||||
$base[$key] = $itemSchema
|
||||
? $itemSchema->merge($val, $base[$key])
|
||||
: Helpers::merge($val, $base[$key]);
|
||||
} else {
|
||||
$base[$key] = $val;
|
||||
$base[$key] = array_key_exists($key, $base) && ($itemSchema = $this->items[$key] ?? $this->otherItems)
|
||||
? $itemSchema->merge($val, $base[$key])
|
||||
: $val;
|
||||
}
|
||||
}
|
||||
|
||||
return $base;
|
||||
}
|
||||
|
||||
return Helpers::merge($value, $base);
|
||||
return $value ?? $base;
|
||||
}
|
||||
|
||||
|
||||
public function complete($value, Context $context)
|
||||
public function complete(mixed $value, Context $context): mixed
|
||||
{
|
||||
if ($value === null) {
|
||||
$value = []; // is unable to distinguish null from array in NEON
|
||||
@@ -153,13 +158,17 @@ final class Structure implements Schema
|
||||
|
||||
$this->doDeprecation($context);
|
||||
|
||||
if (!$this->doValidate($value, 'array', $context)
|
||||
|| !$this->doValidateRange($value, $this->range, $context)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
$isOk = $context->createChecker();
|
||||
Helpers::validateType($value, 'array', $context);
|
||||
$isOk() && Helpers::validateRange($value, $this->range, $context);
|
||||
$isOk() && $this->validateItems($value, $context);
|
||||
$isOk() && $value = $this->doTransform($value, $context);
|
||||
return $isOk() ? $value : null;
|
||||
}
|
||||
|
||||
$errCount = count($context->errors);
|
||||
|
||||
private function validateItems(array &$value, Context $context): void
|
||||
{
|
||||
$items = $this->items;
|
||||
if ($extraKeys = array_keys(array_diff_key($value, $items))) {
|
||||
if ($this->otherItems) {
|
||||
@@ -167,11 +176,11 @@ final class Structure implements Schema
|
||||
} else {
|
||||
$keys = array_map('strval', array_keys($items));
|
||||
foreach ($extraKeys as $key) {
|
||||
$hint = Nette\Utils\ObjectHelpers::getSuggestion($keys, (string) $key);
|
||||
$hint = Nette\Utils\Helpers::getSuggestion($keys, (string) $key);
|
||||
$context->addError(
|
||||
'Unexpected item %path%' . ($hint ? ", did you mean '%hint%'?" : '.'),
|
||||
Nette\Schema\Message::UNEXPECTED_ITEM,
|
||||
['hint' => $hint]
|
||||
Nette\Schema\Message::UnexpectedItem,
|
||||
['hint' => $hint],
|
||||
)->path[] = $key;
|
||||
}
|
||||
}
|
||||
@@ -190,16 +199,10 @@ final class Structure implements Schema
|
||||
|
||||
array_pop($context->path);
|
||||
}
|
||||
|
||||
if (count($context->errors) > $errCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
return $this->doFinalize($value, $context);
|
||||
}
|
||||
|
||||
|
||||
public function completeDefault(Context $context)
|
||||
public function completeDefault(Context $context): mixed
|
||||
{
|
||||
return $this->required
|
||||
? $this->complete([], $context)
|
||||
|
||||
113
vendor/nette/schema/src/Schema/Elements/Type.php
vendored
113
vendor/nette/schema/src/Schema/Elements/Type.php
vendored
@@ -9,35 +9,25 @@ declare(strict_types=1);
|
||||
|
||||
namespace Nette\Schema\Elements;
|
||||
|
||||
use Nette;
|
||||
use Nette\Schema\Context;
|
||||
use Nette\Schema\DynamicParameter;
|
||||
use Nette\Schema\Helpers;
|
||||
use Nette\Schema\Schema;
|
||||
use function array_key_exists, array_pop, implode, is_array, str_replace, strpos;
|
||||
|
||||
|
||||
final class Type implements Schema
|
||||
{
|
||||
use Base;
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var string */
|
||||
private $type;
|
||||
|
||||
/** @var Schema|null for arrays */
|
||||
private $itemsValue;
|
||||
|
||||
/** @var Schema|null for arrays */
|
||||
private $itemsKey;
|
||||
private string $type;
|
||||
private ?Schema $itemsValue = null;
|
||||
private ?Schema $itemsKey = null;
|
||||
|
||||
/** @var array{?float, ?float} */
|
||||
private $range = [null, null];
|
||||
|
||||
/** @var string|null */
|
||||
private $pattern;
|
||||
|
||||
/** @var bool */
|
||||
private $merge = true;
|
||||
private array $range = [null, null];
|
||||
private ?string $pattern = null;
|
||||
private bool $merge = true;
|
||||
|
||||
|
||||
public function __construct(string $type)
|
||||
@@ -84,11 +74,9 @@ final class Type implements Schema
|
||||
|
||||
|
||||
/**
|
||||
* @param string|Schema $valueType
|
||||
* @param string|Schema|null $keyType
|
||||
* @internal use arrayOf() or listOf()
|
||||
*/
|
||||
public function items($valueType = 'mixed', $keyType = null): self
|
||||
public function items(string|Schema $valueType = 'mixed', string|Schema|null $keyType = null): self
|
||||
{
|
||||
$this->itemsValue = $valueType instanceof Schema
|
||||
? $valueType
|
||||
@@ -110,10 +98,10 @@ final class Type implements Schema
|
||||
/********************* processing ****************d*g**/
|
||||
|
||||
|
||||
public function normalize($value, Context $context)
|
||||
public function normalize(mixed $value, Context $context): mixed
|
||||
{
|
||||
if ($prevent = (is_array($value) && isset($value[Helpers::PREVENT_MERGING]))) {
|
||||
unset($value[Helpers::PREVENT_MERGING]);
|
||||
if ($prevent = (is_array($value) && isset($value[Helpers::PreventMerging]))) {
|
||||
unset($value[Helpers::PreventMerging]);
|
||||
}
|
||||
|
||||
$value = $this->doNormalize($value, $context);
|
||||
@@ -134,17 +122,17 @@ final class Type implements Schema
|
||||
}
|
||||
|
||||
if ($prevent && is_array($value)) {
|
||||
$value[Helpers::PREVENT_MERGING] = true;
|
||||
$value[Helpers::PreventMerging] = true;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
public function merge($value, $base)
|
||||
public function merge(mixed $value, mixed $base): mixed
|
||||
{
|
||||
if (is_array($value) && isset($value[Helpers::PREVENT_MERGING])) {
|
||||
unset($value[Helpers::PREVENT_MERGING]);
|
||||
if (is_array($value) && isset($value[Helpers::PreventMerging])) {
|
||||
unset($value[Helpers::PreventMerging]);
|
||||
return $value;
|
||||
}
|
||||
|
||||
@@ -168,11 +156,11 @@ final class Type implements Schema
|
||||
}
|
||||
|
||||
|
||||
public function complete($value, Context $context)
|
||||
public function complete(mixed $value, Context $context): mixed
|
||||
{
|
||||
$merge = $this->merge;
|
||||
if (is_array($value) && isset($value[Helpers::PREVENT_MERGING])) {
|
||||
unset($value[Helpers::PREVENT_MERGING]);
|
||||
if (is_array($value) && isset($value[Helpers::PreventMerging])) {
|
||||
unset($value[Helpers::PreventMerging]);
|
||||
$merge = false;
|
||||
}
|
||||
|
||||
@@ -182,49 +170,40 @@ final class Type implements Schema
|
||||
|
||||
$this->doDeprecation($context);
|
||||
|
||||
if (!$this->doValidate($value, $this->type, $context)
|
||||
|| !$this->doValidateRange($value, $this->range, $context, $this->type)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($value !== null && $this->pattern !== null && !preg_match("\x01^(?:$this->pattern)$\x01Du", $value)) {
|
||||
$context->addError(
|
||||
"The %label% %path% expects to match pattern '%pattern%', %value% given.",
|
||||
Nette\Schema\Message::PATTERN_MISMATCH,
|
||||
['value' => $value, 'pattern' => $this->pattern]
|
||||
);
|
||||
return;
|
||||
$isOk = $context->createChecker();
|
||||
Helpers::validateType($value, $this->type, $context);
|
||||
$isOk() && Helpers::validateRange($value, $this->range, $context, $this->type);
|
||||
$isOk() && $value !== null && $this->pattern !== null && Helpers::validatePattern($value, $this->pattern, $context);
|
||||
$isOk() && is_array($value) && $this->validateItems($value, $context);
|
||||
$isOk() && $merge && $value = Helpers::merge($value, $this->default);
|
||||
$isOk() && $value = $this->doTransform($value, $context);
|
||||
if (!$isOk()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($value instanceof DynamicParameter) {
|
||||
$expected = $this->type . ($this->range === [null, null] ? '' : ':' . implode('..', $this->range));
|
||||
$context->dynamics[] = [$value, str_replace(DynamicParameter::class . '|', '', $expected)];
|
||||
$context->dynamics[] = [$value, str_replace(DynamicParameter::class . '|', '', $expected), $context->path];
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
private function validateItems(array &$value, Context $context): void
|
||||
{
|
||||
if (!$this->itemsValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->itemsValue) {
|
||||
$errCount = count($context->errors);
|
||||
$res = [];
|
||||
foreach ($value as $key => $val) {
|
||||
$context->path[] = $key;
|
||||
$context->isKey = true;
|
||||
$key = $this->itemsKey ? $this->itemsKey->complete($key, $context) : $key;
|
||||
$context->isKey = false;
|
||||
$res[$key] = $this->itemsValue->complete($val, $context);
|
||||
array_pop($context->path);
|
||||
}
|
||||
|
||||
if (count($context->errors) > $errCount) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$value = $res;
|
||||
$res = [];
|
||||
foreach ($value as $key => $val) {
|
||||
$context->path[] = $key;
|
||||
$context->isKey = true;
|
||||
$key = $this->itemsKey ? $this->itemsKey->complete($key, $context) : $key;
|
||||
$context->isKey = false;
|
||||
$res[$key ?? ''] = $this->itemsValue->complete($val, $context);
|
||||
array_pop($context->path);
|
||||
}
|
||||
|
||||
if ($merge) {
|
||||
$value = Helpers::merge($value, $this->default);
|
||||
}
|
||||
|
||||
return $this->doFinalize($value, $context);
|
||||
$value = $res;
|
||||
}
|
||||
}
|
||||
|
||||
54
vendor/nette/schema/src/Schema/Expect.php
vendored
54
vendor/nette/schema/src/Schema/Expect.php
vendored
@@ -13,6 +13,7 @@ use Nette;
|
||||
use Nette\Schema\Elements\AnyOf;
|
||||
use Nette\Schema\Elements\Structure;
|
||||
use Nette\Schema\Elements\Type;
|
||||
use function is_object;
|
||||
|
||||
|
||||
/**
|
||||
@@ -24,7 +25,6 @@ use Nette\Schema\Elements\Type;
|
||||
* @method static Type float($default = null)
|
||||
* @method static Type bool($default = null)
|
||||
* @method static Type null()
|
||||
* @method static Type array($default = [])
|
||||
* @method static Type list($default = [])
|
||||
* @method static Type mixed($default = null)
|
||||
* @method static Type email($default = null)
|
||||
@@ -32,8 +32,6 @@ use Nette\Schema\Elements\Type;
|
||||
*/
|
||||
final class Expect
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
public static function __callStatic(string $name, array $args): Type
|
||||
{
|
||||
$type = new Type($name);
|
||||
@@ -51,39 +49,35 @@ final class Expect
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param mixed|Schema ...$set
|
||||
*/
|
||||
public static function anyOf(...$set): AnyOf
|
||||
public static function anyOf(mixed ...$set): AnyOf
|
||||
{
|
||||
return new AnyOf(...$set);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param Schema[] $items
|
||||
* @param Schema[] $shape
|
||||
*/
|
||||
public static function structure(array $items): Structure
|
||||
public static function structure(array $shape): Structure
|
||||
{
|
||||
return new Structure($items);
|
||||
return new Structure($shape);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param object $object
|
||||
*/
|
||||
public static function from($object, array $items = []): Structure
|
||||
public static function from(object $object, array $items = []): Structure
|
||||
{
|
||||
$ro = new \ReflectionObject($object);
|
||||
foreach ($ro->getProperties() as $prop) {
|
||||
$type = Helpers::getPropertyType($prop) ?? 'mixed';
|
||||
$props = $ro->hasMethod('__construct')
|
||||
? $ro->getMethod('__construct')->getParameters()
|
||||
: $ro->getProperties();
|
||||
|
||||
foreach ($props as $prop) {
|
||||
$item = &$items[$prop->getName()];
|
||||
if (!$item) {
|
||||
$type = Helpers::getPropertyType($prop) ?? 'mixed';
|
||||
$item = new Type($type);
|
||||
if (PHP_VERSION_ID >= 70400 && !$prop->isInitialized($object)) {
|
||||
$item->required();
|
||||
} else {
|
||||
$def = $prop->getValue($object);
|
||||
if ($prop instanceof \ReflectionProperty ? $prop->isInitialized($object) : $prop->isOptional()) {
|
||||
$def = ($prop instanceof \ReflectionProperty ? $prop->getValue($object) : $prop->getDefaultValue());
|
||||
if (is_object($def)) {
|
||||
$item = static::from($def);
|
||||
} elseif ($def === null && !Nette\Utils\Validators::is(null, $type)) {
|
||||
@@ -91,6 +85,8 @@ final class Expect
|
||||
} else {
|
||||
$item->default($def);
|
||||
}
|
||||
} else {
|
||||
$item->required();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,19 +96,23 @@ final class Expect
|
||||
|
||||
|
||||
/**
|
||||
* @param string|Schema $valueType
|
||||
* @param string|Schema|null $keyType
|
||||
* @param mixed[] $shape
|
||||
*/
|
||||
public static function arrayOf($valueType, $keyType = null): Type
|
||||
public static function array(?array $shape = []): Structure|Type
|
||||
{
|
||||
return Nette\Utils\Arrays::first($shape ?? []) instanceof Schema
|
||||
? (new Structure($shape))->castTo('array')
|
||||
: (new Type('array'))->default($shape);
|
||||
}
|
||||
|
||||
|
||||
public static function arrayOf(string|Schema $valueType, string|Schema|null $keyType = null): Type
|
||||
{
|
||||
return (new Type('array'))->items($valueType, $keyType);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string|Schema $type
|
||||
*/
|
||||
public static function listOf($type): Type
|
||||
public static function listOf(string|Schema $type): Type
|
||||
{
|
||||
return (new Type('list'))->items($type);
|
||||
}
|
||||
|
||||
116
vendor/nette/schema/src/Schema/Helpers.php
vendored
116
vendor/nette/schema/src/Schema/Helpers.php
vendored
@@ -11,6 +11,7 @@ namespace Nette\Schema;
|
||||
|
||||
use Nette;
|
||||
use Nette\Utils\Reflection;
|
||||
use function count, explode, get_debug_type, implode, in_array, is_array, is_float, is_int, is_object, is_scalar, is_string, method_exists, preg_match, preg_quote, preg_replace, preg_replace_callback, settype, str_replace, strlen, trim, var_export;
|
||||
|
||||
|
||||
/**
|
||||
@@ -20,17 +21,16 @@ final class Helpers
|
||||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
public const PREVENT_MERGING = '_prevent_merging';
|
||||
public const PreventMerging = '_prevent_merging';
|
||||
|
||||
|
||||
/**
|
||||
* Merges dataset. Left has higher priority than right one.
|
||||
* @return array|string
|
||||
*/
|
||||
public static function merge($value, $base)
|
||||
public static function merge(mixed $value, mixed $base): mixed
|
||||
{
|
||||
if (is_array($value) && isset($value[self::PREVENT_MERGING])) {
|
||||
unset($value[self::PREVENT_MERGING]);
|
||||
if (is_array($value) && isset($value[self::PreventMerging])) {
|
||||
unset($value[self::PreventMerging]);
|
||||
return $value;
|
||||
}
|
||||
|
||||
@@ -56,17 +56,16 @@ final class Helpers
|
||||
}
|
||||
|
||||
|
||||
public static function getPropertyType(\ReflectionProperty $prop): ?string
|
||||
public static function getPropertyType(\ReflectionProperty|\ReflectionParameter $prop): ?string
|
||||
{
|
||||
if (!class_exists(Nette\Utils\Type::class)) {
|
||||
throw new Nette\NotSupportedException('Expect::from() requires nette/utils 3.x');
|
||||
} elseif ($type = Nette\Utils\Type::fromReflection($prop)) {
|
||||
if ($type = Nette\Utils\Type::fromReflection($prop)) {
|
||||
return (string) $type;
|
||||
} elseif ($type = preg_replace('#\s.*#', '', (string) self::parseAnnotation($prop, 'var'))) {
|
||||
} elseif (
|
||||
($prop instanceof \ReflectionProperty)
|
||||
&& ($type = preg_replace('#\s.*#', '', (string) self::parseAnnotation($prop, 'var')))
|
||||
) {
|
||||
$class = Reflection::getPropertyDeclaringClass($prop);
|
||||
return preg_replace_callback('#[\w\\\\]+#', function ($m) use ($class) {
|
||||
return Reflection::expandClassName($m[0], $class);
|
||||
}, $type);
|
||||
return preg_replace_callback('#[\w\\\]+#', fn($m) => Reflection::expandClassName($m[0], $class), $type);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -92,19 +91,94 @@ final class Helpers
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function formatValue($value): string
|
||||
public static function formatValue(mixed $value): string
|
||||
{
|
||||
if (is_object($value)) {
|
||||
return 'object ' . get_class($value);
|
||||
if ($value instanceof DynamicParameter) {
|
||||
return 'dynamic';
|
||||
} elseif (is_object($value)) {
|
||||
return 'object ' . $value::class;
|
||||
} elseif (is_string($value)) {
|
||||
return "'" . Nette\Utils\Strings::truncate($value, 15, '...') . "'";
|
||||
} elseif (is_scalar($value)) {
|
||||
return var_export($value, true);
|
||||
return var_export($value, return: true);
|
||||
} else {
|
||||
return strtolower(gettype($value));
|
||||
return get_debug_type($value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function validateType(mixed $value, string $expected, Context $context): void
|
||||
{
|
||||
if (!Nette\Utils\Validators::is($value, $expected)) {
|
||||
$expected = str_replace(DynamicParameter::class . '|', '', $expected);
|
||||
$expected = str_replace(['|', ':'], [' or ', ' in range '], $expected);
|
||||
$context->addError(
|
||||
'The %label% %path% expects to be %expected%, %value% given.',
|
||||
Message::TypeMismatch,
|
||||
['value' => $value, 'expected' => $expected],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function validateRange(mixed $value, array $range, Context $context, string $types = ''): void
|
||||
{
|
||||
if (is_array($value) || is_string($value)) {
|
||||
[$length, $label] = is_array($value)
|
||||
? [count($value), 'items']
|
||||
: (in_array('unicode', explode('|', $types), true)
|
||||
? [Nette\Utils\Strings::length($value), 'characters']
|
||||
: [strlen($value), 'bytes']);
|
||||
|
||||
if (!self::isInRange($length, $range)) {
|
||||
$context->addError(
|
||||
"The length of %label% %path% expects to be in range %expected%, %length% $label given.",
|
||||
Message::LengthOutOfRange,
|
||||
['value' => $value, 'length' => $length, 'expected' => implode('..', $range)],
|
||||
);
|
||||
}
|
||||
} elseif ((is_int($value) || is_float($value)) && !self::isInRange($value, $range)) {
|
||||
$context->addError(
|
||||
'The %label% %path% expects to be in range %expected%, %value% given.',
|
||||
Message::ValueOutOfRange,
|
||||
['value' => $value, 'expected' => implode('..', $range)],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function isInRange(mixed $value, array $range): bool
|
||||
{
|
||||
return ($range[0] === null || $value >= $range[0])
|
||||
&& ($range[1] === null || $value <= $range[1]);
|
||||
}
|
||||
|
||||
|
||||
public static function validatePattern(string $value, string $pattern, Context $context): void
|
||||
{
|
||||
if (!preg_match("\x01^(?:$pattern)$\x01Du", $value)) {
|
||||
$context->addError(
|
||||
"The %label% %path% expects to match pattern '%pattern%', %value% given.",
|
||||
Message::PatternMismatch,
|
||||
['value' => $value, 'pattern' => $pattern],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function getCastStrategy(string $type): \Closure
|
||||
{
|
||||
if (Nette\Utils\Validators::isBuiltinType($type)) {
|
||||
return static function ($value) use ($type) {
|
||||
settype($value, $type);
|
||||
return $value;
|
||||
};
|
||||
} elseif (method_exists($type, '__construct')) {
|
||||
return static fn($value) => is_array($value) || $value instanceof \stdClass
|
||||
? new $type(...(array) $value)
|
||||
: new $type($value);
|
||||
} else {
|
||||
return static fn($value) => Nette\Utils\Arrays::toObject((array) $value, new $type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
56
vendor/nette/schema/src/Schema/Message.php
vendored
56
vendor/nette/schema/src/Schema/Message.php
vendored
@@ -10,47 +10,67 @@ declare(strict_types=1);
|
||||
namespace Nette\Schema;
|
||||
|
||||
use Nette;
|
||||
use function implode, preg_last_error_msg, preg_replace_callback;
|
||||
|
||||
|
||||
final class Message
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
/** variables: {value: mixed, expected: string} */
|
||||
public const TypeMismatch = 'schema.typeMismatch';
|
||||
|
||||
/** variables: {value: mixed, expected: string} */
|
||||
public const TYPE_MISMATCH = 'schema.typeMismatch';
|
||||
|
||||
/** variables: {value: mixed, expected: string} */
|
||||
public const VALUE_OUT_OF_RANGE = 'schema.valueOutOfRange';
|
||||
public const ValueOutOfRange = 'schema.valueOutOfRange';
|
||||
|
||||
/** variables: {value: mixed, length: int, expected: string} */
|
||||
public const LENGTH_OUT_OF_RANGE = 'schema.lengthOutOfRange';
|
||||
public const LengthOutOfRange = 'schema.lengthOutOfRange';
|
||||
|
||||
/** variables: {value: string, pattern: string} */
|
||||
public const PATTERN_MISMATCH = 'schema.patternMismatch';
|
||||
public const PatternMismatch = 'schema.patternMismatch';
|
||||
|
||||
/** variables: {value: mixed, assertion: string} */
|
||||
public const FAILED_ASSERTION = 'schema.failedAssertion';
|
||||
public const FailedAssertion = 'schema.failedAssertion';
|
||||
|
||||
/** no variables */
|
||||
public const MISSING_ITEM = 'schema.missingItem';
|
||||
public const MissingItem = 'schema.missingItem';
|
||||
|
||||
/** variables: {hint: string} */
|
||||
public const UNEXPECTED_ITEM = 'schema.unexpectedItem';
|
||||
public const UnexpectedItem = 'schema.unexpectedItem';
|
||||
|
||||
/** no variables */
|
||||
public const DEPRECATED = 'schema.deprecated';
|
||||
public const Deprecated = 'schema.deprecated';
|
||||
|
||||
/** @var string */
|
||||
public $message;
|
||||
/** @deprecated use Message::TypeMismatch */
|
||||
public const TYPE_MISMATCH = self::TypeMismatch;
|
||||
|
||||
/** @var string */
|
||||
public $code;
|
||||
/** @deprecated use Message::ValueOutOfRange */
|
||||
public const VALUE_OUT_OF_RANGE = self::ValueOutOfRange;
|
||||
|
||||
/** @deprecated use Message::LengthOutOfRange */
|
||||
public const LENGTH_OUT_OF_RANGE = self::LengthOutOfRange;
|
||||
|
||||
/** @deprecated use Message::PatternMismatch */
|
||||
public const PATTERN_MISMATCH = self::PatternMismatch;
|
||||
|
||||
/** @deprecated use Message::FailedAssertion */
|
||||
public const FAILED_ASSERTION = self::FailedAssertion;
|
||||
|
||||
/** @deprecated use Message::MissingItem */
|
||||
public const MISSING_ITEM = self::MissingItem;
|
||||
|
||||
/** @deprecated use Message::UnexpectedItem */
|
||||
public const UNEXPECTED_ITEM = self::UnexpectedItem;
|
||||
|
||||
/** @deprecated use Message::Deprecated */
|
||||
public const DEPRECATED = self::Deprecated;
|
||||
|
||||
public string $message;
|
||||
public string $code;
|
||||
|
||||
/** @var string[] */
|
||||
public $path;
|
||||
public array $path;
|
||||
|
||||
/** @var string[] */
|
||||
public $variables;
|
||||
public array $variables;
|
||||
|
||||
|
||||
public function __construct(string $message, string $code, array $path, array $variables = [])
|
||||
@@ -74,6 +94,6 @@ final class Message
|
||||
return preg_replace_callback('~( ?)%(\w+)%~', function ($m) use ($vars) {
|
||||
[, $space, $key] = $m;
|
||||
return $vars[$key] === null ? '' : $space . $vars[$key];
|
||||
}, $this->message);
|
||||
}, $this->message) ?? throw new Nette\InvalidStateException(preg_last_error_msg());
|
||||
}
|
||||
}
|
||||
|
||||
25
vendor/nette/schema/src/Schema/Processor.php
vendored
25
vendor/nette/schema/src/Schema/Processor.php
vendored
@@ -17,19 +17,12 @@ use Nette;
|
||||
*/
|
||||
final class Processor
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var array */
|
||||
public $onNewContext = [];
|
||||
|
||||
/** @var Context|null */
|
||||
private $context;
|
||||
|
||||
/** @var bool */
|
||||
private $skipDefaults;
|
||||
public array $onNewContext = [];
|
||||
private Context $context;
|
||||
private bool $skipDefaults = false;
|
||||
|
||||
|
||||
public function skipDefaults(bool $value = true)
|
||||
public function skipDefaults(bool $value = true): void
|
||||
{
|
||||
$this->skipDefaults = $value;
|
||||
}
|
||||
@@ -37,10 +30,9 @@ final class Processor
|
||||
|
||||
/**
|
||||
* Normalizes and validates data. Result is a clean completed data.
|
||||
* @return mixed
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function process(Schema $schema, $data)
|
||||
public function process(Schema $schema, mixed $data): mixed
|
||||
{
|
||||
$this->createContext();
|
||||
$data = $schema->normalize($data, $this->context);
|
||||
@@ -53,10 +45,9 @@ final class Processor
|
||||
|
||||
/**
|
||||
* Normalizes and validates and merges multiple data. Result is a clean completed data.
|
||||
* @return mixed
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function processMultiple(Schema $schema, array $dataset)
|
||||
public function processMultiple(Schema $schema, array $dataset): mixed
|
||||
{
|
||||
$this->createContext();
|
||||
$flatten = null;
|
||||
@@ -96,10 +87,10 @@ final class Processor
|
||||
}
|
||||
|
||||
|
||||
private function createContext()
|
||||
private function createContext(): void
|
||||
{
|
||||
$this->context = new Context;
|
||||
$this->context->skipDefaults = $this->skipDefaults;
|
||||
$this->onNewContext($this->context);
|
||||
Nette\Utils\Arrays::invoke($this->onNewContext, $this->context);
|
||||
}
|
||||
}
|
||||
|
||||
6
vendor/nette/schema/src/Schema/Schema.php
vendored
6
vendor/nette/schema/src/Schema/Schema.php
vendored
@@ -16,19 +16,19 @@ interface Schema
|
||||
* Normalization.
|
||||
* @return mixed
|
||||
*/
|
||||
function normalize($value, Context $context);
|
||||
function normalize(mixed $value, Context $context);
|
||||
|
||||
/**
|
||||
* Merging.
|
||||
* @return mixed
|
||||
*/
|
||||
function merge($value, $base);
|
||||
function merge(mixed $value, mixed $base);
|
||||
|
||||
/**
|
||||
* Validation and finalization.
|
||||
* @return mixed
|
||||
*/
|
||||
function complete($value, Context $context);
|
||||
function complete(mixed $value, Context $context);
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
|
||||
@@ -18,7 +18,7 @@ use Nette;
|
||||
class ValidationException extends Nette\InvalidStateException
|
||||
{
|
||||
/** @var Message[] */
|
||||
private $messages;
|
||||
private array $messages;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
10
vendor/nette/utils/.phpstorm.meta.php
vendored
10
vendor/nette/utils/.phpstorm.meta.php
vendored
@@ -9,11 +9,5 @@ override(\Nette\Utils\Arrays::getRef(0), elementType(0));
|
||||
override(\Nette\Utils\Arrays::grep(0), type(0));
|
||||
override(\Nette\Utils\Arrays::toObject(0), type(1));
|
||||
|
||||
expectedArguments(\Nette\Utils\Arrays::grep(), 2, PREG_GREP_INVERT);
|
||||
expectedArguments(\Nette\Utils\Image::resize(), 2, \Nette\Utils\Image::SHRINK_ONLY, \Nette\Utils\Image::STRETCH, \Nette\Utils\Image::FIT, \Nette\Utils\Image::FILL, \Nette\Utils\Image::EXACT);
|
||||
expectedArguments(\Nette\Utils\Image::calculateSize(), 4, \Nette\Utils\Image::SHRINK_ONLY, \Nette\Utils\Image::STRETCH, \Nette\Utils\Image::FIT, \Nette\Utils\Image::FILL, \Nette\Utils\Image::EXACT);
|
||||
expectedArguments(\Nette\Utils\Json::encode(), 1, \Nette\Utils\Json::PRETTY);
|
||||
expectedArguments(\Nette\Utils\Json::decode(), 1, \Nette\Utils\Json::FORCE_ARRAY);
|
||||
expectedArguments(\Nette\Utils\Strings::split(), 2, \PREG_SPLIT_NO_EMPTY | \PREG_OFFSET_CAPTURE);
|
||||
expectedArguments(\Nette\Utils\Strings::match(), 2, \PREG_OFFSET_CAPTURE | \PREG_UNMATCHED_AS_NULL);
|
||||
expectedArguments(\Nette\Utils\Strings::matchAll(), 2, \PREG_OFFSET_CAPTURE | \PREG_UNMATCHED_AS_NULL | \PREG_PATTERN_ORDER);
|
||||
expectedArguments(\Nette\Utils\Image::resize(), 2, \Nette\Utils\Image::ShrinkOnly, \Nette\Utils\Image::Stretch, \Nette\Utils\Image::OrSmaller, \Nette\Utils\Image::OrBigger, \Nette\Utils\Image::Cover);
|
||||
expectedArguments(\Nette\Utils\Image::calculateSize(), 4, \Nette\Utils\Image::ShrinkOnly, \Nette\Utils\Image::Stretch, \Nette\Utils\Image::OrSmaller, \Nette\Utils\Image::OrBigger, \Nette\Utils\Image::Cover);
|
||||
|
||||
20
vendor/nette/utils/composer.json
vendored
20
vendor/nette/utils/composer.json
vendored
@@ -15,27 +15,31 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.2 <8.3"
|
||||
"php": "8.2 - 8.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "~2.0",
|
||||
"tracy/tracy": "^2.3",
|
||||
"phpstan/phpstan": "^1.0"
|
||||
"nette/tester": "^2.5",
|
||||
"tracy/tracy": "^2.9",
|
||||
"phpstan/phpstan-nette": "^2.0@stable",
|
||||
"jetbrains/phpstorm-attributes": "^1.2"
|
||||
},
|
||||
"conflict": {
|
||||
"nette/di": "<3.0.6"
|
||||
"nette/finder": "<3",
|
||||
"nette/schema": "<1.2.2"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-iconv": "to use Strings::webalize(), toAscii(), chr() and reverse()",
|
||||
"ext-json": "to use Nette\\Utils\\Json",
|
||||
"ext-intl": "to use Strings::webalize(), toAscii(), normalize() and compare()",
|
||||
"ext-mbstring": "to use Strings::lower() etc...",
|
||||
"ext-xml": "to use Strings::length() etc. when mbstring is not available",
|
||||
"ext-gd": "to use Image",
|
||||
"ext-tokenizer": "to use Nette\\Utils\\Reflection::getUseStatements()"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": ["src/"]
|
||||
"classmap": ["src/"],
|
||||
"psr-4": {
|
||||
"Nette\\": "src"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"scripts": {
|
||||
@@ -44,7 +48,7 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.2-dev"
|
||||
"dev-master": "4.1-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
33
vendor/nette/utils/contributing.md
vendored
33
vendor/nette/utils/contributing.md
vendored
@@ -1,33 +0,0 @@
|
||||
How to contribute & use the issue tracker
|
||||
=========================================
|
||||
|
||||
Nette welcomes your contributions. There are several ways to help out:
|
||||
|
||||
* Create an issue on GitHub, if you have found a bug
|
||||
* Write test cases for open bug issues
|
||||
* Write fixes for open bug/feature issues, preferably with test cases included
|
||||
* Contribute to the [documentation](https://nette.org/en/writing)
|
||||
|
||||
Issues
|
||||
------
|
||||
|
||||
Please **do not use the issue tracker to ask questions**. We will be happy to help you
|
||||
on [Nette forum](https://forum.nette.org) or chat with us on [Gitter](https://gitter.im/nette/nette).
|
||||
|
||||
A good bug report shouldn't leave others needing to chase you up for more
|
||||
information. Please try to be as detailed as possible in your report.
|
||||
|
||||
**Feature requests** are welcome. But take a moment to find out whether your idea
|
||||
fits with the scope and aims of the project. It's up to *you* to make a strong
|
||||
case to convince the project's developers of the merits of this feature.
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
If you'd like to contribute, please take a moment to read [the contributing guide](https://nette.org/en/contributing).
|
||||
|
||||
The best way to propose a feature is to discuss your ideas on [Nette forum](https://forum.nette.org) before implementing them.
|
||||
|
||||
Please do not fix whitespace, format code, or make a purely cosmetic patch.
|
||||
|
||||
Thanks! :heart:
|
||||
14
vendor/nette/utils/ncs.php
vendored
14
vendor/nette/utils/ncs.php
vendored
@@ -1,14 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Rules for Nette Coding Standard
|
||||
* https://github.com/nette/coding-standard
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
// use function in Arrays.php, Callback.php, Html.php, Strings.php
|
||||
'single_import_per_statement' => false,
|
||||
'ordered_imports' => false,
|
||||
];
|
||||
18
vendor/nette/utils/ncs.xml
vendored
18
vendor/nette/utils/ncs.xml
vendored
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<ruleset name="Custom" namespace="Nette">
|
||||
<rule ref="$presets/php72.xml"/>
|
||||
|
||||
<!-- bug in SlevomatCodingStandard -->
|
||||
<rule ref="SlevomatCodingStandard.Operators.RequireCombinedAssignmentOperator">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
|
||||
<!-- bug in FunctionSpacingSniff -->
|
||||
<exclude-pattern>./tests/Utils/Reflection.getDeclaringMethod.alias.phpt</exclude-pattern>
|
||||
<exclude-pattern>./tests/Utils/Reflection.getDeclaringMethod.insteadof.phpt</exclude-pattern>
|
||||
|
||||
<!-- use function in Arrays.php, Callback.php, Html.php, Strings.php -->
|
||||
<rule ref="SlevomatCodingStandard.Namespaces.MultipleUsesPerLine.MultipleUsesPerLine">
|
||||
<severity>0</severity>
|
||||
</rule>
|
||||
</ruleset>
|
||||
45
vendor/nette/utils/readme.md
vendored
45
vendor/nette/utils/readme.md
vendored
@@ -1,5 +1,4 @@
|
||||
Nette Utility Classes
|
||||
=====================
|
||||
[](https://doc.nette.org/en/utils)
|
||||
|
||||
[](https://packagist.org/packages/nette/utils)
|
||||
[](https://github.com/nette/utils/actions)
|
||||
@@ -11,24 +10,27 @@ Nette Utility Classes
|
||||
Introduction
|
||||
------------
|
||||
|
||||
In package nette/utils you will find a set of [useful classes](https://doc.nette.org/utils) for everyday use:
|
||||
In package nette/utils you will find a set of useful classes for everyday use:
|
||||
|
||||
- [Arrays](https://doc.nette.org/arrays) - manipulate arrays
|
||||
- [Callback](https://doc.nette.org/callback) - PHP callbacks
|
||||
- [Date and Time](https://doc.nette.org/datetime) - modify times and dates
|
||||
- [Filesystem](https://doc.nette.org/filesystem) - copying, renaming, …
|
||||
- [Helper Functions](https://doc.nette.org/helpers)
|
||||
- [HTML elements](https://doc.nette.org/html-elements) - generate HTML
|
||||
- [Images](https://doc.nette.org/images) - crop, resize, rotate images
|
||||
- [JSON](https://doc.nette.org/json) - encoding and decoding
|
||||
- [Generating Random Strings](https://doc.nette.org/random)
|
||||
- [Paginator](https://doc.nette.org/paginator) - pagination math
|
||||
- [PHP Reflection](https://doc.nette.org/reflection)
|
||||
- [Strings](https://doc.nette.org/strings) - useful text functions
|
||||
- [SmartObject](https://doc.nette.org/smartobject) - PHP object enhancements
|
||||
- [Validation](https://doc.nette.org/validators) - validate inputs
|
||||
- [Type](https://doc.nette.org/type) - PHP data type
|
||||
✅ [Arrays](https://doc.nette.org/utils/arrays)<br>
|
||||
✅ [Callback](https://doc.nette.org/utils/callback) - PHP callbacks<br>
|
||||
✅ [Filesystem](https://doc.nette.org/utils/filesystem) - copying, renaming, …<br>
|
||||
✅ [Finder](https://doc.nette.org/utils/finder) - finds files and directories<br>
|
||||
✅ [Floats](https://doc.nette.org/utils/floats) - floating point numbers<br>
|
||||
✅ [Helper Functions](https://doc.nette.org/utils/helpers)<br>
|
||||
✅ [HTML elements](https://doc.nette.org/utils/html-elements) - generate HTML<br>
|
||||
✅ [Images](https://doc.nette.org/utils/images) - crop, resize, rotate images<br>
|
||||
✅ [Iterables](https://doc.nette.org/utils/iterables) <br>
|
||||
✅ [JSON](https://doc.nette.org/utils/json) - encoding and decoding<br>
|
||||
✅ [Generating Random Strings](https://doc.nette.org/utils/random)<br>
|
||||
✅ [Paginator](https://doc.nette.org/utils/paginator) - pagination math<br>
|
||||
✅ [PHP Reflection](https://doc.nette.org/utils/reflection)<br>
|
||||
✅ [Strings](https://doc.nette.org/utils/strings) - useful text functions<br>
|
||||
✅ [SmartObject](https://doc.nette.org/utils/smartobject) - PHP object enhancements<br>
|
||||
✅ [Type](https://doc.nette.org/utils/type) - PHP data type<br>
|
||||
✅ [Validation](https://doc.nette.org/utils/validators) - validate inputs<br>
|
||||
|
||||
<!---->
|
||||
|
||||
Installation
|
||||
------------
|
||||
@@ -39,10 +41,9 @@ The recommended way to install is via Composer:
|
||||
composer require nette/utils
|
||||
```
|
||||
|
||||
- Nette Utils 3.2 is compatible with PHP 7.2 to 8.2
|
||||
- Nette Utils 3.1 is compatible with PHP 7.1 to 8.0
|
||||
- Nette Utils 3.0 is compatible with PHP 7.1 to 8.0
|
||||
- Nette Utils 2.5 is compatible with PHP 5.6 to 8.0
|
||||
Nette Utils 4.1 is compatible with PHP 8.2 to 8.5.
|
||||
|
||||
<!---->
|
||||
|
||||
[Support Me](https://github.com/sponsors/dg)
|
||||
--------------------------------------------
|
||||
|
||||
@@ -28,30 +28,15 @@ class CachingIterator extends \CachingIterator implements \Countable
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var int */
|
||||
private $counter = 0;
|
||||
private int $counter = 0;
|
||||
|
||||
|
||||
public function __construct($iterator)
|
||||
public function __construct(iterable|\stdClass $iterable)
|
||||
{
|
||||
if (is_array($iterator) || $iterator instanceof \stdClass) {
|
||||
$iterator = new \ArrayIterator($iterator);
|
||||
|
||||
} elseif ($iterator instanceof \IteratorAggregate) {
|
||||
do {
|
||||
$iterator = $iterator->getIterator();
|
||||
} while ($iterator instanceof \IteratorAggregate);
|
||||
|
||||
assert($iterator instanceof \Iterator);
|
||||
|
||||
} elseif ($iterator instanceof \Iterator) {
|
||||
} elseif ($iterator instanceof \Traversable) {
|
||||
$iterator = new \IteratorIterator($iterator);
|
||||
} else {
|
||||
throw new Nette\InvalidArgumentException(sprintf('Invalid argument passed to %s; array or Traversable expected, %s given.', self::class, is_object($iterator) ? get_class($iterator) : gettype($iterator)));
|
||||
}
|
||||
|
||||
parent::__construct($iterator, 0);
|
||||
$iterable = $iterable instanceof \stdClass
|
||||
? new \ArrayIterator((array) $iterable)
|
||||
: Nette\Utils\Iterables::toIterator($iterable);
|
||||
parent::__construct($iterable, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -148,9 +133,8 @@ class CachingIterator extends \CachingIterator implements \Countable
|
||||
|
||||
/**
|
||||
* Returns the next key.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getNextKey()
|
||||
public function getNextKey(): mixed
|
||||
{
|
||||
return $this->getInnerIterator()->key();
|
||||
}
|
||||
@@ -158,9 +142,8 @@ class CachingIterator extends \CachingIterator implements \Countable
|
||||
|
||||
/**
|
||||
* Returns the next element.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getNextValue()
|
||||
public function getNextValue(): mixed
|
||||
{
|
||||
return $this->getInnerIterator()->current();
|
||||
}
|
||||
|
||||
6
vendor/nette/utils/src/Iterators/Mapper.php
vendored
6
vendor/nette/utils/src/Iterators/Mapper.php
vendored
@@ -10,9 +10,8 @@ declare(strict_types=1);
|
||||
namespace Nette\Iterators;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Applies the callback to the elements of the inner iterator.
|
||||
* @deprecated use Nette\Utils\Iterables::map()
|
||||
*/
|
||||
class Mapper extends \IteratorIterator
|
||||
{
|
||||
@@ -27,8 +26,7 @@ class Mapper extends \IteratorIterator
|
||||
}
|
||||
|
||||
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
public function current(): mixed
|
||||
{
|
||||
return ($this->callback)(parent::current(), parent::key());
|
||||
}
|
||||
|
||||
16
vendor/nette/utils/src/SmartObject.php
vendored
16
vendor/nette/utils/src/SmartObject.php
vendored
@@ -22,6 +22,7 @@ use Nette\Utils\ObjectHelpers;
|
||||
trait SmartObject
|
||||
{
|
||||
/**
|
||||
* @return mixed
|
||||
* @throws MemberAccessException
|
||||
*/
|
||||
public function __call(string $name, array $args)
|
||||
@@ -35,11 +36,13 @@ trait SmartObject
|
||||
$handler(...$args);
|
||||
}
|
||||
} elseif ($handlers !== null) {
|
||||
throw new UnexpectedValueException("Property $class::$$name must be iterable or null, " . gettype($handlers) . ' given.');
|
||||
throw new UnexpectedValueException("Property $class::$$name must be iterable or null, " . get_debug_type($handlers) . ' given.');
|
||||
}
|
||||
} else {
|
||||
ObjectHelpers::strictCall($class, $name);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
ObjectHelpers::strictCall($class, $name);
|
||||
}
|
||||
|
||||
|
||||
@@ -87,11 +90,9 @@ trait SmartObject
|
||||
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
* @throws MemberAccessException if the property is not defined or is read-only
|
||||
*/
|
||||
public function __set(string $name, $value)
|
||||
public function __set(string $name, mixed $value): void
|
||||
{
|
||||
$class = static::class;
|
||||
|
||||
@@ -121,10 +122,9 @@ trait SmartObject
|
||||
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws MemberAccessException
|
||||
*/
|
||||
public function __unset(string $name)
|
||||
public function __unset(string $name): void
|
||||
{
|
||||
$class = static::class;
|
||||
if (!ObjectHelpers::hasProperty($class, $name)) {
|
||||
|
||||
17
vendor/nette/utils/src/StaticClass.php
vendored
17
vendor/nette/utils/src/StaticClass.php
vendored
@@ -16,22 +16,9 @@ namespace Nette;
|
||||
trait StaticClass
|
||||
{
|
||||
/**
|
||||
* @return never
|
||||
* @throws \Error
|
||||
* Class is static and cannot be instantiated.
|
||||
*/
|
||||
final public function __construct()
|
||||
private function __construct()
|
||||
{
|
||||
throw new \Error('Class ' . static::class . ' is static and cannot be instantiated.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call to undefined static method.
|
||||
* @return void
|
||||
* @throws MemberAccessException
|
||||
*/
|
||||
public static function __callStatic(string $name, array $args)
|
||||
{
|
||||
Utils\ObjectHelpers::strictStaticCall(static::class, $name);
|
||||
}
|
||||
}
|
||||
|
||||
4
vendor/nette/utils/src/Translator.php
vendored
4
vendor/nette/utils/src/Translator.php
vendored
@@ -17,10 +17,8 @@ interface Translator
|
||||
{
|
||||
/**
|
||||
* Translates the given string.
|
||||
* @param mixed $message
|
||||
* @param mixed ...$parameters
|
||||
*/
|
||||
function translate($message, ...$parameters): string;
|
||||
function translate(string|\Stringable $message, mixed ...$parameters): string|\Stringable;
|
||||
}
|
||||
|
||||
|
||||
|
||||
35
vendor/nette/utils/src/Utils/ArrayHash.php
vendored
35
vendor/nette/utils/src/Utils/ArrayHash.php
vendored
@@ -10,25 +10,27 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function count, is_array, is_scalar, sprintf;
|
||||
|
||||
|
||||
/**
|
||||
* Provides objects to work as array.
|
||||
* @template T
|
||||
* @implements \IteratorAggregate<array-key, T>
|
||||
* @implements \ArrayAccess<array-key, T>
|
||||
*/
|
||||
class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Transforms array to ArrayHash.
|
||||
* @param array<T> $array
|
||||
* @return static
|
||||
*/
|
||||
public static function from(array $array, bool $recursive = true)
|
||||
public static function from(array $array, bool $recursive = true): static
|
||||
{
|
||||
$obj = new static;
|
||||
foreach ($array as $key => $value) {
|
||||
$obj->$key = $recursive && is_array($value)
|
||||
? static::from($value, true)
|
||||
? static::from($value)
|
||||
: $value;
|
||||
}
|
||||
|
||||
@@ -38,11 +40,13 @@ class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \Iterator
|
||||
|
||||
/**
|
||||
* Returns an iterator over all items.
|
||||
* @return \RecursiveArrayIterator<array-key, T>
|
||||
* @return \Iterator<array-key, T>
|
||||
*/
|
||||
public function getIterator(): \RecursiveArrayIterator
|
||||
public function &getIterator(): \Iterator
|
||||
{
|
||||
return new \RecursiveArrayIterator((array) $this);
|
||||
foreach ((array) $this as $key => $foo) {
|
||||
yield $key => $this->$key;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,14 +60,14 @@ class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \Iterator
|
||||
|
||||
|
||||
/**
|
||||
* Replaces or appends a item.
|
||||
* @param string|int $key
|
||||
* Replaces or appends an item.
|
||||
* @param array-key $key
|
||||
* @param T $value
|
||||
*/
|
||||
public function offsetSet($key, $value): void
|
||||
{
|
||||
if (!is_scalar($key)) { // prevents null
|
||||
throw new Nette\InvalidArgumentException(sprintf('Key must be either a string or an integer, %s given.', gettype($key)));
|
||||
throw new Nette\InvalidArgumentException(sprintf('Key must be either a string or an integer, %s given.', get_debug_type($key)));
|
||||
}
|
||||
|
||||
$this->$key = $value;
|
||||
@@ -71,20 +75,19 @@ class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \Iterator
|
||||
|
||||
|
||||
/**
|
||||
* Returns a item.
|
||||
* @param string|int $key
|
||||
* Returns an item.
|
||||
* @param array-key $key
|
||||
* @return T
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($key)
|
||||
public function offsetGet($key): mixed
|
||||
{
|
||||
return $this->$key;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines whether a item exists.
|
||||
* @param string|int $key
|
||||
* Determines whether an item exists.
|
||||
* @param array-key $key
|
||||
*/
|
||||
public function offsetExists($key): bool
|
||||
{
|
||||
@@ -94,7 +97,7 @@ class ArrayHash extends \stdClass implements \ArrayAccess, \Countable, \Iterator
|
||||
|
||||
/**
|
||||
* Removes the element from this list.
|
||||
* @param string|int $key
|
||||
* @param array-key $key
|
||||
*/
|
||||
public function offsetUnset($key): void
|
||||
{
|
||||
|
||||
34
vendor/nette/utils/src/Utils/ArrayList.php
vendored
34
vendor/nette/utils/src/Utils/ArrayList.php
vendored
@@ -10,26 +10,25 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_slice, array_splice, count, is_int;
|
||||
|
||||
|
||||
/**
|
||||
* Provides the base class for a generic list (items can be accessed by index).
|
||||
* @template T
|
||||
* @implements \IteratorAggregate<int, T>
|
||||
* @implements \ArrayAccess<int, T>
|
||||
*/
|
||||
class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var mixed[] */
|
||||
private $list = [];
|
||||
private array $list = [];
|
||||
|
||||
|
||||
/**
|
||||
* Transforms array to ArrayList.
|
||||
* @param array<T> $array
|
||||
* @return static
|
||||
* @param list<T> $array
|
||||
*/
|
||||
public static function from(array $array)
|
||||
public static function from(array $array): static
|
||||
{
|
||||
if (!Arrays::isList($array)) {
|
||||
throw new Nette\InvalidArgumentException('Array is not valid list.');
|
||||
@@ -43,11 +42,13 @@ class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
|
||||
|
||||
/**
|
||||
* Returns an iterator over all items.
|
||||
* @return \ArrayIterator<int, T>
|
||||
* @return \Iterator<int, T>
|
||||
*/
|
||||
public function getIterator(): \ArrayIterator
|
||||
public function &getIterator(): \Iterator
|
||||
{
|
||||
return new \ArrayIterator($this->list);
|
||||
foreach ($this->list as &$item) {
|
||||
yield $item;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +62,7 @@ class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
|
||||
|
||||
|
||||
/**
|
||||
* Replaces or appends a item.
|
||||
* Replaces or appends an item.
|
||||
* @param int|null $index
|
||||
* @param T $value
|
||||
* @throws Nette\OutOfRangeException
|
||||
@@ -81,13 +82,12 @@ class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
|
||||
|
||||
|
||||
/**
|
||||
* Returns a item.
|
||||
* Returns an item.
|
||||
* @param int $index
|
||||
* @return T
|
||||
* @throws Nette\OutOfRangeException
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function offsetGet($index)
|
||||
public function offsetGet($index): mixed
|
||||
{
|
||||
if (!is_int($index) || $index < 0 || $index >= count($this->list)) {
|
||||
throw new Nette\OutOfRangeException('Offset invalid or out of range');
|
||||
@@ -98,7 +98,7 @@ class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
|
||||
|
||||
|
||||
/**
|
||||
* Determines whether a item exists.
|
||||
* Determines whether an item exists.
|
||||
* @param int $index
|
||||
*/
|
||||
public function offsetExists($index): bool
|
||||
@@ -123,10 +123,10 @@ class ArrayList implements \ArrayAccess, \Countable, \IteratorAggregate
|
||||
|
||||
|
||||
/**
|
||||
* Prepends a item.
|
||||
* Prepends an item.
|
||||
* @param T $value
|
||||
*/
|
||||
public function prepend($value): void
|
||||
public function prepend(mixed $value): void
|
||||
{
|
||||
$first = array_slice($this->list, 0, 1);
|
||||
$this->offsetSet(0, $value);
|
||||
|
||||
221
vendor/nette/utils/src/Utils/Arrays.php
vendored
221
vendor/nette/utils/src/Utils/Arrays.php
vendored
@@ -9,8 +9,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace Nette\Utils;
|
||||
|
||||
use JetBrains\PhpStorm\Language;
|
||||
use Nette;
|
||||
use function is_array, is_int, is_object, count;
|
||||
use function array_combine, array_intersect_key, array_is_list, array_key_exists, array_key_first, array_key_last, array_keys, array_reverse, array_search, array_slice, array_walk_recursive, count, func_num_args, in_array, is_array, is_int, is_object, key, preg_split;
|
||||
use const PREG_GREP_INVERT, PREG_SPLIT_DELIM_CAPTURE, PREG_SPLIT_NO_EMPTY;
|
||||
|
||||
|
||||
/**
|
||||
@@ -29,7 +31,7 @@ class Arrays
|
||||
* @return ?T
|
||||
* @throws Nette\InvalidArgumentException if item does not exist and default value is not provided
|
||||
*/
|
||||
public static function get(array $array, $key, $default = null)
|
||||
public static function get(array $array, string|int|array $key, mixed $default = null): mixed
|
||||
{
|
||||
foreach (is_array($key) ? $key : [$key] as $k) {
|
||||
if (is_array($array) && array_key_exists($k, $array)) {
|
||||
@@ -55,7 +57,7 @@ class Arrays
|
||||
* @return ?T
|
||||
* @throws Nette\InvalidArgumentException if traversed item is not an array
|
||||
*/
|
||||
public static function &getRef(array &$array, $key)
|
||||
public static function &getRef(array &$array, string|int|array $key): mixed
|
||||
{
|
||||
foreach (is_array($key) ? $key : [$key] as $k) {
|
||||
if (is_array($array) || $array === null) {
|
||||
@@ -94,12 +96,10 @@ class Arrays
|
||||
|
||||
/**
|
||||
* Returns zero-indexed position of given array key. Returns null if key is not found.
|
||||
* @param array-key $key
|
||||
* @return int|null offset if it is found, null otherwise
|
||||
*/
|
||||
public static function getKeyOffset(array $array, $key): ?int
|
||||
public static function getKeyOffset(array $array, string|int $key): ?int
|
||||
{
|
||||
return Helpers::falseToNull(array_search(self::toKey($key), array_keys($array), true));
|
||||
return Helpers::falseToNull(array_search(self::toKey($key), array_keys($array), strict: true));
|
||||
}
|
||||
|
||||
|
||||
@@ -114,75 +114,118 @@ class Arrays
|
||||
|
||||
/**
|
||||
* Tests an array for the presence of value.
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function contains(array $array, $value): bool
|
||||
public static function contains(array $array, mixed $value): bool
|
||||
{
|
||||
return in_array($value, $array, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first item from the array or null if array is empty.
|
||||
* @template T
|
||||
* @param array<T> $array
|
||||
* @return ?T
|
||||
* Returns the first item (matching the specified predicate if given). If there is no such item, it returns result of invoking $else or null.
|
||||
* @template K of int|string
|
||||
* @template V
|
||||
* @param array<K, V> $array
|
||||
* @param ?callable(V, K, array<K, V>): bool $predicate
|
||||
* @return ?V
|
||||
*/
|
||||
public static function first(array $array)
|
||||
public static function first(array $array, ?callable $predicate = null, ?callable $else = null): mixed
|
||||
{
|
||||
return count($array) ? reset($array) : null;
|
||||
$key = self::firstKey($array, $predicate);
|
||||
return $key === null
|
||||
? ($else ? $else() : null)
|
||||
: $array[$key];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the last item from the array or null if array is empty.
|
||||
* @template T
|
||||
* @param array<T> $array
|
||||
* @return ?T
|
||||
* Returns the last item (matching the specified predicate if given). If there is no such item, it returns result of invoking $else or null.
|
||||
* @template K of int|string
|
||||
* @template V
|
||||
* @param array<K, V> $array
|
||||
* @param ?callable(V, K, array<K, V>): bool $predicate
|
||||
* @return ?V
|
||||
*/
|
||||
public static function last(array $array)
|
||||
public static function last(array $array, ?callable $predicate = null, ?callable $else = null): mixed
|
||||
{
|
||||
return count($array) ? end($array) : null;
|
||||
$key = self::lastKey($array, $predicate);
|
||||
return $key === null
|
||||
? ($else ? $else() : null)
|
||||
: $array[$key];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the key of first item (matching the specified predicate if given) or null if there is no such item.
|
||||
* @template K of int|string
|
||||
* @template V
|
||||
* @param array<K, V> $array
|
||||
* @param ?callable(V, K, array<K, V>): bool $predicate
|
||||
* @return ?K
|
||||
*/
|
||||
public static function firstKey(array $array, ?callable $predicate = null): int|string|null
|
||||
{
|
||||
if (!$predicate) {
|
||||
return array_key_first($array);
|
||||
}
|
||||
foreach ($array as $k => $v) {
|
||||
if ($predicate($v, $k, $array)) {
|
||||
return $k;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the key of last item (matching the specified predicate if given) or null if there is no such item.
|
||||
* @template K of int|string
|
||||
* @template V
|
||||
* @param array<K, V> $array
|
||||
* @param ?callable(V, K, array<K, V>): bool $predicate
|
||||
* @return ?K
|
||||
*/
|
||||
public static function lastKey(array $array, ?callable $predicate = null): int|string|null
|
||||
{
|
||||
return $predicate
|
||||
? self::firstKey(array_reverse($array, preserve_keys: true), $predicate)
|
||||
: array_key_last($array);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inserts the contents of the $inserted array into the $array immediately after the $key.
|
||||
* If $key is null (or does not exist), it is inserted at the beginning.
|
||||
* @param array-key|null $key
|
||||
*/
|
||||
public static function insertBefore(array &$array, $key, array $inserted): void
|
||||
public static function insertBefore(array &$array, string|int|null $key, array $inserted): void
|
||||
{
|
||||
$offset = $key === null ? 0 : (int) self::getKeyOffset($array, $key);
|
||||
$array = array_slice($array, 0, $offset, true)
|
||||
$array = array_slice($array, 0, $offset, preserve_keys: true)
|
||||
+ $inserted
|
||||
+ array_slice($array, $offset, count($array), true);
|
||||
+ array_slice($array, $offset, count($array), preserve_keys: true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inserts the contents of the $inserted array into the $array before the $key.
|
||||
* If $key is null (or does not exist), it is inserted at the end.
|
||||
* @param array-key|null $key
|
||||
*/
|
||||
public static function insertAfter(array &$array, $key, array $inserted): void
|
||||
public static function insertAfter(array &$array, string|int|null $key, array $inserted): void
|
||||
{
|
||||
if ($key === null || ($offset = self::getKeyOffset($array, $key)) === null) {
|
||||
$offset = count($array) - 1;
|
||||
}
|
||||
|
||||
$array = array_slice($array, 0, $offset + 1, true)
|
||||
$array = array_slice($array, 0, $offset + 1, preserve_keys: true)
|
||||
+ $inserted
|
||||
+ array_slice($array, $offset + 1, count($array), true);
|
||||
+ array_slice($array, $offset + 1, count($array), preserve_keys: true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Renames key in array.
|
||||
* @param array-key $oldKey
|
||||
* @param array-key $newKey
|
||||
*/
|
||||
public static function renameKey(array &$array, $oldKey, $newKey): bool
|
||||
public static function renameKey(array &$array, string|int $oldKey, string|int $newKey): bool
|
||||
{
|
||||
$offset = self::getKeyOffset($array, $oldKey);
|
||||
if ($offset === null) {
|
||||
@@ -203,8 +246,14 @@ class Arrays
|
||||
* @param string[] $array
|
||||
* @return string[]
|
||||
*/
|
||||
public static function grep(array $array, string $pattern, int $flags = 0): array
|
||||
public static function grep(
|
||||
array $array,
|
||||
#[Language('RegExp')]
|
||||
string $pattern,
|
||||
bool|int $invert = false,
|
||||
): array
|
||||
{
|
||||
$flags = $invert ? PREG_GREP_INVERT : 0;
|
||||
return Strings::pcre('preg_grep', [$pattern, $array, $flags]);
|
||||
}
|
||||
|
||||
@@ -225,23 +274,19 @@ class Arrays
|
||||
|
||||
/**
|
||||
* Checks if the array is indexed in ascending order of numeric keys from zero, a.k.a list.
|
||||
* @param mixed $value
|
||||
* @return ($value is list ? true : false)
|
||||
*/
|
||||
public static function isList($value): bool
|
||||
public static function isList(mixed $value): bool
|
||||
{
|
||||
return is_array($value) && (PHP_VERSION_ID < 80100
|
||||
? !$value || array_keys($value) === range(0, count($value) - 1)
|
||||
: array_is_list($value)
|
||||
);
|
||||
return is_array($value) && array_is_list($value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reformats table to associative tree. Path looks like 'field|field[]field->field=field'.
|
||||
* @param string|string[] $path
|
||||
* @return array|\stdClass
|
||||
*/
|
||||
public static function associate(array $array, $path)
|
||||
public static function associate(array $array, $path): array|\stdClass
|
||||
{
|
||||
$parts = is_array($path)
|
||||
? $path
|
||||
@@ -293,9 +338,8 @@ class Arrays
|
||||
|
||||
/**
|
||||
* Normalizes array to associative array. Replace numeric keys with their values, the new value will be $filling.
|
||||
* @param mixed $filling
|
||||
*/
|
||||
public static function normalize(array $array, $filling = null): array
|
||||
public static function normalize(array $array, mixed $filling = null): array
|
||||
{
|
||||
$res = [];
|
||||
foreach ($array as $k => $v) {
|
||||
@@ -311,12 +355,11 @@ class Arrays
|
||||
* or returns $default, if provided.
|
||||
* @template T
|
||||
* @param array<T> $array
|
||||
* @param array-key $key
|
||||
* @param ?T $default
|
||||
* @return ?T
|
||||
* @throws Nette\InvalidArgumentException if item does not exist and default value is not provided
|
||||
*/
|
||||
public static function pick(array &$array, $key, $default = null)
|
||||
public static function pick(array &$array, string|int $key, mixed $default = null): mixed
|
||||
{
|
||||
if (array_key_exists($key, $array)) {
|
||||
$value = $array[$key];
|
||||
@@ -333,13 +376,16 @@ class Arrays
|
||||
|
||||
|
||||
/**
|
||||
* Tests whether at least one element in the array passes the test implemented by the
|
||||
* provided callback with signature `function ($value, $key, array $array): bool`.
|
||||
* Tests whether at least one element in the array passes the test implemented by the provided function.
|
||||
* @template K of int|string
|
||||
* @template V
|
||||
* @param array<K, V> $array
|
||||
* @param callable(V, K, array<K, V>): bool $predicate
|
||||
*/
|
||||
public static function some(iterable $array, callable $callback): bool
|
||||
public static function some(iterable $array, callable $predicate): bool
|
||||
{
|
||||
foreach ($array as $k => $v) {
|
||||
if ($callback($v, $k, $array)) {
|
||||
if ($predicate($v, $k, $array)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -349,13 +395,16 @@ class Arrays
|
||||
|
||||
|
||||
/**
|
||||
* Tests whether all elements in the array pass the test implemented by the provided function,
|
||||
* which has the signature `function ($value, $key, array $array): bool`.
|
||||
* Tests whether all elements in the array pass the test implemented by the provided function.
|
||||
* @template K of int|string
|
||||
* @template V
|
||||
* @param array<K, V> $array
|
||||
* @param callable(V, K, array<K, V>): bool $predicate
|
||||
*/
|
||||
public static function every(iterable $array, callable $callback): bool
|
||||
public static function every(iterable $array, callable $predicate): bool
|
||||
{
|
||||
foreach ($array as $k => $v) {
|
||||
if (!$callback($v, $k, $array)) {
|
||||
if (!$predicate($v, $k, $array)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -365,14 +414,64 @@ class Arrays
|
||||
|
||||
|
||||
/**
|
||||
* Calls $callback on all elements in the array and returns the array of return values.
|
||||
* The callback has the signature `function ($value, $key, array $array): bool`.
|
||||
* Returns a new array containing all key-value pairs matching the given $predicate.
|
||||
* @template K of int|string
|
||||
* @template V
|
||||
* @param array<K, V> $array
|
||||
* @param callable(V, K, array<K, V>): bool $predicate
|
||||
* @return array<K, V>
|
||||
*/
|
||||
public static function map(iterable $array, callable $callback): array
|
||||
public static function filter(array $array, callable $predicate): array
|
||||
{
|
||||
$res = [];
|
||||
foreach ($array as $k => $v) {
|
||||
$res[$k] = $callback($v, $k, $array);
|
||||
if ($predicate($v, $k, $array)) {
|
||||
$res[$k] = $v;
|
||||
}
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array containing the original keys and results of applying the given transform function to each element.
|
||||
* @template K of int|string
|
||||
* @template V
|
||||
* @template R
|
||||
* @param array<K, V> $array
|
||||
* @param callable(V, K, array<K, V>): R $transformer
|
||||
* @return array<K, R>
|
||||
*/
|
||||
public static function map(iterable $array, callable $transformer): array
|
||||
{
|
||||
$res = [];
|
||||
foreach ($array as $k => $v) {
|
||||
$res[$k] = $transformer($v, $k, $array);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an array containing new keys and values generated by applying the given transform function to each element.
|
||||
* If the function returns null, the element is skipped.
|
||||
* @template K of int|string
|
||||
* @template V
|
||||
* @template ResK of int|string
|
||||
* @template ResV
|
||||
* @param array<K, V> $array
|
||||
* @param callable(V, K, array<K, V>): ?array{ResK, ResV} $transformer
|
||||
* @return array<ResK, ResV>
|
||||
*/
|
||||
public static function mapWithKeys(array $array, callable $transformer): array
|
||||
{
|
||||
$res = [];
|
||||
foreach ($array as $k => $v) {
|
||||
$pair = $transformer($v, $k, $array);
|
||||
if ($pair) {
|
||||
$res[$pair[0]] = $pair[1];
|
||||
}
|
||||
}
|
||||
|
||||
return $res;
|
||||
@@ -415,7 +514,7 @@ class Arrays
|
||||
* @param T $object
|
||||
* @return T
|
||||
*/
|
||||
public static function toObject(iterable $array, $object)
|
||||
public static function toObject(iterable $array, object $object): object
|
||||
{
|
||||
foreach ($array as $k => $v) {
|
||||
$object->$k = $v;
|
||||
@@ -427,12 +526,10 @@ class Arrays
|
||||
|
||||
/**
|
||||
* Converts value to array key.
|
||||
* @param mixed $value
|
||||
* @return array-key
|
||||
*/
|
||||
public static function toKey($value)
|
||||
public static function toKey(mixed $value): int|string
|
||||
{
|
||||
return key([$value => null]);
|
||||
return key(@[$value => null]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
81
vendor/nette/utils/src/Utils/Callback.php
vendored
81
vendor/nette/utils/src/Utils/Callback.php
vendored
@@ -10,7 +10,7 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function is_array, is_object, is_string;
|
||||
use function explode, func_get_args, ini_get, is_array, is_callable, is_object, is_string, preg_replace, restore_error_handler, set_error_handler, sprintf, str_contains, str_ends_with;
|
||||
|
||||
|
||||
/**
|
||||
@@ -20,52 +20,10 @@ final class Callback
|
||||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
/**
|
||||
* @param string|object|callable $callable class, object, callable
|
||||
* @deprecated use Closure::fromCallable()
|
||||
*/
|
||||
public static function closure($callable, ?string $method = null): \Closure
|
||||
{
|
||||
trigger_error(__METHOD__ . '() is deprecated, use Closure::fromCallable().', E_USER_DEPRECATED);
|
||||
try {
|
||||
return \Closure::fromCallable($method === null ? $callable : [$callable, $method]);
|
||||
} catch (\TypeError $e) {
|
||||
throw new Nette\InvalidArgumentException($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invokes callback.
|
||||
* @return mixed
|
||||
* @deprecated
|
||||
*/
|
||||
public static function invoke($callable, ...$args)
|
||||
{
|
||||
trigger_error(__METHOD__ . '() is deprecated, use native invoking.', E_USER_DEPRECATED);
|
||||
self::check($callable);
|
||||
return $callable(...$args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invokes callback with an array of parameters.
|
||||
* @return mixed
|
||||
* @deprecated
|
||||
*/
|
||||
public static function invokeArgs($callable, array $args = [])
|
||||
{
|
||||
trigger_error(__METHOD__ . '() is deprecated, use native invoking.', E_USER_DEPRECATED);
|
||||
self::check($callable);
|
||||
return $callable(...$args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invokes internal PHP function with own error handler.
|
||||
* @return mixed
|
||||
*/
|
||||
public static function invokeSafe(string $function, array $args, callable $onError)
|
||||
public static function invokeSafe(string $function, array $args, callable $onError): mixed
|
||||
{
|
||||
$prev = set_error_handler(function ($severity, $message, $file) use ($onError, &$prev, $function): ?bool {
|
||||
if ($file === __FILE__) {
|
||||
@@ -92,17 +50,16 @@ final class Callback
|
||||
/**
|
||||
* Checks that $callable is valid PHP callback. Otherwise throws exception. If the $syntax is set to true, only verifies
|
||||
* that $callable has a valid structure to be used as a callback, but does not verify if the class or method actually exists.
|
||||
* @param mixed $callable
|
||||
* @return callable
|
||||
* @throws Nette\InvalidArgumentException
|
||||
*/
|
||||
public static function check($callable, bool $syntax = false)
|
||||
public static function check(mixed $callable, bool $syntax = false)
|
||||
{
|
||||
if (!is_callable($callable, $syntax)) {
|
||||
throw new Nette\InvalidArgumentException(
|
||||
$syntax
|
||||
? 'Given value is not a callable type.'
|
||||
: sprintf("Callback '%s' is not callable.", self::toString($callable))
|
||||
: sprintf("Callback '%s' is not callable.", self::toString($callable)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -112,15 +69,12 @@ final class Callback
|
||||
|
||||
/**
|
||||
* Converts PHP callback to textual form. Class or method may not exists.
|
||||
* @param mixed $callable
|
||||
*/
|
||||
public static function toString($callable): string
|
||||
public static function toString(mixed $callable): string
|
||||
{
|
||||
if ($callable instanceof \Closure) {
|
||||
$inner = self::unwrap($callable);
|
||||
return '{closure' . ($inner instanceof \Closure ? '}' : ' ' . self::toString($inner) . '}');
|
||||
} elseif (is_string($callable) && $callable[0] === "\0") {
|
||||
return '{lambda}';
|
||||
} else {
|
||||
is_callable(is_object($callable) ? [$callable, '__invoke'] : $callable, true, $textual);
|
||||
return $textual;
|
||||
@@ -131,21 +85,20 @@ final class Callback
|
||||
/**
|
||||
* Returns reflection for method or function used in PHP callback.
|
||||
* @param callable $callable type check is escalated to ReflectionException
|
||||
* @return \ReflectionMethod|\ReflectionFunction
|
||||
* @throws \ReflectionException if callback is not valid
|
||||
*/
|
||||
public static function toReflection($callable): \ReflectionFunctionAbstract
|
||||
public static function toReflection($callable): \ReflectionMethod|\ReflectionFunction
|
||||
{
|
||||
if ($callable instanceof \Closure) {
|
||||
$callable = self::unwrap($callable);
|
||||
}
|
||||
|
||||
if (is_string($callable) && strpos($callable, '::')) {
|
||||
return new \ReflectionMethod($callable);
|
||||
if (is_string($callable) && str_contains($callable, '::')) {
|
||||
return new ReflectionMethod(...explode('::', $callable, 2));
|
||||
} elseif (is_array($callable)) {
|
||||
return new \ReflectionMethod($callable[0], $callable[1]);
|
||||
return new ReflectionMethod($callable[0], $callable[1]);
|
||||
} elseif (is_object($callable) && !$callable instanceof \Closure) {
|
||||
return new \ReflectionMethod($callable, '__invoke');
|
||||
return new ReflectionMethod($callable, '__invoke');
|
||||
} else {
|
||||
return new \ReflectionFunction($callable);
|
||||
}
|
||||
@@ -157,25 +110,25 @@ final class Callback
|
||||
*/
|
||||
public static function isStatic(callable $callable): bool
|
||||
{
|
||||
return is_array($callable) ? is_string($callable[0]) : is_string($callable);
|
||||
return is_string(is_array($callable) ? $callable[0] : $callable);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Unwraps closure created by Closure::fromCallable().
|
||||
* @return callable|array
|
||||
*/
|
||||
public static function unwrap(\Closure $closure)
|
||||
public static function unwrap(\Closure $closure): callable|array
|
||||
{
|
||||
$r = new \ReflectionFunction($closure);
|
||||
if (substr($r->name, -1) === '}') {
|
||||
$class = $r->getClosureScopeClass()?->name;
|
||||
if (str_ends_with($r->name, '}')) {
|
||||
return $closure;
|
||||
|
||||
} elseif ($obj = $r->getClosureThis()) {
|
||||
} elseif (($obj = $r->getClosureThis()) && $obj::class === $class) {
|
||||
return [$obj, $r->name];
|
||||
|
||||
} elseif ($class = $r->getClosureScopeClass()) {
|
||||
return [$class->name, $r->name];
|
||||
} elseif ($class) {
|
||||
return [$class, $r->name];
|
||||
|
||||
} else {
|
||||
return $r->name;
|
||||
|
||||
154
vendor/nette/utils/src/Utils/DateTime.php
vendored
154
vendor/nette/utils/src/Utils/DateTime.php
vendored
@@ -9,7 +9,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_merge, checkdate, implode, is_numeric, is_string, preg_replace_callback, sprintf, time, trim;
|
||||
|
||||
|
||||
/**
|
||||
@@ -17,8 +17,6 @@ use Nette;
|
||||
*/
|
||||
class DateTime extends \DateTime implements \JsonSerializable
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** minute in seconds */
|
||||
public const MINUTE = 60;
|
||||
|
||||
@@ -32,29 +30,27 @@ class DateTime extends \DateTime implements \JsonSerializable
|
||||
public const WEEK = 7 * self::DAY;
|
||||
|
||||
/** average month in seconds */
|
||||
public const MONTH = 2629800;
|
||||
public const MONTH = 2_629_800;
|
||||
|
||||
/** average year in seconds */
|
||||
public const YEAR = 31557600;
|
||||
public const YEAR = 31_557_600;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a DateTime object from a string, UNIX timestamp, or other DateTimeInterface object.
|
||||
* @param string|int|\DateTimeInterface $time
|
||||
* @return static
|
||||
* @throws \Exception if the date and time are not valid.
|
||||
*/
|
||||
public static function from($time)
|
||||
public static function from(string|int|\DateTimeInterface|null $time): static
|
||||
{
|
||||
if ($time instanceof \DateTimeInterface) {
|
||||
return new static($time->format('Y-m-d H:i:s.u'), $time->getTimezone());
|
||||
return static::createFromInterface($time);
|
||||
|
||||
} elseif (is_numeric($time)) {
|
||||
if ($time <= self::YEAR) {
|
||||
$time += time();
|
||||
}
|
||||
|
||||
return (new static('@' . $time))->setTimezone(new \DateTimeZone(date_default_timezone_get()));
|
||||
return (new static)->setTimestamp((int) $time);
|
||||
|
||||
} else { // textual or null
|
||||
return new static((string) $time);
|
||||
@@ -64,8 +60,7 @@ class DateTime extends \DateTime implements \JsonSerializable
|
||||
|
||||
/**
|
||||
* Creates DateTime object.
|
||||
* @return static
|
||||
* @throws Nette\InvalidArgumentException if the date and time are not valid.
|
||||
* @throws \Exception if the date and time are not valid.
|
||||
*/
|
||||
public static function fromParts(
|
||||
int $year,
|
||||
@@ -73,50 +68,108 @@ class DateTime extends \DateTime implements \JsonSerializable
|
||||
int $day,
|
||||
int $hour = 0,
|
||||
int $minute = 0,
|
||||
float $second = 0.0
|
||||
) {
|
||||
$s = sprintf('%04d-%02d-%02d %02d:%02d:%02.5F', $year, $month, $day, $hour, $minute, $second);
|
||||
if (
|
||||
!checkdate($month, $day, $year)
|
||||
|| $hour < 0
|
||||
|| $hour > 23
|
||||
|| $minute < 0
|
||||
|| $minute > 59
|
||||
|| $second < 0
|
||||
|| $second >= 60
|
||||
) {
|
||||
throw new Nette\InvalidArgumentException("Invalid date '$s'");
|
||||
}
|
||||
|
||||
return new static($s);
|
||||
float $second = 0.0,
|
||||
): static
|
||||
{
|
||||
$sec = (int) floor($second);
|
||||
return (new static(''))
|
||||
->setDate($year, $month, $day)
|
||||
->setTime($hour, $minute, $sec, (int) round(($second - $sec) * 1e6));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns new DateTime object formatted according to the specified format.
|
||||
* @param string $format The format the $time parameter should be in
|
||||
* @param string $time
|
||||
* @param string|\DateTimeZone $timezone (default timezone is used if null is passed)
|
||||
* @return static|false
|
||||
* Returns a new DateTime object formatted according to the specified format.
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public static function createFromFormat($format, $time, $timezone = null)
|
||||
public static function createFromFormat(
|
||||
string $format,
|
||||
string $datetime,
|
||||
string|\DateTimeZone|null $timezone = null,
|
||||
): static|false
|
||||
{
|
||||
if ($timezone === null) {
|
||||
$timezone = new \DateTimeZone(date_default_timezone_get());
|
||||
|
||||
} elseif (is_string($timezone)) {
|
||||
if (is_string($timezone)) {
|
||||
$timezone = new \DateTimeZone($timezone);
|
||||
|
||||
} elseif (!$timezone instanceof \DateTimeZone) {
|
||||
throw new Nette\InvalidArgumentException('Invalid timezone given');
|
||||
}
|
||||
|
||||
$date = parent::createFromFormat($format, $time, $timezone);
|
||||
$date = parent::createFromFormat($format, $datetime, $timezone);
|
||||
return $date ? static::from($date) : false;
|
||||
}
|
||||
|
||||
|
||||
public function __construct(string $datetime = 'now', ?\DateTimeZone $timezone = null)
|
||||
{
|
||||
$this->apply($datetime, $timezone, true);
|
||||
}
|
||||
|
||||
|
||||
public function modify(string $modifier): static
|
||||
{
|
||||
$this->apply($modifier);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function setDate(int $year, int $month, int $day): static
|
||||
{
|
||||
if (!checkdate($month, $day, $year)) {
|
||||
throw new \Exception(sprintf('The date %04d-%02d-%02d is not valid.', $year, $month, $day));
|
||||
}
|
||||
return parent::setDate($year, $month, $day);
|
||||
}
|
||||
|
||||
|
||||
public function setTime(int $hour, int $minute, int $second = 0, int $microsecond = 0): static
|
||||
{
|
||||
if (
|
||||
$hour < 0 || $hour > 23
|
||||
|| $minute < 0 || $minute > 59
|
||||
|| $second < 0 || $second >= 60
|
||||
|| $microsecond < 0 || $microsecond >= 1_000_000
|
||||
) {
|
||||
throw new \Exception(sprintf('The time %02d:%02d:%08.5F is not valid.', $hour, $minute, $second + $microsecond / 1_000_000));
|
||||
}
|
||||
return parent::setTime($hour, $minute, $second, $microsecond);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts a relative time string (e.g. '10 minut') to seconds.
|
||||
*/
|
||||
public static function relativeToSeconds(string $relativeTime): int
|
||||
{
|
||||
return (new self('@0 ' . $relativeTime))
|
||||
->getTimestamp();
|
||||
}
|
||||
|
||||
|
||||
private function apply(string $datetime, $timezone = null, bool $ctr = false): void
|
||||
{
|
||||
$relPart = '';
|
||||
$absPart = preg_replace_callback(
|
||||
'/[+-]?\s*\d+\s+((microsecond|millisecond|[mµu]sec)s?|[mµ]s|sec(ond)?s?|min(ute)?s?|hours?)(\s+ago)?\b/iu',
|
||||
function ($m) use (&$relPart) {
|
||||
$relPart .= $m[0] . ' ';
|
||||
return '';
|
||||
},
|
||||
$datetime,
|
||||
);
|
||||
|
||||
if ($ctr) {
|
||||
parent::__construct($absPart, $timezone);
|
||||
$this->handleErrors($datetime);
|
||||
} elseif (trim($absPart)) {
|
||||
parent::modify($absPart) && $this->handleErrors($datetime);
|
||||
}
|
||||
|
||||
if ($relPart) {
|
||||
$timezone ??= $this->getTimezone();
|
||||
$this->setTimezone(new \DateTimeZone('UTC'));
|
||||
parent::modify($relPart) && $this->handleErrors($datetime);
|
||||
$this->setTimezone($timezone);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns JSON representation in ISO 8601 (used by JavaScript).
|
||||
*/
|
||||
@@ -136,12 +189,21 @@ class DateTime extends \DateTime implements \JsonSerializable
|
||||
|
||||
|
||||
/**
|
||||
* Creates a copy with a modified time.
|
||||
* @return static
|
||||
* You'd better use: (clone $dt)->modify(...)
|
||||
*/
|
||||
public function modifyClone(string $modify = '')
|
||||
public function modifyClone(string $modify = ''): static
|
||||
{
|
||||
$dolly = clone $this;
|
||||
return $modify ? $dolly->modify($modify) : $dolly;
|
||||
}
|
||||
|
||||
|
||||
private function handleErrors(string $value): void
|
||||
{
|
||||
$errors = self::getLastErrors();
|
||||
$errors = array_merge($errors['errors'] ?? [], $errors['warnings'] ?? []);
|
||||
if ($errors) {
|
||||
throw new \Exception(implode(', ', $errors) . " '$value'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
134
vendor/nette/utils/src/Utils/FileSystem.php
vendored
134
vendor/nette/utils/src/Utils/FileSystem.php
vendored
@@ -10,6 +10,8 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_pop, chmod, decoct, dirname, end, fclose, file_exists, file_get_contents, file_put_contents, fopen, implode, is_dir, is_file, is_link, mkdir, preg_match, preg_split, realpath, rename, rmdir, rtrim, sprintf, str_replace, stream_copy_to_stream, stream_is_local, strtr;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
|
||||
|
||||
/**
|
||||
@@ -17,27 +19,25 @@ use Nette;
|
||||
*/
|
||||
final class FileSystem
|
||||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
/**
|
||||
* Creates a directory if it doesn't exist.
|
||||
* Creates a directory if it does not exist, including parent directories.
|
||||
* @throws Nette\IOException on error occurred
|
||||
*/
|
||||
public static function createDir(string $dir, int $mode = 0777): void
|
||||
public static function createDir(string $dir, int $mode = 0o777): void
|
||||
{
|
||||
if (!is_dir($dir) && !@mkdir($dir, $mode, true) && !is_dir($dir)) { // @ - dir may already exist
|
||||
if (!is_dir($dir) && !@mkdir($dir, $mode, recursive: true) && !is_dir($dir)) { // @ - dir may already exist
|
||||
throw new Nette\IOException(sprintf(
|
||||
"Unable to create directory '%s' with mode %s. %s",
|
||||
self::normalizePath($dir),
|
||||
decoct($mode),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Copies a file or a directory. Overwrites existing files and directories by default.
|
||||
* Copies a file or an entire directory. Overwrites existing files and directories by default.
|
||||
* @throws Nette\IOException on error occurred
|
||||
* @throws Nette\InvalidStateException if $overwrite is set to false and destination already exists
|
||||
*/
|
||||
@@ -64,16 +64,12 @@ final class FileSystem
|
||||
}
|
||||
} else {
|
||||
static::createDir(dirname($target));
|
||||
if (
|
||||
($s = @fopen($origin, 'rb'))
|
||||
&& ($d = @fopen($target, 'wb'))
|
||||
&& @stream_copy_to_stream($s, $d) === false
|
||||
) { // @ is escalated to exception
|
||||
if (@stream_copy_to_stream(static::open($origin, 'rb'), static::open($target, 'wb')) === false) { // @ is escalated to exception
|
||||
throw new Nette\IOException(sprintf(
|
||||
"Unable to copy file '%s' to '%s'. %s",
|
||||
self::normalizePath($origin),
|
||||
self::normalizePath($target),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -81,7 +77,26 @@ final class FileSystem
|
||||
|
||||
|
||||
/**
|
||||
* Deletes a file or directory if exists.
|
||||
* Opens file and returns resource.
|
||||
* @return resource
|
||||
* @throws Nette\IOException on error occurred
|
||||
*/
|
||||
public static function open(string $path, string $mode)
|
||||
{
|
||||
$f = @fopen($path, $mode); // @ is escalated to exception
|
||||
if (!$f) {
|
||||
throw new Nette\IOException(sprintf(
|
||||
"Unable to open file '%s'. %s",
|
||||
self::normalizePath($path),
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
return $f;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deletes a file or an entire directory if exists. If the directory is not empty, it deletes its contents first.
|
||||
* @throws Nette\IOException on error occurred
|
||||
*/
|
||||
public static function delete(string $path): void
|
||||
@@ -92,7 +107,7 @@ final class FileSystem
|
||||
throw new Nette\IOException(sprintf(
|
||||
"Unable to delete '%s'. %s",
|
||||
self::normalizePath($path),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
} elseif (is_dir($path)) {
|
||||
@@ -104,7 +119,7 @@ final class FileSystem
|
||||
throw new Nette\IOException(sprintf(
|
||||
"Unable to delete directory '%s'. %s",
|
||||
self::normalizePath($path),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -135,7 +150,7 @@ final class FileSystem
|
||||
"Unable to rename file or directory '%s' to '%s'. %s",
|
||||
self::normalizePath($origin),
|
||||
self::normalizePath($target),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -153,7 +168,7 @@ final class FileSystem
|
||||
throw new Nette\IOException(sprintf(
|
||||
"Unable to read file '%s'. %s",
|
||||
self::normalizePath($file),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -161,18 +176,49 @@ final class FileSystem
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads the file content line by line. Because it reads continuously as we iterate over the lines,
|
||||
* it is possible to read files larger than the available memory.
|
||||
* @return \Generator<int, string>
|
||||
* @throws Nette\IOException on error occurred
|
||||
*/
|
||||
public static function readLines(string $file, bool $stripNewLines = true): \Generator
|
||||
{
|
||||
return (function ($f) use ($file, $stripNewLines) {
|
||||
$counter = 0;
|
||||
do {
|
||||
$line = Callback::invokeSafe('fgets', [$f], fn($error) => throw new Nette\IOException(sprintf(
|
||||
"Unable to read file '%s'. %s",
|
||||
self::normalizePath($file),
|
||||
$error,
|
||||
)));
|
||||
if ($line === false) {
|
||||
fclose($f);
|
||||
break;
|
||||
}
|
||||
if ($stripNewLines) {
|
||||
$line = rtrim($line, "\r\n");
|
||||
}
|
||||
|
||||
yield $counter++ => $line;
|
||||
|
||||
} while (true);
|
||||
})(static::open($file, 'r'));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Writes the string to a file.
|
||||
* @throws Nette\IOException on error occurred
|
||||
*/
|
||||
public static function write(string $file, string $content, ?int $mode = 0666): void
|
||||
public static function write(string $file, string $content, ?int $mode = 0o666): void
|
||||
{
|
||||
static::createDir(dirname($file));
|
||||
if (@file_put_contents($file, $content) === false) { // @ is escalated to exception
|
||||
throw new Nette\IOException(sprintf(
|
||||
"Unable to write file '%s'. %s",
|
||||
self::normalizePath($file),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -181,17 +227,18 @@ final class FileSystem
|
||||
"Unable to chmod file '%s' to mode %s. %s",
|
||||
self::normalizePath($file),
|
||||
decoct($mode),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fixes permissions to a specific file or directory. Directories can be fixed recursively.
|
||||
* Sets file permissions to `$fileMode` or directory permissions to `$dirMode`.
|
||||
* Recursively traverses and sets permissions on the entire contents of the directory as well.
|
||||
* @throws Nette\IOException on error occurred
|
||||
*/
|
||||
public static function makeWritable(string $path, int $dirMode = 0777, int $fileMode = 0666): void
|
||||
public static function makeWritable(string $path, int $dirMode = 0o777, int $fileMode = 0o666): void
|
||||
{
|
||||
if (is_file($path)) {
|
||||
if (!@chmod($path, $fileMode)) { // @ is escalated to exception
|
||||
@@ -199,7 +246,7 @@ final class FileSystem
|
||||
"Unable to chmod file '%s' to mode %s. %s",
|
||||
self::normalizePath($path),
|
||||
decoct($fileMode),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
} elseif (is_dir($path)) {
|
||||
@@ -212,7 +259,7 @@ final class FileSystem
|
||||
"Unable to chmod directory '%s' to mode %s. %s",
|
||||
self::normalizePath($path),
|
||||
decoct($dirMode),
|
||||
Helpers::getLastError()
|
||||
Helpers::getLastError(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
@@ -226,7 +273,7 @@ final class FileSystem
|
||||
*/
|
||||
public static function isAbsolute(string $path): bool
|
||||
{
|
||||
return (bool) preg_match('#([a-z]:)?[/\\\\]|[a-z][a-z0-9+.-]*://#Ai', $path);
|
||||
return (bool) preg_match('#([a-z]:)?[/\\\]|[a-z][a-z0-9+.-]*://#Ai', $path);
|
||||
}
|
||||
|
||||
|
||||
@@ -235,7 +282,7 @@ final class FileSystem
|
||||
*/
|
||||
public static function normalizePath(string $path): string
|
||||
{
|
||||
$parts = $path === '' ? [] : preg_split('~[/\\\\]+~', $path);
|
||||
$parts = $path === '' ? [] : preg_split('~[/\\\]+~', $path);
|
||||
$res = [];
|
||||
foreach ($parts as $part) {
|
||||
if ($part === '..' && $res && end($res) !== '..' && end($res) !== '') {
|
||||
@@ -258,4 +305,37 @@ final class FileSystem
|
||||
{
|
||||
return self::normalizePath(implode('/', $paths));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolves a path against a base path. If the path is absolute, returns it directly, if it's relative, joins it with the base path.
|
||||
*/
|
||||
public static function resolvePath(string $basePath, string $path): string
|
||||
{
|
||||
return match (true) {
|
||||
self::isAbsolute($path) => self::platformSlashes($path),
|
||||
$path === '' => self::platformSlashes($basePath),
|
||||
default => self::joinPaths($basePath, $path),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts backslashes to slashes.
|
||||
*/
|
||||
public static function unixSlashes(string $path): string
|
||||
{
|
||||
return strtr($path, '\\', '/');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts slashes to platform-specific directory separators.
|
||||
*/
|
||||
public static function platformSlashes(string $path): string
|
||||
{
|
||||
return DIRECTORY_SEPARATOR === '/'
|
||||
? strtr($path, '\\', '/')
|
||||
: str_replace(':\\\\', '://', strtr($path, '/', '\\')); // protocol://
|
||||
}
|
||||
}
|
||||
|
||||
1
vendor/nette/utils/src/Utils/Floats.php
vendored
1
vendor/nette/utils/src/Utils/Floats.php
vendored
@@ -10,6 +10,7 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function abs, is_finite, is_nan, max, round;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
48
vendor/nette/utils/src/Utils/Helpers.php
vendored
48
vendor/nette/utils/src/Utils/Helpers.php
vendored
@@ -10,16 +10,21 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_unique, ini_get, levenshtein, max, min, ob_end_clean, ob_get_clean, ob_start, preg_replace, strlen;
|
||||
use const PHP_OS_FAMILY;
|
||||
|
||||
|
||||
class Helpers
|
||||
{
|
||||
public const IsWindows = PHP_OS_FAMILY === 'Windows';
|
||||
|
||||
|
||||
/**
|
||||
* Executes a callback and returns the captured output as a string.
|
||||
*/
|
||||
public static function capture(callable $func): string
|
||||
{
|
||||
ob_start(function () {});
|
||||
ob_start(fn() => '');
|
||||
try {
|
||||
$func();
|
||||
return ob_get_clean();
|
||||
@@ -45,10 +50,8 @@ class Helpers
|
||||
|
||||
/**
|
||||
* Converts false to null, does not change other values.
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
public static function falseToNull($value)
|
||||
public static function falseToNull(mixed $value): mixed
|
||||
{
|
||||
return $value === false ? null : $value;
|
||||
}
|
||||
@@ -56,12 +59,8 @@ class Helpers
|
||||
|
||||
/**
|
||||
* Returns value clamped to the inclusive range of min and max.
|
||||
* @param int|float $value
|
||||
* @param int|float $min
|
||||
* @param int|float $max
|
||||
* @return int|float
|
||||
*/
|
||||
public static function clamp($value, $min, $max)
|
||||
public static function clamp(int|float $value, int|float $min, int|float $max): int|float
|
||||
{
|
||||
if ($min > $max) {
|
||||
throw new Nette\InvalidArgumentException("Minimum ($min) is not less than maximum ($max).");
|
||||
@@ -88,4 +87,35 @@ class Helpers
|
||||
|
||||
return $best;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares two values in the same way that PHP does. Recognizes operators: >, >=, <, <=, =, ==, ===, !=, !==, <>
|
||||
*/
|
||||
public static function compare(mixed $left, string $operator, mixed $right): bool
|
||||
{
|
||||
return match ($operator) {
|
||||
'>' => $left > $right,
|
||||
'>=' => $left >= $right,
|
||||
'<' => $left < $right,
|
||||
'<=' => $left <= $right,
|
||||
'=', '==' => $left == $right,
|
||||
'===' => $left === $right,
|
||||
'!=', '<>' => $left != $right,
|
||||
'!==' => $left !== $right,
|
||||
default => throw new Nette\InvalidArgumentException("Unknown operator '$operator'"),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Splits a class name into namespace and short class name.
|
||||
* @return array{string, string}
|
||||
*/
|
||||
public static function splitClassName(string $name): array
|
||||
{
|
||||
return ($pos = strrpos($name, '\\')) === false
|
||||
? ['', $name]
|
||||
: [substr($name, 0, $pos), substr($name, $pos + 1)];
|
||||
}
|
||||
}
|
||||
|
||||
128
vendor/nette/utils/src/Utils/Html.php
vendored
128
vendor/nette/utils/src/Utils/Html.php
vendored
@@ -9,9 +9,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use Nette\HtmlStringable;
|
||||
use function is_array, is_float, is_object, is_string;
|
||||
use function array_merge, array_splice, count, explode, func_num_args, html_entity_decode, htmlspecialchars, http_build_query, implode, is_array, is_bool, is_float, is_object, is_string, json_encode, max, number_format, rtrim, str_contains, str_repeat, str_replace, strip_tags, strncmp, strpbrk, substr;
|
||||
use const ENT_HTML5, ENT_NOQUOTES, ENT_QUOTES;
|
||||
|
||||
|
||||
/**
|
||||
@@ -233,37 +233,30 @@ use function is_array, is_float, is_object, is_string;
|
||||
*/
|
||||
class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringable
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var array<string, mixed> element's attributes */
|
||||
public $attrs = [];
|
||||
public array $attrs = [];
|
||||
|
||||
/** @var bool use XHTML syntax? */
|
||||
public static $xhtml = false;
|
||||
|
||||
/** @var array<string, int> void elements */
|
||||
public static $emptyElements = [
|
||||
/** void elements */
|
||||
public static array $emptyElements = [
|
||||
'img' => 1, 'hr' => 1, 'br' => 1, 'input' => 1, 'meta' => 1, 'area' => 1, 'embed' => 1, 'keygen' => 1,
|
||||
'source' => 1, 'base' => 1, 'col' => 1, 'link' => 1, 'param' => 1, 'basefont' => 1, 'frame' => 1,
|
||||
'isindex' => 1, 'wbr' => 1, 'command' => 1, 'track' => 1,
|
||||
];
|
||||
|
||||
/** @var array<int, HtmlStringable|string> nodes */
|
||||
protected $children = [];
|
||||
protected array $children = [];
|
||||
|
||||
/** @var string element's name */
|
||||
private $name;
|
||||
/** element's name */
|
||||
private string $name = '';
|
||||
|
||||
/** @var bool is element empty? */
|
||||
private $isEmpty;
|
||||
private bool $isEmpty = false;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs new HTML element.
|
||||
* @param array|string $attrs element's attributes or plain text content
|
||||
* @return static
|
||||
*/
|
||||
public static function el(?string $name = null, $attrs = null)
|
||||
public static function el(?string $name = null, array|string|null $attrs = null): static
|
||||
{
|
||||
$el = new static;
|
||||
$parts = explode(' ', (string) $name, 2);
|
||||
@@ -289,7 +282,7 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
/**
|
||||
* Returns an object representing HTML text.
|
||||
*/
|
||||
public static function fromHtml(string $html): self
|
||||
public static function fromHtml(string $html): static
|
||||
{
|
||||
return (new static)->setHtml($html);
|
||||
}
|
||||
@@ -298,7 +291,7 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
/**
|
||||
* Returns an object representing plain text.
|
||||
*/
|
||||
public static function fromText(string $text): self
|
||||
public static function fromText(string $text): static
|
||||
{
|
||||
return (new static)->setText($text);
|
||||
}
|
||||
@@ -333,9 +326,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Changes element's name.
|
||||
* @return static
|
||||
*/
|
||||
final public function setName(string $name, ?bool $isEmpty = null)
|
||||
final public function setName(string $name, ?bool $isEmpty = null): static
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->isEmpty = $isEmpty ?? isset(static::$emptyElements[$name]);
|
||||
@@ -363,9 +355,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Sets multiple attributes.
|
||||
* @return static
|
||||
*/
|
||||
public function addAttributes(array $attrs)
|
||||
public function addAttributes(array $attrs): static
|
||||
{
|
||||
$this->attrs = array_merge($this->attrs, $attrs);
|
||||
return $this;
|
||||
@@ -374,11 +365,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Appends value to element's attribute.
|
||||
* @param mixed $value
|
||||
* @param mixed $option
|
||||
* @return static
|
||||
*/
|
||||
public function appendAttribute(string $name, $value, $option = true)
|
||||
public function appendAttribute(string $name, mixed $value, mixed $option = true): static
|
||||
{
|
||||
if (is_array($value)) {
|
||||
$prev = isset($this->attrs[$name]) ? (array) $this->attrs[$name] : [];
|
||||
@@ -400,10 +388,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Sets element's attribute.
|
||||
* @param mixed $value
|
||||
* @return static
|
||||
*/
|
||||
public function setAttribute(string $name, $value)
|
||||
public function setAttribute(string $name, mixed $value): static
|
||||
{
|
||||
$this->attrs[$name] = $value;
|
||||
return $this;
|
||||
@@ -412,9 +398,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Returns element's attribute.
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAttribute(string $name)
|
||||
public function getAttribute(string $name): mixed
|
||||
{
|
||||
return $this->attrs[$name] ?? null;
|
||||
}
|
||||
@@ -422,9 +407,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Unsets element's attribute.
|
||||
* @return static
|
||||
*/
|
||||
public function removeAttribute(string $name)
|
||||
public function removeAttribute(string $name): static
|
||||
{
|
||||
unset($this->attrs[$name]);
|
||||
return $this;
|
||||
@@ -433,9 +417,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Unsets element's attributes.
|
||||
* @return static
|
||||
*/
|
||||
public function removeAttributes(array $attributes)
|
||||
public function removeAttributes(array $attributes): static
|
||||
{
|
||||
foreach ($attributes as $name) {
|
||||
unset($this->attrs[$name]);
|
||||
@@ -447,9 +430,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Overloaded setter for element's attribute.
|
||||
* @param mixed $value
|
||||
*/
|
||||
final public function __set(string $name, $value): void
|
||||
final public function __set(string $name, mixed $value): void
|
||||
{
|
||||
$this->attrs[$name] = $value;
|
||||
}
|
||||
@@ -457,9 +439,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Overloaded getter for element's attribute.
|
||||
* @return mixed
|
||||
*/
|
||||
final public function &__get(string $name)
|
||||
final public function &__get(string $name): mixed
|
||||
{
|
||||
return $this->attrs[$name];
|
||||
}
|
||||
@@ -485,9 +466,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Overloaded setter for element's attribute.
|
||||
* @return mixed
|
||||
*/
|
||||
final public function __call(string $m, array $args)
|
||||
final public function __call(string $m, array $args): mixed
|
||||
{
|
||||
$p = substr($m, 0, 3);
|
||||
if ($p === 'get' || $p === 'set' || $p === 'add') {
|
||||
@@ -516,9 +496,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Special setter for element's attribute.
|
||||
* @return static
|
||||
*/
|
||||
final public function href(string $path, ?array $query = null)
|
||||
final public function href(string $path, array $query = []): static
|
||||
{
|
||||
if ($query) {
|
||||
$query = http_build_query($query, '', '&');
|
||||
@@ -534,10 +513,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Setter for data-* attributes. Booleans are converted to 'true' resp. 'false'.
|
||||
* @param mixed $value
|
||||
* @return static
|
||||
*/
|
||||
public function data(string $name, $value = null)
|
||||
public function data(string $name, mixed $value = null): static
|
||||
{
|
||||
if (func_num_args() === 1) {
|
||||
$this->attrs['data'] = $name;
|
||||
@@ -553,10 +530,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Sets element's HTML content.
|
||||
* @param HtmlStringable|string $html
|
||||
* @return static
|
||||
*/
|
||||
final public function setHtml($html)
|
||||
final public function setHtml(mixed $html): static
|
||||
{
|
||||
$this->children = [(string) $html];
|
||||
return $this;
|
||||
@@ -574,10 +549,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Sets element's textual content.
|
||||
* @param HtmlStringable|string|int|float $text
|
||||
* @return static
|
||||
*/
|
||||
final public function setText($text)
|
||||
final public function setText(mixed $text): static
|
||||
{
|
||||
if (!$text instanceof HtmlStringable) {
|
||||
$text = htmlspecialchars((string) $text, ENT_NOQUOTES, 'UTF-8');
|
||||
@@ -599,10 +572,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Adds new element's child.
|
||||
* @param HtmlStringable|string $child Html node or raw HTML string
|
||||
* @return static
|
||||
*/
|
||||
final public function addHtml($child)
|
||||
final public function addHtml(HtmlStringable|string $child): static
|
||||
{
|
||||
return $this->insert(null, $child);
|
||||
}
|
||||
@@ -610,10 +581,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Appends plain-text string to element content.
|
||||
* @param HtmlStringable|string|int|float $text
|
||||
* @return static
|
||||
*/
|
||||
public function addText($text)
|
||||
public function addText(\Stringable|string|int|null $text): static
|
||||
{
|
||||
if (!$text instanceof HtmlStringable) {
|
||||
$text = htmlspecialchars((string) $text, ENT_NOQUOTES, 'UTF-8');
|
||||
@@ -625,10 +594,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Creates and adds a new Html child.
|
||||
* @param array|string $attrs element's attributes or raw HTML string
|
||||
* @return static created element
|
||||
*/
|
||||
final public function create(string $name, $attrs = null)
|
||||
final public function create(string $name, array|string|null $attrs = null): static
|
||||
{
|
||||
$this->insert(null, $child = static::el($name, $attrs));
|
||||
return $child;
|
||||
@@ -637,10 +604,8 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
/**
|
||||
* Inserts child node.
|
||||
* @param HtmlStringable|string $child Html node or raw HTML string
|
||||
* @return static
|
||||
*/
|
||||
public function insert(?int $index, $child, bool $replace = false)
|
||||
public function insert(?int $index, HtmlStringable|string $child, bool $replace = false): static
|
||||
{
|
||||
$child = $child instanceof self ? $child : (string) $child;
|
||||
if ($index === null) { // append
|
||||
@@ -661,17 +626,15 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
*/
|
||||
final public function offsetSet($index, $child): void
|
||||
{
|
||||
$this->insert($index, $child, true);
|
||||
$this->insert($index, $child, replace: true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns child node (\ArrayAccess implementation).
|
||||
* @param int $index
|
||||
* @return HtmlStringable|string
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
final public function offsetGet($index)
|
||||
final public function offsetGet($index): HtmlStringable|string
|
||||
{
|
||||
return $this->children[$index];
|
||||
}
|
||||
@@ -771,16 +734,7 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
|
||||
final public function __toString(): string
|
||||
{
|
||||
try {
|
||||
return $this->render();
|
||||
} catch (\Throwable $e) {
|
||||
if (PHP_VERSION_ID >= 70400) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
trigger_error('Exception in ' . __METHOD__ . "(): {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}", E_USER_ERROR);
|
||||
return '';
|
||||
}
|
||||
return $this->render();
|
||||
}
|
||||
|
||||
|
||||
@@ -790,7 +744,7 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
final public function startTag(): string
|
||||
{
|
||||
return $this->name
|
||||
? '<' . $this->name . $this->attributes() . (static::$xhtml && $this->isEmpty ? ' />' : '>')
|
||||
? '<' . $this->name . $this->attributes() . '>'
|
||||
: '';
|
||||
}
|
||||
|
||||
@@ -821,11 +775,7 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
continue;
|
||||
|
||||
} elseif ($value === true) {
|
||||
if (static::$xhtml) {
|
||||
$s .= ' ' . $key . '="' . $key . '"';
|
||||
} else {
|
||||
$s .= ' ' . $key;
|
||||
}
|
||||
$s .= ' ' . $key;
|
||||
|
||||
continue;
|
||||
|
||||
@@ -857,14 +807,14 @@ class Html implements \ArrayAccess, \Countable, \IteratorAggregate, HtmlStringab
|
||||
$value = (string) $value;
|
||||
}
|
||||
|
||||
$q = strpos($value, '"') === false ? '"' : "'";
|
||||
$q = str_contains($value, '"') ? "'" : '"';
|
||||
$s .= ' ' . $key . '=' . $q
|
||||
. str_replace(
|
||||
['&', $q, '<'],
|
||||
['&', $q === '"' ? '"' : ''', self::$xhtml ? '<' : '<'],
|
||||
$value
|
||||
['&', $q === '"' ? '"' : ''', '<'],
|
||||
$value,
|
||||
)
|
||||
. (strpos($value, '`') !== false && strpbrk($value, ' <>"\'') === false ? ' ' : '')
|
||||
. (str_contains($value, '`') && strpbrk($value, ' <>"\'') === false ? ' ' : '')
|
||||
. $q;
|
||||
}
|
||||
|
||||
|
||||
527
vendor/nette/utils/src/Utils/Image.php
vendored
527
vendor/nette/utils/src/Utils/Image.php
vendored
@@ -10,6 +10,8 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function is_array, is_int, is_string;
|
||||
use const IMG_BMP, IMG_FLIP_BOTH, IMG_FLIP_HORIZONTAL, IMG_FLIP_VERTICAL, IMG_GIF, IMG_JPG, IMG_PNG, IMG_WEBP, PATHINFO_EXTENSION;
|
||||
|
||||
|
||||
/**
|
||||
@@ -22,116 +24,128 @@ use Nette;
|
||||
* $image->send();
|
||||
* </code>
|
||||
*
|
||||
* @method Image affine(array $affine, array $clip = null)
|
||||
* @method array affineMatrixConcat(array $m1, array $m2)
|
||||
* @method array affineMatrixGet(int $type, mixed $options = null)
|
||||
* @method void alphaBlending(bool $on)
|
||||
* @method void antialias(bool $on)
|
||||
* @method void arc($x, $y, $w, $h, $start, $end, $color)
|
||||
* @method void char(int $font, $x, $y, string $char, $color)
|
||||
* @method void charUp(int $font, $x, $y, string $char, $color)
|
||||
* @method int colorAllocate($red, $green, $blue)
|
||||
* @method int colorAllocateAlpha($red, $green, $blue, $alpha)
|
||||
* @method int colorAt($x, $y)
|
||||
* @method int colorClosest($red, $green, $blue)
|
||||
* @method int colorClosestAlpha($red, $green, $blue, $alpha)
|
||||
* @method int colorClosestHWB($red, $green, $blue)
|
||||
* @method void colorDeallocate($color)
|
||||
* @method int colorExact($red, $green, $blue)
|
||||
* @method int colorExactAlpha($red, $green, $blue, $alpha)
|
||||
* @method Image affine(array $affine, ?array $clip = null)
|
||||
* @method void alphaBlending(bool $enable)
|
||||
* @method void antialias(bool $enable)
|
||||
* @method void arc(int $centerX, int $centerY, int $width, int $height, int $startAngle, int $endAngle, ImageColor $color)
|
||||
* @method int colorAllocate(int $red, int $green, int $blue)
|
||||
* @method int colorAllocateAlpha(int $red, int $green, int $blue, int $alpha)
|
||||
* @method int colorAt(int $x, int $y)
|
||||
* @method int colorClosest(int $red, int $green, int $blue)
|
||||
* @method int colorClosestAlpha(int $red, int $green, int $blue, int $alpha)
|
||||
* @method int colorClosestHWB(int $red, int $green, int $blue)
|
||||
* @method void colorDeallocate(int $color)
|
||||
* @method int colorExact(int $red, int $green, int $blue)
|
||||
* @method int colorExactAlpha(int $red, int $green, int $blue, int $alpha)
|
||||
* @method void colorMatch(Image $image2)
|
||||
* @method int colorResolve($red, $green, $blue)
|
||||
* @method int colorResolveAlpha($red, $green, $blue, $alpha)
|
||||
* @method void colorSet($index, $red, $green, $blue)
|
||||
* @method array colorsForIndex($index)
|
||||
* @method int colorResolve(int $red, int $green, int $blue)
|
||||
* @method int colorResolveAlpha(int $red, int $green, int $blue, int $alpha)
|
||||
* @method void colorSet(int $index, int $red, int $green, int $blue, int $alpha = 0)
|
||||
* @method array colorsForIndex(int $color)
|
||||
* @method int colorsTotal()
|
||||
* @method int colorTransparent($color = null)
|
||||
* @method int colorTransparent(?int $color = null)
|
||||
* @method void convolution(array $matrix, float $div, float $offset)
|
||||
* @method void copy(Image $src, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH)
|
||||
* @method void copyMerge(Image $src, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH, $opacity)
|
||||
* @method void copyMergeGray(Image $src, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH, $opacity)
|
||||
* @method void copyResampled(Image $src, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH)
|
||||
* @method void copyResized(Image $src, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH)
|
||||
* @method Image cropAuto(int $mode = -1, float $threshold = .5, int $color = -1)
|
||||
* @method void ellipse($cx, $cy, $w, $h, $color)
|
||||
* @method void fill($x, $y, $color)
|
||||
* @method void filledArc($cx, $cy, $w, $h, $s, $e, $color, $style)
|
||||
* @method void filledEllipse($cx, $cy, $w, $h, $color)
|
||||
* @method void filledPolygon(array $points, $numPoints, $color)
|
||||
* @method void filledRectangle($x1, $y1, $x2, $y2, $color)
|
||||
* @method void fillToBorder($x, $y, $border, $color)
|
||||
* @method void filter($filtertype)
|
||||
* @method void copy(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $srcW, int $srcH)
|
||||
* @method void copyMerge(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $srcW, int $srcH, int $pct)
|
||||
* @method void copyMergeGray(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $srcW, int $srcH, int $pct)
|
||||
* @method void copyResampled(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $dstW, int $dstH, int $srcW, int $srcH)
|
||||
* @method void copyResized(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $dstW, int $dstH, int $srcW, int $srcH)
|
||||
* @method Image cropAuto(int $mode = IMG_CROP_DEFAULT, float $threshold = .5, ?ImageColor $color = null)
|
||||
* @method void ellipse(int $centerX, int $centerY, int $width, int $height, ImageColor $color)
|
||||
* @method void fill(int $x, int $y, ImageColor $color)
|
||||
* @method void filledArc(int $centerX, int $centerY, int $width, int $height, int $startAngle, int $endAngle, ImageColor $color, int $style)
|
||||
* @method void filledEllipse(int $centerX, int $centerY, int $width, int $height, ImageColor $color)
|
||||
* @method void filledPolygon(array $points, ImageColor $color)
|
||||
* @method void filledRectangle(int $x1, int $y1, int $x2, int $y2, ImageColor $color)
|
||||
* @method void fillToBorder(int $x, int $y, ImageColor $borderColor, ImageColor $color)
|
||||
* @method void filter(int $filter, ...$args)
|
||||
* @method void flip(int $mode)
|
||||
* @method array ftText($size, $angle, $x, $y, $col, string $fontFile, string $text, array $extrainfo = null)
|
||||
* @method array ftText(float $size, float $angle, int $x, int $y, ImageColor $color, string $fontFile, string $text, array $options = [])
|
||||
* @method void gammaCorrect(float $inputgamma, float $outputgamma)
|
||||
* @method array getClip()
|
||||
* @method int interlace($interlace = null)
|
||||
* @method int getInterpolation()
|
||||
* @method int interlace(?bool $enable = null)
|
||||
* @method bool isTrueColor()
|
||||
* @method void layerEffect($effect)
|
||||
* @method void line($x1, $y1, $x2, $y2, $color)
|
||||
* @method void openPolygon(array $points, int $num_points, int $color)
|
||||
* @method void layerEffect(int $effect)
|
||||
* @method void line(int $x1, int $y1, int $x2, int $y2, ImageColor $color)
|
||||
* @method void openPolygon(array $points, ImageColor $color)
|
||||
* @method void paletteCopy(Image $source)
|
||||
* @method void paletteToTrueColor()
|
||||
* @method void polygon(array $points, $numPoints, $color)
|
||||
* @method array psText(string $text, $font, $size, $color, $backgroundColor, $x, $y, $space = null, $tightness = null, float $angle = null, $antialiasSteps = null)
|
||||
* @method void rectangle($x1, $y1, $x2, $y2, $col)
|
||||
* @method mixed resolution(int $res_x = null, int $res_y = null)
|
||||
* @method Image rotate(float $angle, $backgroundColor)
|
||||
* @method void saveAlpha(bool $saveflag)
|
||||
* @method void polygon(array $points, ImageColor $color)
|
||||
* @method void rectangle(int $x1, int $y1, int $x2, int $y2, ImageColor $color)
|
||||
* @method mixed resolution(?int $resolutionX = null, ?int $resolutionY = null)
|
||||
* @method Image rotate(float $angle, ImageColor $backgroundColor)
|
||||
* @method void saveAlpha(bool $enable)
|
||||
* @method Image scale(int $newWidth, int $newHeight = -1, int $mode = IMG_BILINEAR_FIXED)
|
||||
* @method void setBrush(Image $brush)
|
||||
* @method void setClip(int $x1, int $y1, int $x2, int $y2)
|
||||
* @method void setInterpolation(int $method = IMG_BILINEAR_FIXED)
|
||||
* @method void setPixel($x, $y, $color)
|
||||
* @method void setPixel(int $x, int $y, ImageColor $color)
|
||||
* @method void setStyle(array $style)
|
||||
* @method void setThickness($thickness)
|
||||
* @method void setThickness(int $thickness)
|
||||
* @method void setTile(Image $tile)
|
||||
* @method void string($font, $x, $y, string $s, $col)
|
||||
* @method void stringUp($font, $x, $y, string $s, $col)
|
||||
* @method void trueColorToPalette(bool $dither, $ncolors)
|
||||
* @method array ttfText($size, $angle, $x, $y, $color, string $fontfile, string $text)
|
||||
* @property-read int $width
|
||||
* @property-read int $height
|
||||
* @property-read resource|\GdImage $imageResource
|
||||
* @method void trueColorToPalette(bool $dither, int $ncolors)
|
||||
* @method array ttfText(float $size, float $angle, int $x, int $y, ImageColor $color, string $fontfile, string $text, array $options = [])
|
||||
* @property-read positive-int $width
|
||||
* @property-read positive-int $height
|
||||
* @property-read \GdImage $imageResource
|
||||
*/
|
||||
class Image
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** {@link resize()} only shrinks images */
|
||||
public const SHRINK_ONLY = 0b0001;
|
||||
/** Prevent from getting resized to a bigger size than the original */
|
||||
public const ShrinkOnly = 0b0001;
|
||||
|
||||
/** {@link resize()} will ignore aspect ratio */
|
||||
public const STRETCH = 0b0010;
|
||||
/** Resizes to a specified width and height without keeping aspect ratio */
|
||||
public const Stretch = 0b0010;
|
||||
|
||||
/** {@link resize()} fits in given area so its dimensions are less than or equal to the required dimensions */
|
||||
public const FIT = 0b0000;
|
||||
/** Resizes to fit into a specified width and height and preserves aspect ratio */
|
||||
public const OrSmaller = 0b0000;
|
||||
|
||||
/** {@link resize()} fills given area so its dimensions are greater than or equal to the required dimensions */
|
||||
public const FILL = 0b0100;
|
||||
/** Resizes while bounding the smaller dimension to the specified width or height and preserves aspect ratio */
|
||||
public const OrBigger = 0b0100;
|
||||
|
||||
/** {@link resize()} fills given area exactly */
|
||||
public const EXACT = 0b1000;
|
||||
/** Resizes to the smallest possible size to completely cover specified width and height and reserves aspect ratio */
|
||||
public const Cover = 0b1000;
|
||||
|
||||
/** @deprecated use Image::ShrinkOnly */
|
||||
public const SHRINK_ONLY = self::ShrinkOnly;
|
||||
|
||||
/** @deprecated use Image::Stretch */
|
||||
public const STRETCH = self::Stretch;
|
||||
|
||||
/** @deprecated use Image::OrSmaller */
|
||||
public const FIT = self::OrSmaller;
|
||||
|
||||
/** @deprecated use Image::OrBigger */
|
||||
public const FILL = self::OrBigger;
|
||||
|
||||
/** @deprecated use Image::Cover */
|
||||
public const EXACT = self::Cover;
|
||||
|
||||
/** @deprecated use Image::EmptyGIF */
|
||||
public const EMPTY_GIF = self::EmptyGIF;
|
||||
|
||||
/** image types */
|
||||
public const
|
||||
JPEG = IMAGETYPE_JPEG,
|
||||
PNG = IMAGETYPE_PNG,
|
||||
GIF = IMAGETYPE_GIF,
|
||||
WEBP = IMAGETYPE_WEBP,
|
||||
AVIF = 19, // IMAGETYPE_AVIF,
|
||||
BMP = IMAGETYPE_BMP;
|
||||
JPEG = ImageType::JPEG,
|
||||
PNG = ImageType::PNG,
|
||||
GIF = ImageType::GIF,
|
||||
WEBP = ImageType::WEBP,
|
||||
AVIF = ImageType::AVIF,
|
||||
BMP = ImageType::BMP;
|
||||
|
||||
public const EMPTY_GIF = "GIF89a\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00!\xf9\x04\x01\x00\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;";
|
||||
public const EmptyGIF = "GIF89a\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00!\xf9\x04\x01\x00\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;";
|
||||
|
||||
private const Formats = [self::JPEG => 'jpeg', self::PNG => 'png', self::GIF => 'gif', self::WEBP => 'webp', self::AVIF => 'avif', self::BMP => 'bmp'];
|
||||
private const Formats = [ImageType::JPEG => 'jpeg', ImageType::PNG => 'png', ImageType::GIF => 'gif', ImageType::WEBP => 'webp', ImageType::AVIF => 'avif', ImageType::BMP => 'bmp'];
|
||||
|
||||
/** @var resource|\GdImage */
|
||||
private $image;
|
||||
private \GdImage $image;
|
||||
|
||||
|
||||
/**
|
||||
* Returns RGB color (0..255) and transparency (0..127).
|
||||
* @deprecated use ImageColor::rgb()
|
||||
*/
|
||||
public static function rgb(int $red, int $green, int $blue, int $transparency = 0): array
|
||||
{
|
||||
@@ -148,14 +162,10 @@ class Image
|
||||
* Reads an image from a file and returns its type in $type.
|
||||
* @throws Nette\NotSupportedException if gd extension is not loaded
|
||||
* @throws UnknownImageFileException if file not found or file type is not known
|
||||
* @return static
|
||||
*/
|
||||
public static function fromFile(string $file, ?int &$type = null)
|
||||
public static function fromFile(string $file, ?int &$type = null): static
|
||||
{
|
||||
if (!extension_loaded('gd')) {
|
||||
throw new Nette\NotSupportedException('PHP extension GD is not loaded.');
|
||||
}
|
||||
|
||||
self::ensureExtension();
|
||||
$type = self::detectTypeFromFile($file);
|
||||
if (!$type) {
|
||||
throw new UnknownImageFileException(is_file($file) ? "Unknown type of file '$file'." : "File '$file' not found.");
|
||||
@@ -167,16 +177,12 @@ class Image
|
||||
|
||||
/**
|
||||
* Reads an image from a string and returns its type in $type.
|
||||
* @return static
|
||||
* @throws Nette\NotSupportedException if gd extension is not loaded
|
||||
* @throws ImageException
|
||||
*/
|
||||
public static function fromString(string $s, ?int &$type = null)
|
||||
public static function fromString(string $s, ?int &$type = null): static
|
||||
{
|
||||
if (!extension_loaded('gd')) {
|
||||
throw new Nette\NotSupportedException('PHP extension GD is not loaded.');
|
||||
}
|
||||
|
||||
self::ensureExtension();
|
||||
$type = self::detectTypeFromString($s);
|
||||
if (!$type) {
|
||||
throw new UnknownImageFileException('Unknown type of image.');
|
||||
@@ -186,7 +192,7 @@ class Image
|
||||
}
|
||||
|
||||
|
||||
private static function invokeSafe(string $func, string $arg, string $message, string $callee): self
|
||||
private static function invokeSafe(string $func, string $arg, string $message, string $callee): static
|
||||
{
|
||||
$errors = [];
|
||||
$res = Callback::invokeSafe($func, [$arg], function (string $message) use (&$errors): void {
|
||||
@@ -205,54 +211,54 @@ class Image
|
||||
|
||||
/**
|
||||
* Creates a new true color image of the given dimensions. The default color is black.
|
||||
* @return static
|
||||
* @param positive-int $width
|
||||
* @param positive-int $height
|
||||
* @throws Nette\NotSupportedException if gd extension is not loaded
|
||||
*/
|
||||
public static function fromBlank(int $width, int $height, ?array $color = null)
|
||||
public static function fromBlank(int $width, int $height, ImageColor|array|null $color = null): static
|
||||
{
|
||||
if (!extension_loaded('gd')) {
|
||||
throw new Nette\NotSupportedException('PHP extension GD is not loaded.');
|
||||
}
|
||||
|
||||
self::ensureExtension();
|
||||
if ($width < 1 || $height < 1) {
|
||||
throw new Nette\InvalidArgumentException('Image width and height must be greater than zero.');
|
||||
}
|
||||
|
||||
$image = imagecreatetruecolor($width, $height);
|
||||
$image = new static(imagecreatetruecolor($width, $height));
|
||||
if ($color) {
|
||||
$color += ['alpha' => 0];
|
||||
$color = imagecolorresolvealpha($image, $color['red'], $color['green'], $color['blue'], $color['alpha']);
|
||||
imagealphablending($image, false);
|
||||
imagefilledrectangle($image, 0, 0, $width - 1, $height - 1, $color);
|
||||
imagealphablending($image, true);
|
||||
$image->alphablending(false);
|
||||
$image->filledrectangle(0, 0, $width - 1, $height - 1, $color);
|
||||
$image->alphablending(true);
|
||||
}
|
||||
|
||||
return new static($image);
|
||||
return $image;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type of image from file.
|
||||
* @return ImageType::*|null
|
||||
*/
|
||||
public static function detectTypeFromFile(string $file, &$width = null, &$height = null): ?int
|
||||
{
|
||||
[$width, $height, $type] = @getimagesize($file); // @ - files smaller than 12 bytes causes read error
|
||||
return isset(self::Formats[$type]) ? $type : null;
|
||||
[$width, $height, $type] = Helpers::falseToNull(@getimagesize($file)); // @ - files smaller than 12 bytes causes read error
|
||||
return $type && isset(self::Formats[$type]) ? $type : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type of image from string.
|
||||
* @return ImageType::*|null
|
||||
*/
|
||||
public static function detectTypeFromString(string $s, &$width = null, &$height = null): ?int
|
||||
{
|
||||
[$width, $height, $type] = @getimagesizefromstring($s); // @ - strings smaller than 12 bytes causes read error
|
||||
return isset(self::Formats[$type]) ? $type : null;
|
||||
[$width, $height, $type] = Helpers::falseToNull(@getimagesizefromstring($s)); // @ - strings smaller than 12 bytes causes read error
|
||||
return $type && isset(self::Formats[$type]) ? $type : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the file extension for the given `Image::XXX` constant.
|
||||
* Returns the file extension for the given image type.
|
||||
* @param ImageType::* $type
|
||||
* @return value-of<self::Formats>
|
||||
*/
|
||||
public static function typeToExtension(int $type): string
|
||||
{
|
||||
@@ -265,11 +271,12 @@ class Image
|
||||
|
||||
|
||||
/**
|
||||
* Returns the `Image::XXX` constant for given file extension.
|
||||
* Returns the image type for given file extension.
|
||||
* @return ImageType::*
|
||||
*/
|
||||
public static function extensionToType(string $extension): int
|
||||
{
|
||||
$extensions = array_flip(self::Formats) + ['jpg' => self::JPEG];
|
||||
$extensions = array_flip(self::Formats) + ['jpg' => ImageType::JPEG];
|
||||
$extension = strtolower($extension);
|
||||
if (!isset($extensions[$extension])) {
|
||||
throw new Nette\InvalidArgumentException("Unsupported file extension '$extension'.");
|
||||
@@ -280,7 +287,8 @@ class Image
|
||||
|
||||
|
||||
/**
|
||||
* Returns the mime type for the given `Image::XXX` constant.
|
||||
* Returns the mime type for the given image type.
|
||||
* @param ImageType::* $type
|
||||
*/
|
||||
public static function typeToMimeType(int $type): string
|
||||
{
|
||||
@@ -289,10 +297,43 @@ class Image
|
||||
|
||||
|
||||
/**
|
||||
* Wraps GD image.
|
||||
* @param resource|\GdImage $image
|
||||
* @param ImageType::* $type
|
||||
*/
|
||||
public function __construct($image)
|
||||
public static function isTypeSupported(int $type): bool
|
||||
{
|
||||
self::ensureExtension();
|
||||
return (bool) (imagetypes() & match ($type) {
|
||||
ImageType::JPEG => IMG_JPG,
|
||||
ImageType::PNG => IMG_PNG,
|
||||
ImageType::GIF => IMG_GIF,
|
||||
ImageType::WEBP => IMG_WEBP,
|
||||
ImageType::AVIF => 256, // IMG_AVIF,
|
||||
ImageType::BMP => IMG_BMP,
|
||||
default => 0,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/** @return ImageType[] */
|
||||
public static function getSupportedTypes(): array
|
||||
{
|
||||
self::ensureExtension();
|
||||
$flag = imagetypes();
|
||||
return array_filter([
|
||||
$flag & IMG_GIF ? ImageType::GIF : null,
|
||||
$flag & IMG_JPG ? ImageType::JPEG : null,
|
||||
$flag & IMG_PNG ? ImageType::PNG : null,
|
||||
$flag & IMG_WEBP ? ImageType::WEBP : null,
|
||||
$flag & 256 ? ImageType::AVIF : null, // IMG_AVIF
|
||||
$flag & IMG_BMP ? ImageType::BMP : null,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wraps GD image.
|
||||
*/
|
||||
public function __construct(\GdImage $image)
|
||||
{
|
||||
$this->setImageResource($image);
|
||||
imagesavealpha($image, true);
|
||||
@@ -301,6 +342,7 @@ class Image
|
||||
|
||||
/**
|
||||
* Returns image width.
|
||||
* @return positive-int
|
||||
*/
|
||||
public function getWidth(): int
|
||||
{
|
||||
@@ -310,6 +352,7 @@ class Image
|
||||
|
||||
/**
|
||||
* Returns image height.
|
||||
* @return positive-int
|
||||
*/
|
||||
public function getHeight(): int
|
||||
{
|
||||
@@ -319,15 +362,9 @@ class Image
|
||||
|
||||
/**
|
||||
* Sets image resource.
|
||||
* @param resource|\GdImage $image
|
||||
* @return static
|
||||
*/
|
||||
protected function setImageResource($image)
|
||||
protected function setImageResource(\GdImage $image): static
|
||||
{
|
||||
if (!$image instanceof \GdImage && !(is_resource($image) && get_resource_type($image) === 'gd')) {
|
||||
throw new Nette\InvalidArgumentException('Image is not valid.');
|
||||
}
|
||||
|
||||
$this->image = $image;
|
||||
return $this;
|
||||
}
|
||||
@@ -335,30 +372,27 @@ class Image
|
||||
|
||||
/**
|
||||
* Returns image GD resource.
|
||||
* @return resource|\GdImage
|
||||
*/
|
||||
public function getImageResource()
|
||||
public function getImageResource(): \GdImage
|
||||
{
|
||||
return $this->image;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scales an image.
|
||||
* @param int|string|null $width in pixels or percent
|
||||
* @param int|string|null $height in pixels or percent
|
||||
* @return static
|
||||
* Scales an image. Width and height accept pixels or percent.
|
||||
* @param int-mask-of<self::OrSmaller|self::OrBigger|self::Stretch|self::Cover|self::ShrinkOnly> $mode
|
||||
*/
|
||||
public function resize($width, $height, int $flags = self::FIT)
|
||||
public function resize(int|string|null $width, int|string|null $height, int $mode = self::OrSmaller): static
|
||||
{
|
||||
if ($flags & self::EXACT) {
|
||||
return $this->resize($width, $height, self::FILL)->crop('50%', '50%', $width, $height);
|
||||
if ($mode & self::Cover) {
|
||||
return $this->resize($width, $height, self::OrBigger)->crop('50%', '50%', $width, $height);
|
||||
}
|
||||
|
||||
[$newWidth, $newHeight] = static::calculateSize($this->getWidth(), $this->getHeight(), $width, $height, $flags);
|
||||
[$newWidth, $newHeight] = static::calculateSize($this->getWidth(), $this->getHeight(), $width, $height, $mode);
|
||||
|
||||
if ($newWidth !== $this->getWidth() || $newHeight !== $this->getHeight()) { // resize
|
||||
$newImage = static::fromBlank($newWidth, $newHeight, self::rgb(0, 0, 0, 127))->getImageResource();
|
||||
$newImage = static::fromBlank($newWidth, $newHeight, ImageColor::rgb(0, 0, 0, 0))->getImageResource();
|
||||
imagecopyresampled(
|
||||
$newImage,
|
||||
$this->image,
|
||||
@@ -369,7 +403,7 @@ class Image
|
||||
$newWidth,
|
||||
$newHeight,
|
||||
$this->getWidth(),
|
||||
$this->getHeight()
|
||||
$this->getHeight(),
|
||||
);
|
||||
$this->image = $newImage;
|
||||
}
|
||||
@@ -383,17 +417,17 @@ class Image
|
||||
|
||||
|
||||
/**
|
||||
* Calculates dimensions of resized image.
|
||||
* @param int|string|null $newWidth in pixels or percent
|
||||
* @param int|string|null $newHeight in pixels or percent
|
||||
* Calculates dimensions of resized image. Width and height accept pixels or percent.
|
||||
* @param int-mask-of<self::OrSmaller|self::OrBigger|self::Stretch|self::Cover|self::ShrinkOnly> $mode
|
||||
*/
|
||||
public static function calculateSize(
|
||||
int $srcWidth,
|
||||
int $srcHeight,
|
||||
$newWidth,
|
||||
$newHeight,
|
||||
int $flags = self::FIT
|
||||
): array {
|
||||
int $mode = self::OrSmaller,
|
||||
): array
|
||||
{
|
||||
if ($newWidth === null) {
|
||||
} elseif (self::isPercent($newWidth)) {
|
||||
$newWidth = (int) round($srcWidth / 100 * abs($newWidth));
|
||||
@@ -405,19 +439,19 @@ class Image
|
||||
if ($newHeight === null) {
|
||||
} elseif (self::isPercent($newHeight)) {
|
||||
$newHeight = (int) round($srcHeight / 100 * abs($newHeight));
|
||||
$flags |= empty($percents) ? 0 : self::STRETCH;
|
||||
$mode |= empty($percents) ? 0 : self::Stretch;
|
||||
} else {
|
||||
$newHeight = abs($newHeight);
|
||||
}
|
||||
|
||||
if ($flags & self::STRETCH) { // non-proportional
|
||||
if ($mode & self::Stretch) { // non-proportional
|
||||
if (!$newWidth || !$newHeight) {
|
||||
throw new Nette\InvalidArgumentException('For stretching must be both width and height specified.');
|
||||
}
|
||||
|
||||
if ($flags & self::SHRINK_ONLY) {
|
||||
$newWidth = (int) round($srcWidth * min(1, $newWidth / $srcWidth));
|
||||
$newHeight = (int) round($srcHeight * min(1, $newHeight / $srcHeight));
|
||||
if ($mode & self::ShrinkOnly) {
|
||||
$newWidth = min($srcWidth, $newWidth);
|
||||
$newHeight = min($srcHeight, $newHeight);
|
||||
}
|
||||
} else { // proportional
|
||||
if (!$newWidth && !$newHeight) {
|
||||
@@ -433,11 +467,11 @@ class Image
|
||||
$scale[] = $newHeight / $srcHeight;
|
||||
}
|
||||
|
||||
if ($flags & self::FILL) {
|
||||
if ($mode & self::OrBigger) {
|
||||
$scale = [max($scale)];
|
||||
}
|
||||
|
||||
if ($flags & self::SHRINK_ONLY) {
|
||||
if ($mode & self::ShrinkOnly) {
|
||||
$scale[] = 1;
|
||||
}
|
||||
|
||||
@@ -451,14 +485,9 @@ class Image
|
||||
|
||||
|
||||
/**
|
||||
* Crops image.
|
||||
* @param int|string $left in pixels or percent
|
||||
* @param int|string $top in pixels or percent
|
||||
* @param int|string $width in pixels or percent
|
||||
* @param int|string $height in pixels or percent
|
||||
* @return static
|
||||
* Crops image. Arguments accepts pixels or percent.
|
||||
*/
|
||||
public function crop($left, $top, $width, $height)
|
||||
public function crop(int|string $left, int|string $top, int|string $width, int|string $height): static
|
||||
{
|
||||
[$r['x'], $r['y'], $r['width'], $r['height']]
|
||||
= static::calculateCutout($this->getWidth(), $this->getHeight(), $left, $top, $width, $height);
|
||||
@@ -466,7 +495,7 @@ class Image
|
||||
$this->image = imagecrop($this->image, $r);
|
||||
imagesavealpha($this->image, true);
|
||||
} else {
|
||||
$newImage = static::fromBlank($r['width'], $r['height'], self::RGB(0, 0, 0, 127))->getImageResource();
|
||||
$newImage = static::fromBlank($r['width'], $r['height'], ImageColor::rgb(0, 0, 0, 0))->getImageResource();
|
||||
imagecopy($newImage, $this->image, 0, 0, $r['x'], $r['y'], $r['width'], $r['height']);
|
||||
$this->image = $newImage;
|
||||
}
|
||||
@@ -476,13 +505,16 @@ class Image
|
||||
|
||||
|
||||
/**
|
||||
* Calculates dimensions of cutout in image.
|
||||
* @param int|string $left in pixels or percent
|
||||
* @param int|string $top in pixels or percent
|
||||
* @param int|string $newWidth in pixels or percent
|
||||
* @param int|string $newHeight in pixels or percent
|
||||
* Calculates dimensions of cutout in image. Arguments accepts pixels or percent.
|
||||
*/
|
||||
public static function calculateCutout(int $srcWidth, int $srcHeight, $left, $top, $newWidth, $newHeight): array
|
||||
public static function calculateCutout(
|
||||
int $srcWidth,
|
||||
int $srcHeight,
|
||||
int|string $left,
|
||||
int|string $top,
|
||||
int|string $newWidth,
|
||||
int|string $newHeight,
|
||||
): array
|
||||
{
|
||||
if (self::isPercent($newWidth)) {
|
||||
$newWidth = (int) round($srcWidth / 100 * $newWidth);
|
||||
@@ -518,9 +550,8 @@ class Image
|
||||
|
||||
/**
|
||||
* Sharpens image a little bit.
|
||||
* @return static
|
||||
*/
|
||||
public function sharpen()
|
||||
public function sharpen(): static
|
||||
{
|
||||
imageconvolution($this->image, [ // my magic numbers ;)
|
||||
[-1, -1, -1],
|
||||
@@ -532,13 +563,10 @@ class Image
|
||||
|
||||
|
||||
/**
|
||||
* Puts another image into this image.
|
||||
* @param int|string $left in pixels or percent
|
||||
* @param int|string $top in pixels or percent
|
||||
* @param int $opacity 0..100
|
||||
* @return static
|
||||
* Puts another image into this image. Left and top accepts pixels or percent.
|
||||
* @param int<0, 100> $opacity 0..100
|
||||
*/
|
||||
public function place(self $image, $left = 0, $top = 0, int $opacity = 100)
|
||||
public function place(self $image, int|string $left = 0, int|string $top = 0, int $opacity = 100): static
|
||||
{
|
||||
$opacity = max(0, min(100, $opacity));
|
||||
if ($opacity === 0) {
|
||||
@@ -590,29 +618,75 @@ class Image
|
||||
0,
|
||||
0,
|
||||
$width,
|
||||
$height
|
||||
$height,
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the bounding box for a TrueType text. Returns keys left, top, width and height.
|
||||
*/
|
||||
public static function calculateTextBox(
|
||||
string $text,
|
||||
string $fontFile,
|
||||
float $size,
|
||||
float $angle = 0,
|
||||
array $options = [],
|
||||
): array
|
||||
{
|
||||
self::ensureExtension();
|
||||
$box = imagettfbbox($size, $angle, $fontFile, $text, $options);
|
||||
return [
|
||||
'left' => $minX = min([$box[0], $box[2], $box[4], $box[6]]),
|
||||
'top' => $minY = min([$box[1], $box[3], $box[5], $box[7]]),
|
||||
'width' => max([$box[0], $box[2], $box[4], $box[6]]) - $minX + 1,
|
||||
'height' => max([$box[1], $box[3], $box[5], $box[7]]) - $minY + 1,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Draw a rectangle.
|
||||
*/
|
||||
public function rectangleWH(int $x, int $y, int $width, int $height, ImageColor $color): void
|
||||
{
|
||||
if ($width !== 0 && $height !== 0) {
|
||||
$this->rectangle($x, $y, $x + $width + ($width > 0 ? -1 : 1), $y + $height + ($height > 0 ? -1 : 1), $color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Draw a filled rectangle.
|
||||
*/
|
||||
public function filledRectangleWH(int $x, int $y, int $width, int $height, ImageColor $color): void
|
||||
{
|
||||
if ($width !== 0 && $height !== 0) {
|
||||
$this->filledRectangle($x, $y, $x + $width + ($width > 0 ? -1 : 1), $y + $height + ($height > 0 ? -1 : 1), $color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Saves image to the file. Quality is in the range 0..100 for JPEG (default 85), WEBP (default 80) and AVIF (default 30) and 0..9 for PNG (default 9).
|
||||
* @param ImageType::*|null $type
|
||||
* @throws ImageException
|
||||
*/
|
||||
public function save(string $file, ?int $quality = null, ?int $type = null): void
|
||||
{
|
||||
$type = $type ?? self::extensionToType(pathinfo($file, PATHINFO_EXTENSION));
|
||||
$type ??= self::extensionToType(pathinfo($file, PATHINFO_EXTENSION));
|
||||
$this->output($type, $quality, $file);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Outputs image to string. Quality is in the range 0..100 for JPEG (default 85), WEBP (default 80) and AVIF (default 30) and 0..9 for PNG (default 9).
|
||||
* @param ImageType::* $type
|
||||
*/
|
||||
public function toString(int $type = self::JPEG, ?int $quality = null): string
|
||||
public function toString(int $type = ImageType::JPEG, ?int $quality = null): string
|
||||
{
|
||||
return Helpers::capture(function () use ($type, $quality) {
|
||||
return Helpers::capture(function () use ($type, $quality): void {
|
||||
$this->output($type, $quality);
|
||||
});
|
||||
}
|
||||
@@ -623,24 +697,16 @@ class Image
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
try {
|
||||
return $this->toString();
|
||||
} catch (\Throwable $e) {
|
||||
if (func_num_args() || PHP_VERSION_ID >= 70400) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
trigger_error('Exception in ' . __METHOD__ . "(): {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}", E_USER_ERROR);
|
||||
return '';
|
||||
}
|
||||
return $this->toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Outputs image to browser. Quality is in the range 0..100 for JPEG (default 85), WEBP (default 80) and AVIF (default 30) and 0..9 for PNG (default 9).
|
||||
* @param ImageType::* $type
|
||||
* @throws ImageException
|
||||
*/
|
||||
public function send(int $type = self::JPEG, ?int $quality = null): void
|
||||
public function send(int $type = ImageType::JPEG, ?int $quality = null): void
|
||||
{
|
||||
header('Content-Type: ' . self::typeToMimeType($type));
|
||||
$this->output($type, $quality);
|
||||
@@ -649,55 +715,40 @@ class Image
|
||||
|
||||
/**
|
||||
* Outputs image to browser or file.
|
||||
* @param ImageType::* $type
|
||||
* @throws ImageException
|
||||
*/
|
||||
private function output(int $type, ?int $quality, ?string $file = null): void
|
||||
{
|
||||
switch ($type) {
|
||||
case self::JPEG:
|
||||
$quality = $quality === null ? 85 : max(0, min(100, $quality));
|
||||
$success = @imagejpeg($this->image, $file, $quality); // @ is escalated to exception
|
||||
break;
|
||||
[$defQuality, $min, $max] = match ($type) {
|
||||
ImageType::JPEG => [85, 0, 100],
|
||||
ImageType::PNG => [9, 0, 9],
|
||||
ImageType::GIF => [null, null, null],
|
||||
ImageType::WEBP => [80, 0, 100],
|
||||
ImageType::AVIF => [30, 0, 100],
|
||||
ImageType::BMP => [null, null, null],
|
||||
default => throw new Nette\InvalidArgumentException("Unsupported image type '$type'."),
|
||||
};
|
||||
|
||||
case self::PNG:
|
||||
$quality = $quality === null ? 9 : max(0, min(9, $quality));
|
||||
$success = @imagepng($this->image, $file, $quality); // @ is escalated to exception
|
||||
break;
|
||||
|
||||
case self::GIF:
|
||||
$success = @imagegif($this->image, $file); // @ is escalated to exception
|
||||
break;
|
||||
|
||||
case self::WEBP:
|
||||
$quality = $quality === null ? 80 : max(0, min(100, $quality));
|
||||
$success = @imagewebp($this->image, $file, $quality); // @ is escalated to exception
|
||||
break;
|
||||
|
||||
case self::AVIF:
|
||||
$quality = $quality === null ? 30 : max(0, min(100, $quality));
|
||||
$success = @imageavif($this->image, $file, $quality); // @ is escalated to exception
|
||||
break;
|
||||
|
||||
case self::BMP:
|
||||
$success = @imagebmp($this->image, $file); // @ is escalated to exception
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Nette\InvalidArgumentException("Unsupported image type '$type'.");
|
||||
$args = [$this->image, $file];
|
||||
if ($defQuality !== null) {
|
||||
$args[] = $quality === null ? $defQuality : max($min, min($max, $quality));
|
||||
}
|
||||
|
||||
if (!$success) {
|
||||
throw new ImageException(Helpers::getLastError() ?: 'Unknown error');
|
||||
}
|
||||
Callback::invokeSafe('image' . self::Formats[$type], $args, function (string $message) use ($file): void {
|
||||
if ($file !== null) {
|
||||
@unlink($file);
|
||||
}
|
||||
throw new ImageException($message);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call to undefined method.
|
||||
* @return mixed
|
||||
* @throws Nette\MemberAccessException
|
||||
*/
|
||||
public function __call(string $name, array $args)
|
||||
public function __call(string $name, array $args): mixed
|
||||
{
|
||||
$function = 'image' . $name;
|
||||
if (!function_exists($function)) {
|
||||
@@ -708,25 +759,13 @@ class Image
|
||||
if ($value instanceof self) {
|
||||
$args[$key] = $value->getImageResource();
|
||||
|
||||
} elseif (is_array($value) && isset($value['red'])) { // rgb
|
||||
$args[$key] = imagecolorallocatealpha(
|
||||
$this->image,
|
||||
$value['red'],
|
||||
$value['green'],
|
||||
$value['blue'],
|
||||
$value['alpha']
|
||||
) ?: imagecolorresolvealpha(
|
||||
$this->image,
|
||||
$value['red'],
|
||||
$value['green'],
|
||||
$value['blue'],
|
||||
$value['alpha']
|
||||
);
|
||||
} elseif ($value instanceof ImageColor || (is_array($value) && isset($value['red']))) {
|
||||
$args[$key] = $this->resolveColor($value);
|
||||
}
|
||||
}
|
||||
|
||||
$res = $function($this->image, ...$args);
|
||||
return $res instanceof \GdImage || (is_resource($res) && get_resource_type($res) === 'gd')
|
||||
return $res instanceof \GdImage
|
||||
? $this->setImageResource($res)
|
||||
: $res;
|
||||
}
|
||||
@@ -734,18 +773,15 @@ class Image
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
ob_start(function () {});
|
||||
ob_start(fn() => '');
|
||||
imagepng($this->image, null, 0);
|
||||
$this->setImageResource(imagecreatefromstring(ob_get_clean()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int|string $num in pixels or percent
|
||||
*/
|
||||
private static function isPercent(&$num): bool
|
||||
private static function isPercent(int|string &$num): bool
|
||||
{
|
||||
if (is_string($num) && substr($num, -1) === '%') {
|
||||
if (is_string($num) && str_ends_with($num, '%')) {
|
||||
$num = (float) substr($num, 0, -1);
|
||||
return true;
|
||||
} elseif (is_int($num) || $num === (string) (int) $num) {
|
||||
@@ -760,8 +796,23 @@ class Image
|
||||
/**
|
||||
* Prevents serialization.
|
||||
*/
|
||||
public function __sleep(): array
|
||||
public function __serialize(): array
|
||||
{
|
||||
throw new Nette\NotSupportedException('You cannot serialize or unserialize ' . self::class . ' instances.');
|
||||
}
|
||||
|
||||
|
||||
public function resolveColor(ImageColor|array $color): int
|
||||
{
|
||||
$color = $color instanceof ImageColor ? $color->toRGBA() : array_values($color);
|
||||
return imagecolorallocatealpha($this->image, ...$color) ?: imagecolorresolvealpha($this->image, ...$color);
|
||||
}
|
||||
|
||||
|
||||
private static function ensureExtension(): void
|
||||
{
|
||||
if (!extension_loaded('gd')) {
|
||||
throw new Nette\NotSupportedException('PHP extension GD is not loaded.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
45
vendor/nette/utils/src/Utils/Json.php
vendored
45
vendor/nette/utils/src/Utils/Json.php
vendored
@@ -10,6 +10,8 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function defined, is_int, json_decode, json_encode, json_last_error, json_last_error_msg;
|
||||
use const JSON_BIGINT_AS_STRING, JSON_FORCE_OBJECT, JSON_HEX_AMP, JSON_HEX_APOS, JSON_HEX_QUOT, JSON_HEX_TAG, JSON_OBJECT_AS_ARRAY, JSON_PRESERVE_ZERO_FRACTION, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE;
|
||||
|
||||
|
||||
/**
|
||||
@@ -19,22 +21,39 @@ final class Json
|
||||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
/** @deprecated use Json::decode(..., forceArrays: true) */
|
||||
public const FORCE_ARRAY = JSON_OBJECT_AS_ARRAY;
|
||||
|
||||
/** @deprecated use Json::encode(..., pretty: true) */
|
||||
public const PRETTY = JSON_PRETTY_PRINT;
|
||||
|
||||
/** @deprecated use Json::encode(..., asciiSafe: true) */
|
||||
public const ESCAPE_UNICODE = 1 << 19;
|
||||
|
||||
|
||||
/**
|
||||
* Converts value to JSON format. The flag can be Json::PRETTY, which formats JSON for easier reading and clarity,
|
||||
* and Json::ESCAPE_UNICODE for ASCII output.
|
||||
* @param mixed $value
|
||||
* Converts value to JSON format. Use $pretty for easier reading and clarity, $asciiSafe for ASCII output
|
||||
* and $htmlSafe for HTML escaping, $forceObjects enforces the encoding of non-associateve arrays as objects.
|
||||
* @throws JsonException
|
||||
*/
|
||||
public static function encode($value, int $flags = 0): string
|
||||
public static function encode(
|
||||
mixed $value,
|
||||
bool|int $pretty = false,
|
||||
bool $asciiSafe = false,
|
||||
bool $htmlSafe = false,
|
||||
bool $forceObjects = false,
|
||||
): string
|
||||
{
|
||||
$flags = ($flags & self::ESCAPE_UNICODE ? 0 : JSON_UNESCAPED_UNICODE)
|
||||
| JSON_UNESCAPED_SLASHES
|
||||
| ($flags & ~self::ESCAPE_UNICODE)
|
||||
if (is_int($pretty)) { // back compatibility
|
||||
$flags = ($pretty & self::ESCAPE_UNICODE ? 0 : JSON_UNESCAPED_UNICODE) | ($pretty & ~self::ESCAPE_UNICODE);
|
||||
} else {
|
||||
$flags = ($asciiSafe ? 0 : JSON_UNESCAPED_UNICODE)
|
||||
| ($pretty ? JSON_PRETTY_PRINT : 0)
|
||||
| ($forceObjects ? JSON_FORCE_OBJECT : 0)
|
||||
| ($htmlSafe ? JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_TAG : 0);
|
||||
}
|
||||
|
||||
$flags |= JSON_UNESCAPED_SLASHES
|
||||
| (defined('JSON_PRESERVE_ZERO_FRACTION') ? JSON_PRESERVE_ZERO_FRACTION : 0); // since PHP 5.6.6 & PECL JSON-C 1.3.7
|
||||
|
||||
$json = json_encode($value, $flags);
|
||||
@@ -47,13 +66,17 @@ final class Json
|
||||
|
||||
|
||||
/**
|
||||
* Parses JSON to PHP value. The flag can be Json::FORCE_ARRAY, which forces an array instead of an object as the return value.
|
||||
* @return mixed
|
||||
* Parses JSON to PHP value. The $forceArrays enforces the decoding of objects as arrays.
|
||||
* @throws JsonException
|
||||
*/
|
||||
public static function decode(string $json, int $flags = 0)
|
||||
public static function decode(string $json, bool|int $forceArrays = false): mixed
|
||||
{
|
||||
$value = json_decode($json, null, 512, $flags | JSON_BIGINT_AS_STRING);
|
||||
$flags = is_int($forceArrays) // back compatibility
|
||||
? $forceArrays
|
||||
: ($forceArrays ? JSON_OBJECT_AS_ARRAY : 0);
|
||||
$flags |= JSON_BIGINT_AS_STRING;
|
||||
|
||||
$value = json_decode($json, flags: $flags);
|
||||
if ($error = json_last_error()) {
|
||||
throw new JsonException(json_last_error_msg(), $error);
|
||||
}
|
||||
|
||||
25
vendor/nette/utils/src/Utils/ObjectHelpers.php
vendored
25
vendor/nette/utils/src/Utils/ObjectHelpers.php
vendored
@@ -11,10 +11,13 @@ namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use Nette\MemberAccessException;
|
||||
use function array_filter, array_merge, array_pop, array_unique, get_class_methods, get_parent_class, implode, is_a, levenshtein, method_exists, preg_match_all, preg_replace, strlen, ucfirst;
|
||||
use const PREG_SET_ORDER, SORT_REGULAR;
|
||||
|
||||
|
||||
/**
|
||||
* Nette\SmartObject helpers.
|
||||
* @internal
|
||||
*/
|
||||
final class ObjectHelpers
|
||||
{
|
||||
@@ -28,8 +31,8 @@ final class ObjectHelpers
|
||||
{
|
||||
$rc = new \ReflectionClass($class);
|
||||
$hint = self::getSuggestion(array_merge(
|
||||
array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), function ($p) { return !$p->isStatic(); }),
|
||||
self::parseFullDoc($rc, '~^[ \t*]*@property(?:-read)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m')
|
||||
array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), fn($p) => !$p->isStatic()),
|
||||
self::parseFullDoc($rc, '~^[ \t*]*@property(?:-read)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m'),
|
||||
), $name);
|
||||
throw new MemberAccessException("Cannot read an undeclared property $class::\$$name" . ($hint ? ", did you mean \$$hint?" : '.'));
|
||||
}
|
||||
@@ -43,8 +46,8 @@ final class ObjectHelpers
|
||||
{
|
||||
$rc = new \ReflectionClass($class);
|
||||
$hint = self::getSuggestion(array_merge(
|
||||
array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), function ($p) { return !$p->isStatic(); }),
|
||||
self::parseFullDoc($rc, '~^[ \t*]*@property(?:-write)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m')
|
||||
array_filter($rc->getProperties(\ReflectionProperty::IS_PUBLIC), fn($p) => !$p->isStatic()),
|
||||
self::parseFullDoc($rc, '~^[ \t*]*@property(?:-write)?[ \t]+(?:\S+[ \t]+)??\$(\w+)~m'),
|
||||
), $name);
|
||||
throw new MemberAccessException("Cannot write to an undeclared property $class::\$$name" . ($hint ? ", did you mean \$$hint?" : '.'));
|
||||
}
|
||||
@@ -76,7 +79,7 @@ final class ObjectHelpers
|
||||
$hint = self::getSuggestion(array_merge(
|
||||
get_class_methods($class),
|
||||
self::parseFullDoc(new \ReflectionClass($class), '~^[ \t*]*@method[ \t]+(?:static[ \t]+)?(?:\S+[ \t]+)??(\w+)\(~m'),
|
||||
$additionalMethods
|
||||
$additionalMethods,
|
||||
), $method);
|
||||
throw new MemberAccessException("Call to undefined method $class::$method()" . ($hint ? ", did you mean $hint()?" : '.'));
|
||||
}
|
||||
@@ -107,8 +110,8 @@ final class ObjectHelpers
|
||||
|
||||
} else {
|
||||
$hint = self::getSuggestion(
|
||||
array_filter((new \ReflectionClass($class))->getMethods(\ReflectionMethod::IS_PUBLIC), function ($m) { return $m->isStatic(); }),
|
||||
$method
|
||||
array_filter((new \ReflectionClass($class))->getMethods(\ReflectionMethod::IS_PUBLIC), fn($m) => $m->isStatic()),
|
||||
$method,
|
||||
);
|
||||
throw new MemberAccessException("Call to undefined static method $class::$method()" . ($hint ? ", did you mean $hint()?" : '.'));
|
||||
}
|
||||
@@ -133,7 +136,7 @@ final class ObjectHelpers
|
||||
'~^ [ \t*]* @property(|-read|-write|-deprecated) [ \t]+ [^\s$]+ [ \t]+ \$ (\w+) ()~mx',
|
||||
(string) $rc->getDocComment(),
|
||||
$matches,
|
||||
PREG_SET_ORDER
|
||||
PREG_SET_ORDER,
|
||||
);
|
||||
|
||||
$props = [];
|
||||
@@ -199,16 +202,16 @@ final class ObjectHelpers
|
||||
}
|
||||
} while ($rc = $rc->getParentClass());
|
||||
|
||||
return preg_match_all($pattern, implode($doc), $m) ? $m[1] : [];
|
||||
return preg_match_all($pattern, implode('', $doc), $m) ? $m[1] : [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the public non-static property exists.
|
||||
* @return bool|string returns 'event' if the property exists and has event like name
|
||||
* Returns 'event' if the property exists and has event like name
|
||||
* @internal
|
||||
*/
|
||||
public static function hasProperty(string $class, string $name)
|
||||
public static function hasProperty(string $class, string $name): bool|string
|
||||
{
|
||||
static $cache;
|
||||
$prop = &$cache[$class][$name];
|
||||
|
||||
41
vendor/nette/utils/src/Utils/ObjectMixin.php
vendored
41
vendor/nette/utils/src/Utils/ObjectMixin.php
vendored
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the Nette Framework (https://nette.org)
|
||||
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
|
||||
|
||||
/**
|
||||
* Nette\Object behaviour mixin.
|
||||
* @deprecated
|
||||
*/
|
||||
final class ObjectMixin
|
||||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
/** @deprecated use ObjectHelpers::getSuggestion() */
|
||||
public static function getSuggestion(array $possibilities, string $value): ?string
|
||||
{
|
||||
trigger_error(__METHOD__ . '() has been renamed to Nette\Utils\ObjectHelpers::getSuggestion()', E_USER_DEPRECATED);
|
||||
return ObjectHelpers::getSuggestion($possibilities, $value);
|
||||
}
|
||||
|
||||
|
||||
public static function setExtensionMethod(): void
|
||||
{
|
||||
trigger_error('Class Nette\Utils\ObjectMixin is deprecated', E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
|
||||
public static function getExtensionMethod(): void
|
||||
{
|
||||
trigger_error('Class Nette\Utils\ObjectMixin is deprecated', E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
51
vendor/nette/utils/src/Utils/Paginator.php
vendored
51
vendor/nette/utils/src/Utils/Paginator.php
vendored
@@ -18,40 +18,37 @@ use Nette;
|
||||
* @property int $page
|
||||
* @property-read int $firstPage
|
||||
* @property-read int|null $lastPage
|
||||
* @property-read int $firstItemOnPage
|
||||
* @property-read int $lastItemOnPage
|
||||
* @property-read int<0,max> $firstItemOnPage
|
||||
* @property-read int<0,max> $lastItemOnPage
|
||||
* @property int $base
|
||||
* @property-read bool $first
|
||||
* @property-read bool $last
|
||||
* @property-read int|null $pageCount
|
||||
* @property int $itemsPerPage
|
||||
* @property int|null $itemCount
|
||||
* @property-read int $offset
|
||||
* @property-read int|null $countdownOffset
|
||||
* @property-read int $length
|
||||
* @property-read int<0,max>|null $pageCount
|
||||
* @property positive-int $itemsPerPage
|
||||
* @property int<0,max>|null $itemCount
|
||||
* @property-read int<0,max> $offset
|
||||
* @property-read int<0,max>|null $countdownOffset
|
||||
* @property-read int<0,max> $length
|
||||
*/
|
||||
class Paginator
|
||||
{
|
||||
use Nette\SmartObject;
|
||||
|
||||
/** @var int */
|
||||
private $base = 1;
|
||||
private int $base = 1;
|
||||
|
||||
/** @var int */
|
||||
private $itemsPerPage = 1;
|
||||
/** @var positive-int */
|
||||
private int $itemsPerPage = 1;
|
||||
|
||||
/** @var int */
|
||||
private $page = 1;
|
||||
private int $page = 1;
|
||||
|
||||
/** @var int|null */
|
||||
private $itemCount;
|
||||
/** @var int<0, max>|null */
|
||||
private ?int $itemCount = null;
|
||||
|
||||
|
||||
/**
|
||||
* Sets current page number.
|
||||
* @return static
|
||||
*/
|
||||
public function setPage(int $page)
|
||||
public function setPage(int $page): static
|
||||
{
|
||||
$this->page = $page;
|
||||
return $this;
|
||||
@@ -89,6 +86,7 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Returns the sequence number of the first element on the page
|
||||
* @return int<0, max>
|
||||
*/
|
||||
public function getFirstItemOnPage(): int
|
||||
{
|
||||
@@ -100,6 +98,7 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Returns the sequence number of the last element on the page
|
||||
* @return int<0, max>
|
||||
*/
|
||||
public function getLastItemOnPage(): int
|
||||
{
|
||||
@@ -109,9 +108,8 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Sets first page (base) number.
|
||||
* @return static
|
||||
*/
|
||||
public function setBase(int $base)
|
||||
public function setBase(int $base): static
|
||||
{
|
||||
$this->base = $base;
|
||||
return $this;
|
||||
@@ -129,6 +127,7 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Returns zero-based page number.
|
||||
* @return int<0, max>
|
||||
*/
|
||||
protected function getPageIndex(): int
|
||||
{
|
||||
@@ -161,6 +160,7 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Returns the total number of pages.
|
||||
* @return int<0, max>|null
|
||||
*/
|
||||
public function getPageCount(): ?int
|
||||
{
|
||||
@@ -172,9 +172,8 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Sets the number of items to display on a single page.
|
||||
* @return static
|
||||
*/
|
||||
public function setItemsPerPage(int $itemsPerPage)
|
||||
public function setItemsPerPage(int $itemsPerPage): static
|
||||
{
|
||||
$this->itemsPerPage = max(1, $itemsPerPage);
|
||||
return $this;
|
||||
@@ -183,6 +182,7 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Returns the number of items to display on a single page.
|
||||
* @return positive-int
|
||||
*/
|
||||
public function getItemsPerPage(): int
|
||||
{
|
||||
@@ -192,9 +192,8 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Sets the total number of items.
|
||||
* @return static
|
||||
*/
|
||||
public function setItemCount(?int $itemCount = null)
|
||||
public function setItemCount(?int $itemCount = null): static
|
||||
{
|
||||
$this->itemCount = $itemCount === null ? null : max(0, $itemCount);
|
||||
return $this;
|
||||
@@ -203,6 +202,7 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Returns the total number of items.
|
||||
* @return int<0, max>|null
|
||||
*/
|
||||
public function getItemCount(): ?int
|
||||
{
|
||||
@@ -212,6 +212,7 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Returns the absolute index of the first item on current page.
|
||||
* @return int<0, max>
|
||||
*/
|
||||
public function getOffset(): int
|
||||
{
|
||||
@@ -221,6 +222,7 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Returns the absolute index of the first item on current page in countdown paging.
|
||||
* @return int<0, max>|null
|
||||
*/
|
||||
public function getCountdownOffset(): ?int
|
||||
{
|
||||
@@ -232,6 +234,7 @@ class Paginator
|
||||
|
||||
/**
|
||||
* Returns the number of items on current page.
|
||||
* @return int<0, max>
|
||||
*/
|
||||
public function getLength(): int
|
||||
{
|
||||
|
||||
14
vendor/nette/utils/src/Utils/Random.php
vendored
14
vendor/nette/utils/src/Utils/Random.php
vendored
@@ -10,6 +10,9 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use Random\Randomizer;
|
||||
use function strlen;
|
||||
use const PHP_VERSION_ID;
|
||||
|
||||
|
||||
/**
|
||||
@@ -25,15 +28,20 @@ final class Random
|
||||
*/
|
||||
public static function generate(int $length = 10, string $charlist = '0-9a-z'): string
|
||||
{
|
||||
$charlist = count_chars(preg_replace_callback('#.-.#', function (array $m): string {
|
||||
return implode('', range($m[0][0], $m[0][2]));
|
||||
}, $charlist), 3);
|
||||
$charlist = preg_replace_callback(
|
||||
'#.-.#',
|
||||
fn(array $m): string => implode('', range($m[0][0], $m[0][2])),
|
||||
$charlist,
|
||||
);
|
||||
$charlist = count_chars($charlist, mode: 3);
|
||||
$chLen = strlen($charlist);
|
||||
|
||||
if ($length < 1) {
|
||||
throw new Nette\InvalidArgumentException('Length must be greater than zero.');
|
||||
} elseif ($chLen < 2) {
|
||||
throw new Nette\InvalidArgumentException('Character list must contain at least two chars.');
|
||||
} elseif (PHP_VERSION_ID >= 80300) {
|
||||
return (new Randomizer)->getBytesFromString($charlist, $length);
|
||||
}
|
||||
|
||||
$res = '';
|
||||
|
||||
156
vendor/nette/utils/src/Utils/Reflection.php
vendored
156
vendor/nette/utils/src/Utils/Reflection.php
vendored
@@ -10,6 +10,8 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function constant, current, defined, end, explode, file_get_contents, implode, ltrim, next, ord, strrchr, strtolower, substr;
|
||||
use const T_AS, T_CLASS, T_COMMENT, T_CURLY_OPEN, T_DOC_COMMENT, T_DOLLAR_OPEN_CURLY_BRACES, T_ENUM, T_INTERFACE, T_NAME_FULLY_QUALIFIED, T_NAME_QUALIFIED, T_NAMESPACE, T_NS_SEPARATOR, T_STRING, T_TRAIT, T_USE, T_WHITESPACE, TOKEN_PARSE;
|
||||
|
||||
|
||||
/**
|
||||
@@ -19,125 +21,21 @@ final class Reflection
|
||||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
private const BuiltinTypes = [
|
||||
'string' => 1, 'int' => 1, 'float' => 1, 'bool' => 1, 'array' => 1, 'object' => 1,
|
||||
'callable' => 1, 'iterable' => 1, 'void' => 1, 'null' => 1, 'mixed' => 1, 'false' => 1,
|
||||
'never' => 1,
|
||||
];
|
||||
|
||||
private const ClassKeywords = [
|
||||
'self' => 1, 'parent' => 1, 'static' => 1,
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Determines if type is PHP built-in type. Otherwise, it is the class name.
|
||||
*/
|
||||
/** @deprecated use Nette\Utils\Validators::isBuiltinType() */
|
||||
public static function isBuiltinType(string $type): bool
|
||||
{
|
||||
return isset(self::BuiltinTypes[strtolower($type)]);
|
||||
return Validators::isBuiltinType($type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines if type is special class name self/parent/static.
|
||||
*/
|
||||
#[\Deprecated('use Nette\Utils\Validators::isClassKeyword()')]
|
||||
public static function isClassKeyword(string $name): bool
|
||||
{
|
||||
return isset(self::ClassKeywords[strtolower($name)]);
|
||||
return Validators::isClassKeyword($name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type of return value of given function or method and normalizes `self`, `static`, and `parent` to actual class names.
|
||||
* If the function does not have a return type, it returns null.
|
||||
* If the function has union or intersection type, it throws Nette\InvalidStateException.
|
||||
*/
|
||||
public static function getReturnType(\ReflectionFunctionAbstract $func): ?string
|
||||
{
|
||||
$type = $func->getReturnType() ?? (PHP_VERSION_ID >= 80100 && $func instanceof \ReflectionMethod ? $func->getTentativeReturnType() : null);
|
||||
return self::getType($func, $type);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public static function getReturnTypes(\ReflectionFunctionAbstract $func): array
|
||||
{
|
||||
$type = Type::fromReflection($func);
|
||||
return $type ? $type->getNames() : [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type of given parameter and normalizes `self` and `parent` to the actual class names.
|
||||
* If the parameter does not have a type, it returns null.
|
||||
* If the parameter has union or intersection type, it throws Nette\InvalidStateException.
|
||||
*/
|
||||
public static function getParameterType(\ReflectionParameter $param): ?string
|
||||
{
|
||||
return self::getType($param, $param->getType());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public static function getParameterTypes(\ReflectionParameter $param): array
|
||||
{
|
||||
$type = Type::fromReflection($param);
|
||||
return $type ? $type->getNames() : [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type of given property and normalizes `self` and `parent` to the actual class names.
|
||||
* If the property does not have a type, it returns null.
|
||||
* If the property has union or intersection type, it throws Nette\InvalidStateException.
|
||||
*/
|
||||
public static function getPropertyType(\ReflectionProperty $prop): ?string
|
||||
{
|
||||
return self::getType($prop, PHP_VERSION_ID >= 70400 ? $prop->getType() : null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public static function getPropertyTypes(\ReflectionProperty $prop): array
|
||||
{
|
||||
$type = Type::fromReflection($prop);
|
||||
return $type ? $type->getNames() : [];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param \ReflectionFunction|\ReflectionMethod|\ReflectionParameter|\ReflectionProperty $reflection
|
||||
*/
|
||||
private static function getType($reflection, ?\ReflectionType $type): ?string
|
||||
{
|
||||
if ($type === null) {
|
||||
return null;
|
||||
|
||||
} elseif ($type instanceof \ReflectionNamedType) {
|
||||
return Type::resolve($type->getName(), $reflection);
|
||||
|
||||
} elseif ($type instanceof \ReflectionUnionType || $type instanceof \ReflectionIntersectionType) {
|
||||
throw new Nette\InvalidStateException('The ' . self::toString($reflection) . ' is not expected to have a union or intersection type.');
|
||||
|
||||
} else {
|
||||
throw new Nette\InvalidStateException('Unexpected type of ' . self::toString($reflection));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the default value of parameter. If it is a constant, it returns its value.
|
||||
* @return mixed
|
||||
* @throws \ReflectionException If the parameter does not have a default value or the constant cannot be resolved
|
||||
*/
|
||||
public static function getParameterDefaultValue(\ReflectionParameter $param)
|
||||
public static function getParameterDefaultValue(\ReflectionParameter $param): mixed
|
||||
{
|
||||
if ($param->isDefaultValueConstant()) {
|
||||
$const = $orig = $param->getDefaultValueConstantName();
|
||||
@@ -203,7 +101,7 @@ final class Reflection
|
||||
|
||||
$hash = [$method->getFileName(), $method->getStartLine(), $method->getEndLine()];
|
||||
if (($alias = $decl->getTraitAliases()[$method->name] ?? null)
|
||||
&& ($m = new \ReflectionMethod($alias))
|
||||
&& ($m = new \ReflectionMethod(...explode('::', $alias, 2)))
|
||||
&& $hash === [$m->getFileName(), $m->getStartLine(), $m->getEndLine()]
|
||||
) {
|
||||
return self::getMethodDeclaringMethod($m);
|
||||
@@ -228,7 +126,7 @@ final class Reflection
|
||||
public static function areCommentsAvailable(): bool
|
||||
{
|
||||
static $res;
|
||||
return $res ?? $res = (bool) (new \ReflectionMethod(__METHOD__))->getDocComment();
|
||||
return $res ?? $res = (bool) (new \ReflectionMethod(self::class, __FUNCTION__))->getDocComment();
|
||||
}
|
||||
|
||||
|
||||
@@ -239,7 +137,7 @@ final class Reflection
|
||||
} elseif ($ref instanceof \ReflectionMethod) {
|
||||
return $ref->getDeclaringClass()->name . '::' . $ref->name . '()';
|
||||
} elseif ($ref instanceof \ReflectionFunction) {
|
||||
return $ref->name . '()';
|
||||
return $ref->isAnonymous() ? '{closure}()' : $ref->name . '()';
|
||||
} elseif ($ref instanceof \ReflectionProperty) {
|
||||
return self::getPropertyDeclaringClass($ref)->name . '::$' . $ref->name;
|
||||
} elseif ($ref instanceof \ReflectionParameter) {
|
||||
@@ -261,7 +159,7 @@ final class Reflection
|
||||
if (empty($name)) {
|
||||
throw new Nette\InvalidArgumentException('Class name must not be empty.');
|
||||
|
||||
} elseif (isset(self::BuiltinTypes[$lower])) {
|
||||
} elseif (Validators::isBuiltinType($lower)) {
|
||||
return $lower;
|
||||
|
||||
} elseif ($lower === 'self' || $lower === 'static') {
|
||||
@@ -291,7 +189,7 @@ final class Reflection
|
||||
}
|
||||
|
||||
|
||||
/** @return array of [alias => class] */
|
||||
/** @return array<string, class-string> of [alias => class] */
|
||||
public static function getUseStatements(\ReflectionClass $class): array
|
||||
{
|
||||
if ($class->isAnonymous()) {
|
||||
@@ -318,22 +216,21 @@ final class Reflection
|
||||
private static function parseUseStatements(string $code, ?string $forClass = null): array
|
||||
{
|
||||
try {
|
||||
$tokens = token_get_all($code, TOKEN_PARSE);
|
||||
$tokens = \PhpToken::tokenize($code, TOKEN_PARSE);
|
||||
} catch (\ParseError $e) {
|
||||
trigger_error($e->getMessage(), E_USER_NOTICE);
|
||||
$tokens = [];
|
||||
}
|
||||
|
||||
$namespace = $class = $classLevel = $level = null;
|
||||
$namespace = $class = null;
|
||||
$classLevel = $level = 0;
|
||||
$res = $uses = [];
|
||||
|
||||
$nameTokens = PHP_VERSION_ID < 80000
|
||||
? [T_STRING, T_NS_SEPARATOR]
|
||||
: [T_STRING, T_NS_SEPARATOR, T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED];
|
||||
$nameTokens = [T_STRING, T_NS_SEPARATOR, T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED];
|
||||
|
||||
while ($token = current($tokens)) {
|
||||
next($tokens);
|
||||
switch (is_array($token) ? $token[0] : $token) {
|
||||
switch ($token->id) {
|
||||
case T_NAMESPACE:
|
||||
$namespace = ltrim(self::fetch($tokens, $nameTokens) . '\\', '\\');
|
||||
$uses = [];
|
||||
@@ -342,9 +239,7 @@ final class Reflection
|
||||
case T_CLASS:
|
||||
case T_INTERFACE:
|
||||
case T_TRAIT:
|
||||
case PHP_VERSION_ID < 80100
|
||||
? T_CLASS
|
||||
: T_ENUM:
|
||||
case T_ENUM:
|
||||
if ($name = self::fetch($tokens, T_STRING)) {
|
||||
$class = $namespace . $name;
|
||||
$classLevel = $level + 1;
|
||||
@@ -389,13 +284,13 @@ final class Reflection
|
||||
|
||||
case T_CURLY_OPEN:
|
||||
case T_DOLLAR_OPEN_CURLY_BRACES:
|
||||
case '{':
|
||||
case ord('{'):
|
||||
$level++;
|
||||
break;
|
||||
|
||||
case '}':
|
||||
case ord('}'):
|
||||
if ($level === $classLevel) {
|
||||
$class = $classLevel = null;
|
||||
$class = $classLevel = 0;
|
||||
}
|
||||
|
||||
$level--;
|
||||
@@ -406,14 +301,13 @@ final class Reflection
|
||||
}
|
||||
|
||||
|
||||
private static function fetch(array &$tokens, $take): ?string
|
||||
private static function fetch(array &$tokens, string|int|array $take): ?string
|
||||
{
|
||||
$res = null;
|
||||
while ($token = current($tokens)) {
|
||||
[$token, $s] = is_array($token) ? $token : [$token, $token];
|
||||
if (in_array($token, (array) $take, true)) {
|
||||
$res .= $s;
|
||||
} elseif (!in_array($token, [T_DOC_COMMENT, T_WHITESPACE, T_COMMENT], true)) {
|
||||
if ($token->is($take)) {
|
||||
$res .= $token->text;
|
||||
} elseif (!$token->is([T_DOC_COMMENT, T_WHITESPACE, T_COMMENT])) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
292
vendor/nette/utils/src/Utils/Strings.php
vendored
292
vendor/nette/utils/src/Utils/Strings.php
vendored
@@ -9,8 +9,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace Nette\Utils;
|
||||
|
||||
use JetBrains\PhpStorm\Language;
|
||||
use Nette;
|
||||
use function is_array, is_object, strlen;
|
||||
use function array_keys, array_map, array_shift, array_values, bin2hex, class_exists, defined, extension_loaded, function_exists, htmlspecialchars, htmlspecialchars_decode, iconv, iconv_strlen, iconv_substr, implode, in_array, is_array, is_callable, is_int, is_object, is_string, key, max, mb_convert_case, mb_strlen, mb_strtolower, mb_strtoupper, mb_substr, pack, preg_last_error, preg_last_error_msg, preg_quote, preg_replace, str_contains, str_ends_with, str_repeat, str_replace, str_starts_with, strlen, strpos, strrev, strrpos, strtolower, strtoupper, strtr, substr, trim, unpack, utf8_decode;
|
||||
use const ENT_IGNORE, ENT_NOQUOTES, ICONV_IMPL, MB_CASE_TITLE, PHP_EOL, PREG_OFFSET_CAPTURE, PREG_PATTERN_ORDER, PREG_SET_ORDER, PREG_SPLIT_DELIM_CAPTURE, PREG_SPLIT_NO_EMPTY, PREG_SPLIT_OFFSET_CAPTURE, PREG_UNMATCHED_AS_NULL;
|
||||
|
||||
|
||||
/**
|
||||
@@ -20,11 +22,14 @@ class Strings
|
||||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
public const TRIM_CHARACTERS = " \t\n\r\0\x0B\u{A0}";
|
||||
public const TrimCharacters = " \t\n\r\0\x0B\u{A0}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{200B}\u{2028}\u{3000}";
|
||||
|
||||
#[\Deprecated('use Strings::TrimCharacters')]
|
||||
public const TRIM_CHARACTERS = self::TrimCharacters;
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the string is valid in UTF-8 encoding.
|
||||
* @deprecated use Nette\Utils\Validators::isUnicode()
|
||||
*/
|
||||
public static function checkEncoding(string $s): bool
|
||||
{
|
||||
@@ -59,29 +64,47 @@ class Strings
|
||||
|
||||
|
||||
/**
|
||||
* Starts the $haystack string with the prefix $needle?
|
||||
* Returns a code point of specific character in UTF-8 (number in range 0x0000..D7FF or 0xE000..10FFFF).
|
||||
*/
|
||||
public static function ord(string $c): int
|
||||
{
|
||||
if (!extension_loaded('iconv')) {
|
||||
throw new Nette\NotSupportedException(__METHOD__ . '() requires ICONV extension that is not loaded.');
|
||||
}
|
||||
|
||||
$tmp = iconv('UTF-8', 'UTF-32BE//IGNORE', $c);
|
||||
if (!$tmp) {
|
||||
throw new Nette\InvalidArgumentException('Invalid UTF-8 character "' . ($c === '' ? '' : '\x' . strtoupper(bin2hex($c))) . '".');
|
||||
}
|
||||
|
||||
return unpack('N', $tmp)[1];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated use str_starts_with()
|
||||
*/
|
||||
public static function startsWith(string $haystack, string $needle): bool
|
||||
{
|
||||
return strncmp($haystack, $needle, strlen($needle)) === 0;
|
||||
return str_starts_with($haystack, $needle);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ends the $haystack string with the suffix $needle?
|
||||
* @deprecated use str_ends_with()
|
||||
*/
|
||||
public static function endsWith(string $haystack, string $needle): bool
|
||||
{
|
||||
return $needle === '' || substr($haystack, -strlen($needle)) === $needle;
|
||||
return str_ends_with($haystack, $needle);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Does $haystack contain $needle?
|
||||
* @deprecated use str_contains()
|
||||
*/
|
||||
public static function contains(string $haystack, string $needle): bool
|
||||
{
|
||||
return strpos($haystack, $needle) !== false;
|
||||
return str_contains($haystack, $needle);
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +139,7 @@ class Strings
|
||||
$s = $n;
|
||||
}
|
||||
|
||||
$s = self::normalizeNewLines($s);
|
||||
$s = self::unixNewLines($s);
|
||||
|
||||
// remove control characters; leave \t + \n
|
||||
$s = self::pcre('preg_replace', ['#[\x00-\x08\x0B-\x1F\x7F-\x9F]+#u', '', $s]);
|
||||
@@ -131,12 +154,30 @@ class Strings
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Standardize line endings to unix-like.
|
||||
*/
|
||||
/** @deprecated use Strings::unixNewLines() */
|
||||
public static function normalizeNewLines(string $s): string
|
||||
{
|
||||
return str_replace(["\r\n", "\r"], "\n", $s);
|
||||
return self::unixNewLines($s);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts line endings to \n used on Unix-like systems.
|
||||
* Line endings are: \n, \r, \r\n, U+2028 line separator, U+2029 paragraph separator.
|
||||
*/
|
||||
public static function unixNewLines(string $s): string
|
||||
{
|
||||
return preg_replace("~\r\n?|\u{2028}|\u{2029}~", "\n", $s);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts line endings to platform-specific, i.e. \r\n on Windows and \n elsewhere.
|
||||
* Line endings are: \n, \r, \r\n, U+2028 line separator, U+2029 paragraph separator.
|
||||
*/
|
||||
public static function platformNewLines(string $s): string
|
||||
{
|
||||
return preg_replace("~\r\n?|\n|\u{2028}|\u{2029}~", PHP_EOL, $s);
|
||||
}
|
||||
|
||||
|
||||
@@ -145,17 +186,12 @@ class Strings
|
||||
*/
|
||||
public static function toAscii(string $s): string
|
||||
{
|
||||
$iconv = defined('ICONV_IMPL') ? trim(ICONV_IMPL, '"\'') : null;
|
||||
static $transliterator = null;
|
||||
if ($transliterator === null) {
|
||||
if (class_exists('Transliterator', false)) {
|
||||
$transliterator = \Transliterator::create('Any-Latin; Latin-ASCII');
|
||||
} else {
|
||||
trigger_error(__METHOD__ . "(): it is recommended to enable PHP extensions 'intl'.", E_USER_NOTICE);
|
||||
$transliterator = false;
|
||||
}
|
||||
if (!extension_loaded('intl')) {
|
||||
throw new Nette\NotSupportedException(__METHOD__ . '() requires INTL extension that is not loaded.');
|
||||
}
|
||||
|
||||
$iconv = defined('ICONV_IMPL') ? trim(ICONV_IMPL, '"\'') : null;
|
||||
|
||||
// remove control characters and check UTF-8 validity
|
||||
$s = self::pcre('preg_replace', ['#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{2FF}\x{370}-\x{10FFFF}]#u', '', $s]);
|
||||
|
||||
@@ -165,39 +201,15 @@ class Strings
|
||||
$s = strtr($s, ["\u{AE}" => '(R)', "\u{A9}" => '(c)', "\u{2026}" => '...', "\u{AB}" => '<<', "\u{BB}" => '>>', "\u{A3}" => 'lb', "\u{A5}" => 'yen', "\u{B2}" => '^2', "\u{B3}" => '^3', "\u{B5}" => 'u', "\u{B9}" => '^1', "\u{BA}" => 'o', "\u{BF}" => '?', "\u{2CA}" => "'", "\u{2CD}" => '_', "\u{2DD}" => '"', "\u{1FEF}" => '', "\u{20AC}" => 'EUR', "\u{2122}" => 'TM', "\u{212E}" => 'e', "\u{2190}" => '<-', "\u{2191}" => '^', "\u{2192}" => '->', "\u{2193}" => 'V', "\u{2194}" => '<->']); // ® © … « » £ ¥ ² ³ µ ¹ º ¿ ˊ ˍ ˝ ` € ™ ℮ ← ↑ → ↓ ↔
|
||||
}
|
||||
|
||||
if ($transliterator) {
|
||||
$s = $transliterator->transliterate($s);
|
||||
// use iconv because The transliterator leaves some characters out of ASCII, eg → ʾ
|
||||
if ($iconv === 'glibc') {
|
||||
$s = strtr($s, '?', "\x01"); // temporarily hide ? to distinguish them from the garbage that iconv creates
|
||||
$s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
|
||||
$s = str_replace(['?', "\x01"], ['', '?'], $s); // remove garbage and restore ? characters
|
||||
} elseif ($iconv === 'libiconv') {
|
||||
$s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
|
||||
} else { // null or 'unknown' (#216)
|
||||
$s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]); // remove non-ascii chars
|
||||
}
|
||||
} elseif ($iconv === 'glibc' || $iconv === 'libiconv') {
|
||||
// temporarily hide these characters to distinguish them from the garbage that iconv creates
|
||||
$s = strtr($s, '`\'"^~?', "\x01\x02\x03\x04\x05\x06");
|
||||
if ($iconv === 'glibc') {
|
||||
// glibc implementation is very limited. transliterate into Windows-1250 and then into ASCII, so most Eastern European characters are preserved
|
||||
$s = iconv('UTF-8', 'WINDOWS-1250//TRANSLIT//IGNORE', $s);
|
||||
$s = strtr(
|
||||
$s,
|
||||
"\xa5\xa3\xbc\x8c\xa7\x8a\xaa\x8d\x8f\x8e\xaf\xb9\xb3\xbe\x9c\x9a\xba\x9d\x9f\x9e\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe\x96\xa0\x8b\x97\x9b\xa6\xad\xb7",
|
||||
'ALLSSSSTZZZallssstzzzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTsraaaalccceeeeiiddnnooooruuuuyt- <->|-.'
|
||||
);
|
||||
$s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]);
|
||||
} else {
|
||||
$s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
|
||||
}
|
||||
|
||||
// remove garbage that iconv creates during transliteration (eg Ý -> Y')
|
||||
$s = str_replace(['`', "'", '"', '^', '~', '?'], '', $s);
|
||||
// restore temporarily hidden characters
|
||||
$s = strtr($s, "\x01\x02\x03\x04\x05\x06", '`\'"^~?');
|
||||
} else {
|
||||
$s = \Transliterator::create('Any-Latin; Latin-ASCII')->transliterate($s);
|
||||
// use iconv because The transliterator leaves some characters out of ASCII, eg → ʾ
|
||||
if ($iconv === 'glibc') {
|
||||
$s = strtr($s, '?', "\x01"); // temporarily hide ? to distinguish them from the garbage that iconv creates
|
||||
$s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
|
||||
$s = str_replace(['?', "\x01"], ['', '?'], $s); // remove garbage and restore ? characters
|
||||
} elseif ($iconv === 'libiconv') {
|
||||
$s = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s);
|
||||
} else { // null or 'unknown' (#216)
|
||||
$s = self::pcre('preg_replace', ['#[^\x00-\x7F]++#', '', $s]); // remove non-ascii chars
|
||||
}
|
||||
|
||||
@@ -357,16 +369,18 @@ class Strings
|
||||
*/
|
||||
public static function length(string $s): int
|
||||
{
|
||||
return function_exists('mb_strlen')
|
||||
? mb_strlen($s, 'UTF-8')
|
||||
: strlen(utf8_decode($s));
|
||||
return match (true) {
|
||||
extension_loaded('mbstring') => mb_strlen($s, 'UTF-8'),
|
||||
extension_loaded('iconv') => iconv_strlen($s, 'UTF-8'),
|
||||
default => strlen(@utf8_decode($s)), // deprecated
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes all left and right side spaces (or the characters passed as second argument) from a UTF-8 encoded string.
|
||||
*/
|
||||
public static function trim(string $s, string $charlist = self::TRIM_CHARACTERS): string
|
||||
public static function trim(string $s, string $charlist = self::TrimCharacters): string
|
||||
{
|
||||
$charlist = preg_quote($charlist, '#');
|
||||
return self::replace($s, '#^[' . $charlist . ']+|[' . $charlist . ']+$#Du', '');
|
||||
@@ -375,6 +389,7 @@ class Strings
|
||||
|
||||
/**
|
||||
* Pads a UTF-8 string to given length by prepending the $pad string to the beginning.
|
||||
* @param non-empty-string $pad
|
||||
*/
|
||||
public static function padLeft(string $s, int $length, string $pad = ' '): string
|
||||
{
|
||||
@@ -386,6 +401,7 @@ class Strings
|
||||
|
||||
/**
|
||||
* Pads UTF-8 string to given length by appending the $pad string to the end.
|
||||
* @param non-empty-string $pad
|
||||
*/
|
||||
public static function padRight(string $s, int $length, string $pad = ' '): string
|
||||
{
|
||||
@@ -482,73 +498,187 @@ class Strings
|
||||
|
||||
|
||||
/**
|
||||
* Splits a string into array by the regular expression. Parenthesized expression in the delimiter are captured.
|
||||
* Parameter $flags can be any combination of PREG_SPLIT_NO_EMPTY and PREG_OFFSET_CAPTURE flags.
|
||||
* Divides the string into arrays according to the regular expression. Expressions in parentheses will be captured and returned as well.
|
||||
*/
|
||||
public static function split(string $subject, string $pattern, int $flags = 0): array
|
||||
public static function split(
|
||||
string $subject,
|
||||
#[Language('RegExp')]
|
||||
string $pattern,
|
||||
bool|int $captureOffset = false,
|
||||
bool $skipEmpty = false,
|
||||
int $limit = -1,
|
||||
bool $utf8 = false,
|
||||
): array
|
||||
{
|
||||
return self::pcre('preg_split', [$pattern, $subject, -1, $flags | PREG_SPLIT_DELIM_CAPTURE]);
|
||||
$flags = is_int($captureOffset) // back compatibility
|
||||
? $captureOffset
|
||||
: ($captureOffset ? PREG_SPLIT_OFFSET_CAPTURE : 0) | ($skipEmpty ? PREG_SPLIT_NO_EMPTY : 0);
|
||||
|
||||
$pattern .= $utf8 ? 'u' : '';
|
||||
$m = self::pcre('preg_split', [$pattern, $subject, $limit, $flags | PREG_SPLIT_DELIM_CAPTURE]);
|
||||
return $utf8 && $captureOffset
|
||||
? self::bytesToChars($subject, [$m])[0]
|
||||
: $m;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if given string matches a regular expression pattern and returns an array with first found match and each subpattern.
|
||||
* Parameter $flags can be any combination of PREG_OFFSET_CAPTURE and PREG_UNMATCHED_AS_NULL flags.
|
||||
* Searches the string for the part matching the regular expression and returns
|
||||
* an array with the found expression and individual subexpressions, or `null`.
|
||||
*/
|
||||
public static function match(string $subject, string $pattern, int $flags = 0, int $offset = 0): ?array
|
||||
public static function match(
|
||||
string $subject,
|
||||
#[Language('RegExp')]
|
||||
string $pattern,
|
||||
bool|int $captureOffset = false,
|
||||
int $offset = 0,
|
||||
bool $unmatchedAsNull = false,
|
||||
bool $utf8 = false,
|
||||
): ?array
|
||||
{
|
||||
if ($offset > strlen($subject)) {
|
||||
return null;
|
||||
$flags = is_int($captureOffset) // back compatibility
|
||||
? $captureOffset
|
||||
: ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0);
|
||||
|
||||
if ($utf8) {
|
||||
$offset = strlen(self::substring($subject, 0, $offset));
|
||||
$pattern .= 'u';
|
||||
}
|
||||
|
||||
return self::pcre('preg_match', [$pattern, $subject, &$m, $flags, $offset])
|
||||
? $m
|
||||
: null;
|
||||
if ($offset > strlen($subject)) {
|
||||
return null;
|
||||
} elseif (!self::pcre('preg_match', [$pattern, $subject, &$m, $flags, $offset])) {
|
||||
return null;
|
||||
} elseif ($utf8 && $captureOffset) {
|
||||
return self::bytesToChars($subject, [$m])[0];
|
||||
} else {
|
||||
return $m;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds all occurrences matching regular expression pattern and returns a two-dimensional array. Result is array of matches (ie uses by default PREG_SET_ORDER).
|
||||
* Parameter $flags can be any combination of PREG_OFFSET_CAPTURE, PREG_UNMATCHED_AS_NULL and PREG_PATTERN_ORDER flags.
|
||||
* Searches the string for all occurrences matching the regular expression and
|
||||
* returns an array of arrays containing the found expression and each subexpression.
|
||||
* @return ($lazy is true ? \Generator<int, array> : array[])
|
||||
*/
|
||||
public static function matchAll(string $subject, string $pattern, int $flags = 0, int $offset = 0): array
|
||||
public static function matchAll(
|
||||
string $subject,
|
||||
#[Language('RegExp')]
|
||||
string $pattern,
|
||||
bool|int $captureOffset = false,
|
||||
int $offset = 0,
|
||||
bool $unmatchedAsNull = false,
|
||||
bool $patternOrder = false,
|
||||
bool $utf8 = false,
|
||||
bool $lazy = false,
|
||||
): array|\Generator
|
||||
{
|
||||
if ($utf8) {
|
||||
$offset = strlen(self::substring($subject, 0, $offset));
|
||||
$pattern .= 'u';
|
||||
}
|
||||
|
||||
if ($lazy) {
|
||||
$flags = PREG_OFFSET_CAPTURE | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0);
|
||||
return (function () use ($utf8, $captureOffset, $flags, $subject, $pattern, $offset) {
|
||||
$counter = 0;
|
||||
while (
|
||||
$offset <= strlen($subject) - ($counter ? 1 : 0)
|
||||
&& self::pcre('preg_match', [$pattern, $subject, &$m, $flags, $offset])
|
||||
) {
|
||||
$offset = $m[0][1] + max(1, strlen($m[0][0]));
|
||||
if (!$captureOffset) {
|
||||
$m = array_map(fn($item) => $item[0], $m);
|
||||
} elseif ($utf8) {
|
||||
$m = self::bytesToChars($subject, [$m])[0];
|
||||
}
|
||||
yield $counter++ => $m;
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
if ($offset > strlen($subject)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$flags = is_int($captureOffset) // back compatibility
|
||||
? $captureOffset
|
||||
: ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0) | ($patternOrder ? PREG_PATTERN_ORDER : 0);
|
||||
|
||||
self::pcre('preg_match_all', [
|
||||
$pattern, $subject, &$m,
|
||||
($flags & PREG_PATTERN_ORDER) ? $flags : ($flags | PREG_SET_ORDER),
|
||||
$offset,
|
||||
]);
|
||||
return $m;
|
||||
return $utf8 && $captureOffset
|
||||
? self::bytesToChars($subject, $m)
|
||||
: $m;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replaces all occurrences matching regular expression $pattern which can be string or array in the form `pattern => replacement`.
|
||||
* @param string|array $pattern
|
||||
* @param string|callable $replacement
|
||||
*/
|
||||
public static function replace(string $subject, $pattern, $replacement = '', int $limit = -1): string
|
||||
public static function replace(
|
||||
string $subject,
|
||||
#[Language('RegExp')]
|
||||
string|array $pattern,
|
||||
string|callable $replacement = '',
|
||||
int $limit = -1,
|
||||
bool $captureOffset = false,
|
||||
bool $unmatchedAsNull = false,
|
||||
bool $utf8 = false,
|
||||
): string
|
||||
{
|
||||
if (is_object($replacement) || is_array($replacement)) {
|
||||
if (!is_callable($replacement, false, $textual)) {
|
||||
throw new Nette\InvalidStateException("Callback '$textual' is not callable.");
|
||||
}
|
||||
|
||||
return self::pcre('preg_replace_callback', [$pattern, $replacement, $subject, $limit]);
|
||||
$flags = ($captureOffset ? PREG_OFFSET_CAPTURE : 0) | ($unmatchedAsNull ? PREG_UNMATCHED_AS_NULL : 0);
|
||||
if ($utf8) {
|
||||
$pattern .= 'u';
|
||||
if ($captureOffset) {
|
||||
$replacement = fn($m) => $replacement(self::bytesToChars($subject, [$m])[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return self::pcre('preg_replace_callback', [$pattern, $replacement, $subject, $limit, 0, $flags]);
|
||||
|
||||
} elseif (is_array($pattern) && is_string(key($pattern))) {
|
||||
$replacement = array_values($pattern);
|
||||
$pattern = array_keys($pattern);
|
||||
}
|
||||
|
||||
if ($utf8) {
|
||||
$pattern = array_map(fn($item) => $item . 'u', (array) $pattern);
|
||||
}
|
||||
|
||||
return self::pcre('preg_replace', [$pattern, $replacement, $subject, $limit]);
|
||||
}
|
||||
|
||||
|
||||
private static function bytesToChars(string $s, array $groups): array
|
||||
{
|
||||
$lastBytes = $lastChars = 0;
|
||||
foreach ($groups as &$matches) {
|
||||
foreach ($matches as &$match) {
|
||||
if ($match[1] > $lastBytes) {
|
||||
$lastChars += self::length(substr($s, $lastBytes, $match[1] - $lastBytes));
|
||||
} elseif ($match[1] < $lastBytes) {
|
||||
$lastChars -= self::length(substr($s, $match[1], $lastBytes - $match[1]));
|
||||
}
|
||||
|
||||
$lastBytes = $match[1];
|
||||
$match[1] = $lastChars;
|
||||
}
|
||||
}
|
||||
|
||||
return $groups;
|
||||
}
|
||||
|
||||
|
||||
/** @internal */
|
||||
public static function pcre(string $func, array $args)
|
||||
{
|
||||
@@ -560,7 +690,7 @@ class Strings
|
||||
if (($code = preg_last_error()) // run-time error, but preg_last_error & return code are liars
|
||||
&& ($res === null || !in_array($func, ['preg_filter', 'preg_replace_callback', 'preg_replace'], true))
|
||||
) {
|
||||
throw new RegexpException((RegexpException::MESSAGES[$code] ?? 'Unknown error')
|
||||
throw new RegexpException(preg_last_error_msg()
|
||||
. ' (pattern: ' . implode(' or ', (array) $args[0]) . ')', $code);
|
||||
}
|
||||
|
||||
|
||||
232
vendor/nette/utils/src/Utils/Type.php
vendored
232
vendor/nette/utils/src/Utils/Type.php
vendored
@@ -10,58 +10,52 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_map, array_search, array_splice, count, explode, implode, is_a, is_resource, is_string, strcasecmp, strtolower, substr, trim;
|
||||
|
||||
|
||||
/**
|
||||
* PHP type reflection.
|
||||
*/
|
||||
final class Type
|
||||
final readonly class Type
|
||||
{
|
||||
/** @var array */
|
||||
private $types;
|
||||
|
||||
/** @var bool */
|
||||
private $single;
|
||||
|
||||
/** @var string |, & */
|
||||
private $kind;
|
||||
/** @var array<int, string|self> */
|
||||
private array $types;
|
||||
private bool $simple;
|
||||
private string $kind; // | &
|
||||
|
||||
|
||||
/**
|
||||
* Creates a Type object based on reflection. Resolves self, static and parent to the actual class name.
|
||||
* If the subject has no type, it returns null.
|
||||
* @param \ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $reflection
|
||||
*/
|
||||
public static function fromReflection($reflection): ?self
|
||||
public static function fromReflection(
|
||||
\ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $reflection,
|
||||
): ?self
|
||||
{
|
||||
if ($reflection instanceof \ReflectionProperty && PHP_VERSION_ID < 70400) {
|
||||
return null;
|
||||
} elseif ($reflection instanceof \ReflectionMethod) {
|
||||
$type = $reflection->getReturnType() ?? (PHP_VERSION_ID >= 80100 ? $reflection->getTentativeReturnType() : null);
|
||||
} else {
|
||||
$type = $reflection instanceof \ReflectionFunctionAbstract
|
||||
? $reflection->getReturnType()
|
||||
: $reflection->getType();
|
||||
}
|
||||
$type = $reflection instanceof \ReflectionFunctionAbstract
|
||||
? $reflection->getReturnType() ?? ($reflection instanceof \ReflectionMethod ? $reflection->getTentativeReturnType() : null)
|
||||
: $reflection->getType();
|
||||
|
||||
if ($type === null) {
|
||||
return null;
|
||||
return $type ? self::fromReflectionType($type, $reflection, asObject: true) : null;
|
||||
}
|
||||
|
||||
} elseif ($type instanceof \ReflectionNamedType) {
|
||||
$name = self::resolve($type->getName(), $reflection);
|
||||
return new self($type->allowsNull() && $type->getName() !== 'mixed' ? [$name, 'null'] : [$name]);
|
||||
|
||||
private static function fromReflectionType(\ReflectionType $type, $of, bool $asObject): self|string
|
||||
{
|
||||
if ($type instanceof \ReflectionNamedType) {
|
||||
$name = self::resolve($type->getName(), $of);
|
||||
return $asObject
|
||||
? new self($type->allowsNull() && $name !== 'mixed' ? [$name, 'null'] : [$name])
|
||||
: $name;
|
||||
|
||||
} elseif ($type instanceof \ReflectionUnionType || $type instanceof \ReflectionIntersectionType) {
|
||||
return new self(
|
||||
array_map(
|
||||
function ($t) use ($reflection) { return self::resolve($t->getName(), $reflection); },
|
||||
$type->getTypes()
|
||||
),
|
||||
$type instanceof \ReflectionUnionType ? '|' : '&'
|
||||
array_map(fn($t) => self::fromReflectionType($t, $of, asObject: false), $type->getTypes()),
|
||||
$type instanceof \ReflectionUnionType ? '|' : '&',
|
||||
);
|
||||
|
||||
} else {
|
||||
throw new Nette\InvalidStateException('Unexpected type of ' . Reflection::toString($reflection));
|
||||
throw new Nette\InvalidStateException('Unexpected type of ' . Reflection::toString($of));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,37 +65,60 @@ final class Type
|
||||
*/
|
||||
public static function fromString(string $type): self
|
||||
{
|
||||
if (!preg_match('#(?:
|
||||
\?([\w\\\\]+)|
|
||||
[\w\\\\]+ (?: (&[\w\\\\]+)* | (\|[\w\\\\]+)* )
|
||||
)()$#xAD', $type, $m)) {
|
||||
if (!Validators::isTypeDeclaration($type)) {
|
||||
throw new Nette\InvalidArgumentException("Invalid type '$type'.");
|
||||
}
|
||||
|
||||
[, $nType, $iType] = $m;
|
||||
if ($nType) {
|
||||
return new self([$nType, 'null']);
|
||||
} elseif ($iType) {
|
||||
return new self(explode('&', $type), '&');
|
||||
} else {
|
||||
return new self(explode('|', $type));
|
||||
if ($type[0] === '?') {
|
||||
return new self([substr($type, 1), 'null']);
|
||||
}
|
||||
|
||||
$unions = [];
|
||||
foreach (explode('|', $type) as $part) {
|
||||
$part = explode('&', trim($part, '()'));
|
||||
$unions[] = count($part) === 1 ? $part[0] : new self($part, '&');
|
||||
}
|
||||
|
||||
return count($unions) === 1 && $unions[0] instanceof self
|
||||
? $unions[0]
|
||||
: new self($unions);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a Type object based on the actual type of value.
|
||||
*/
|
||||
public static function fromValue(mixed $value): self
|
||||
{
|
||||
$type = get_debug_type($value);
|
||||
if (is_resource($value)) {
|
||||
$type = 'mixed';
|
||||
} elseif (str_ends_with($type, '@anonymous')) {
|
||||
$parent = substr($type, 0, -10);
|
||||
$type = $parent === 'class' ? 'object' : $parent;
|
||||
}
|
||||
|
||||
return new self([$type]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolves 'self', 'static' and 'parent' to the actual class name.
|
||||
* @param \ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $reflection
|
||||
*/
|
||||
public static function resolve(string $type, $reflection): string
|
||||
public static function resolve(
|
||||
string $type,
|
||||
\ReflectionFunctionAbstract|\ReflectionParameter|\ReflectionProperty $of,
|
||||
): string
|
||||
{
|
||||
$lower = strtolower($type);
|
||||
if ($reflection instanceof \ReflectionFunction) {
|
||||
if ($of instanceof \ReflectionFunction) {
|
||||
return $type;
|
||||
} elseif ($lower === 'self' || $lower === 'static') {
|
||||
return $reflection->getDeclaringClass()->name;
|
||||
} elseif ($lower === 'parent' && $reflection->getDeclaringClass()->getParentClass()) {
|
||||
return $reflection->getDeclaringClass()->getParentClass()->name;
|
||||
} elseif ($lower === 'self') {
|
||||
return $of->getDeclaringClass()->name;
|
||||
} elseif ($lower === 'static') {
|
||||
return ($of instanceof ReflectionMethod ? $of->getOriginalClass() : $of->getDeclaringClass())->name;
|
||||
} elseif ($lower === 'parent' && $of->getDeclaringClass()->getParentClass()) {
|
||||
return $of->getDeclaringClass()->getParentClass()->name;
|
||||
} else {
|
||||
return $type;
|
||||
}
|
||||
@@ -110,31 +127,57 @@ final class Type
|
||||
|
||||
private function __construct(array $types, string $kind = '|')
|
||||
{
|
||||
if ($types[0] === 'null') { // null as last
|
||||
array_push($types, array_shift($types));
|
||||
$o = array_search('null', $types, strict: true);
|
||||
if ($o !== false) { // null as last
|
||||
array_splice($types, $o, 1);
|
||||
$types[] = 'null';
|
||||
}
|
||||
|
||||
$this->types = $types;
|
||||
$this->single = ($types[1] ?? 'null') === 'null';
|
||||
$this->simple = is_string($types[0]) && ($types[1] ?? 'null') === 'null';
|
||||
$this->kind = count($types) > 1 ? $kind : '';
|
||||
}
|
||||
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->single
|
||||
? (count($this->types) > 1 ? '?' : '') . $this->types[0]
|
||||
: implode($this->kind, $this->types);
|
||||
$multi = count($this->types) > 1;
|
||||
if ($this->simple) {
|
||||
return ($multi ? '?' : '') . $this->types[0];
|
||||
}
|
||||
|
||||
$res = [];
|
||||
foreach ($this->types as $type) {
|
||||
$res[] = $type instanceof self && $multi ? "($type)" : $type;
|
||||
}
|
||||
return implode($this->kind, $res);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a type that accepts both the current type and the given type.
|
||||
*/
|
||||
public function with(string|self $type): self
|
||||
{
|
||||
$type = is_string($type) ? self::fromString($type) : $type;
|
||||
return match (true) {
|
||||
$this->allows($type) => $this,
|
||||
$type->allows($this) => $type,
|
||||
default => new self(array_unique(
|
||||
array_merge($this->isIntersection() ? [$this] : $this->types, $type->isIntersection() ? [$type] : $type->types),
|
||||
SORT_REGULAR,
|
||||
), '|'),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the array of subtypes that make up the compound type as strings.
|
||||
* @return string[]
|
||||
* @return array<int, string|string[]>
|
||||
*/
|
||||
public function getNames(): array
|
||||
{
|
||||
return $this->types;
|
||||
return array_map(fn($t) => $t instanceof self ? $t->getNames() : $t, $this->types);
|
||||
}
|
||||
|
||||
|
||||
@@ -144,16 +187,16 @@ final class Type
|
||||
*/
|
||||
public function getTypes(): array
|
||||
{
|
||||
return array_map(function ($name) { return self::fromString($name); }, $this->types);
|
||||
return array_map(fn($t) => $t instanceof self ? $t : new self([$t]), $this->types);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type name for single types, otherwise null.
|
||||
* Returns the type name for simple types, otherwise null.
|
||||
*/
|
||||
public function getSingleName(): ?string
|
||||
{
|
||||
return $this->single
|
||||
return $this->simple
|
||||
? $this->types[0]
|
||||
: null;
|
||||
}
|
||||
@@ -178,29 +221,36 @@ final class Type
|
||||
|
||||
|
||||
/**
|
||||
* Returns true whether it is a single type. Simple nullable types are also considered to be single types.
|
||||
* Returns true whether it is a simple type. Single nullable types are also considered to be simple types.
|
||||
*/
|
||||
public function isSimple(): bool
|
||||
{
|
||||
return $this->simple;
|
||||
}
|
||||
|
||||
|
||||
#[\Deprecated('use isSimple()')]
|
||||
public function isSingle(): bool
|
||||
{
|
||||
return $this->single;
|
||||
return $this->simple;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true whether the type is both a single and a PHP built-in type.
|
||||
* Returns true whether the type is both a simple and a PHP built-in type.
|
||||
*/
|
||||
public function isBuiltin(): bool
|
||||
{
|
||||
return $this->single && Reflection::isBuiltinType($this->types[0]);
|
||||
return $this->simple && Validators::isBuiltinType($this->types[0]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true whether the type is both a single and a class name.
|
||||
* Returns true whether the type is both a simple and a class name.
|
||||
*/
|
||||
public function isClass(): bool
|
||||
{
|
||||
return $this->single && !Reflection::isBuiltinType($this->types[0]);
|
||||
return $this->simple && !Validators::isBuiltinType($this->types[0]);
|
||||
}
|
||||
|
||||
|
||||
@@ -209,44 +259,44 @@ final class Type
|
||||
*/
|
||||
public function isClassKeyword(): bool
|
||||
{
|
||||
return $this->single && Reflection::isClassKeyword($this->types[0]);
|
||||
return $this->simple && Validators::isClassKeyword($this->types[0]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verifies type compatibility. For example, it checks if a value of a certain type could be passed as a parameter.
|
||||
*/
|
||||
public function allows(string $type): bool
|
||||
public function allows(string|self $type): bool
|
||||
{
|
||||
if ($this->types === ['mixed']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$type = self::fromString($type);
|
||||
$type = is_string($type) ? self::fromString($type) : $type;
|
||||
return $type->isUnion()
|
||||
? Arrays::every($type->types, fn($t) => $this->allowsAny($t instanceof self ? $t->types : [$t]))
|
||||
: $this->allowsAny($type->types);
|
||||
}
|
||||
|
||||
if ($this->isIntersection()) {
|
||||
if (!$type->isIntersection()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Arrays::every($this->types, function ($currentType) use ($type) {
|
||||
$builtin = Reflection::isBuiltinType($currentType);
|
||||
return Arrays::some($type->types, function ($testedType) use ($currentType, $builtin) {
|
||||
return $builtin
|
||||
? strcasecmp($currentType, $testedType) === 0
|
||||
: is_a($testedType, $currentType, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
private function allowsAny(array $givenTypes): bool
|
||||
{
|
||||
return $this->isUnion()
|
||||
? Arrays::some($this->types, fn($t) => $this->allowsAll($t instanceof self ? $t->types : [$t], $givenTypes))
|
||||
: $this->allowsAll($this->types, $givenTypes);
|
||||
}
|
||||
|
||||
$method = $type->isIntersection() ? 'some' : 'every';
|
||||
return Arrays::$method($type->types, function ($testedType) {
|
||||
$builtin = Reflection::isBuiltinType($testedType);
|
||||
return Arrays::some($this->types, function ($currentType) use ($testedType, $builtin) {
|
||||
return $builtin
|
||||
? strcasecmp($currentType, $testedType) === 0
|
||||
: is_a($testedType, $currentType, true);
|
||||
});
|
||||
});
|
||||
|
||||
private function allowsAll(array $ourTypes, array $givenTypes): bool
|
||||
{
|
||||
return Arrays::every(
|
||||
$ourTypes,
|
||||
fn($ourType) => Arrays::some(
|
||||
$givenTypes,
|
||||
fn($givenType) => Validators::isBuiltinType($ourType)
|
||||
? strcasecmp($ourType, $givenType) === 0
|
||||
: is_a($givenType, $ourType, allow_string: true),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
134
vendor/nette/utils/src/Utils/Validators.php
vendored
134
vendor/nette/utils/src/Utils/Validators.php
vendored
@@ -10,6 +10,7 @@ declare(strict_types=1);
|
||||
namespace Nette\Utils;
|
||||
|
||||
use Nette;
|
||||
use function array_key_exists, class_exists, explode, gettype, interface_exists, is_callable, is_float, is_int, is_iterable, is_numeric, is_object, is_string, preg_match, str_ends_with, str_replace, str_starts_with, strlen, strtolower, substr, trait_exists, var_export;
|
||||
|
||||
|
||||
/**
|
||||
@@ -19,6 +20,12 @@ class Validators
|
||||
{
|
||||
use Nette\StaticClass;
|
||||
|
||||
private const BuiltinTypes = [
|
||||
'string' => 1, 'int' => 1, 'float' => 1, 'bool' => 1, 'array' => 1, 'object' => 1,
|
||||
'callable' => 1, 'iterable' => 1, 'void' => 1, 'null' => 1, 'mixed' => 1, 'false' => 1,
|
||||
'never' => 1, 'true' => 1,
|
||||
];
|
||||
|
||||
/** @var array<string,?callable> */
|
||||
protected static $validators = [
|
||||
// PHP types
|
||||
@@ -87,19 +94,18 @@ class Validators
|
||||
|
||||
/**
|
||||
* Verifies that the value is of expected types separated by pipe.
|
||||
* @param mixed $value
|
||||
* @throws AssertionException
|
||||
*/
|
||||
public static function assert($value, string $expected, string $label = 'variable'): void
|
||||
public static function assert(mixed $value, string $expected, string $label = 'variable'): void
|
||||
{
|
||||
if (!static::is($value, $expected)) {
|
||||
$expected = str_replace(['|', ':'], [' or ', ' in range '], $expected);
|
||||
$translate = ['boolean' => 'bool', 'integer' => 'int', 'double' => 'float', 'NULL' => 'null'];
|
||||
$type = $translate[gettype($value)] ?? gettype($value);
|
||||
if (is_int($value) || is_float($value) || (is_string($value) && strlen($value) < 40)) {
|
||||
$type .= ' ' . var_export($value, true);
|
||||
$type .= ' ' . var_export($value, return: true);
|
||||
} elseif (is_object($value)) {
|
||||
$type .= ' ' . get_class($value);
|
||||
$type .= ' ' . $value::class;
|
||||
}
|
||||
|
||||
throw new AssertionException("The $label expects to be $expected, $type given.");
|
||||
@@ -110,15 +116,15 @@ class Validators
|
||||
/**
|
||||
* Verifies that element $key in array is of expected types separated by pipe.
|
||||
* @param mixed[] $array
|
||||
* @param int|string $key
|
||||
* @throws AssertionException
|
||||
*/
|
||||
public static function assertField(
|
||||
array $array,
|
||||
$key,
|
||||
?string $expected = null,
|
||||
string $label = "item '%' in array"
|
||||
): void {
|
||||
string $label = "item '%' in array",
|
||||
): void
|
||||
{
|
||||
if (!array_key_exists($key, $array)) {
|
||||
throw new AssertionException('Missing ' . str_replace('%', $key, $label) . '.');
|
||||
|
||||
@@ -130,18 +136,17 @@ class Validators
|
||||
|
||||
/**
|
||||
* Verifies that the value is of expected types separated by pipe.
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function is($value, string $expected): bool
|
||||
public static function is(mixed $value, string $expected): bool
|
||||
{
|
||||
foreach (explode('|', $expected) as $item) {
|
||||
if (substr($item, -2) === '[]') {
|
||||
if (str_ends_with($item, '[]')) {
|
||||
if (is_iterable($value) && self::everyIs($value, substr($item, 0, -2))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
continue;
|
||||
} elseif (substr($item, 0, 1) === '?') {
|
||||
} elseif (str_starts_with($item, '?')) {
|
||||
$item = substr($item, 1);
|
||||
if ($value === null) {
|
||||
return true;
|
||||
@@ -208,9 +213,9 @@ class Validators
|
||||
|
||||
/**
|
||||
* Checks if the value is an integer or a float.
|
||||
* @param mixed $value
|
||||
* @return ($value is int|float ? true : false)
|
||||
*/
|
||||
public static function isNumber($value): bool
|
||||
public static function isNumber(mixed $value): bool
|
||||
{
|
||||
return is_int($value) || is_float($value);
|
||||
}
|
||||
@@ -218,9 +223,9 @@ class Validators
|
||||
|
||||
/**
|
||||
* Checks if the value is an integer or a integer written in a string.
|
||||
* @param mixed $value
|
||||
* @return ($value is non-empty-string ? bool : ($value is int ? true : false))
|
||||
*/
|
||||
public static function isNumericInt($value): bool
|
||||
public static function isNumericInt(mixed $value): bool
|
||||
{
|
||||
return is_int($value) || (is_string($value) && preg_match('#^[+-]?[0-9]+$#D', $value));
|
||||
}
|
||||
@@ -228,9 +233,9 @@ class Validators
|
||||
|
||||
/**
|
||||
* Checks if the value is a number or a number written in a string.
|
||||
* @param mixed $value
|
||||
* @return ($value is non-empty-string ? bool : ($value is int|float ? true : false))
|
||||
*/
|
||||
public static function isNumeric($value): bool
|
||||
public static function isNumeric(mixed $value): bool
|
||||
{
|
||||
return is_float($value) || is_int($value) || (is_string($value) && preg_match('#^[+-]?([0-9]++\.?[0-9]*|\.[0-9]+)$#D', $value));
|
||||
}
|
||||
@@ -238,19 +243,17 @@ class Validators
|
||||
|
||||
/**
|
||||
* Checks if the value is a syntactically correct callback.
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function isCallable($value): bool
|
||||
public static function isCallable(mixed $value): bool
|
||||
{
|
||||
return $value && is_callable($value, true);
|
||||
return $value && is_callable($value, syntax_only: true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if the value is a valid UTF-8 string.
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function isUnicode($value): bool
|
||||
public static function isUnicode(mixed $value): bool
|
||||
{
|
||||
return is_string($value) && preg_match('##u', $value);
|
||||
}
|
||||
@@ -258,9 +261,9 @@ class Validators
|
||||
|
||||
/**
|
||||
* Checks if the value is 0, '', false or null.
|
||||
* @param mixed $value
|
||||
* @return ($value is 0|''|false|null ? true : false)
|
||||
*/
|
||||
public static function isNone($value): bool
|
||||
public static function isNone(mixed $value): bool
|
||||
{
|
||||
return $value == null; // intentionally ==
|
||||
}
|
||||
@@ -275,10 +278,10 @@ class Validators
|
||||
|
||||
/**
|
||||
* Checks if a variable is a zero-based integer indexed array.
|
||||
* @param mixed $value
|
||||
* @deprecated use Nette\Utils\Arrays::isList
|
||||
* @return ($value is list ? true : false)
|
||||
*/
|
||||
public static function isList($value): bool
|
||||
public static function isList(mixed $value): bool
|
||||
{
|
||||
return Arrays::isList($value);
|
||||
}
|
||||
@@ -287,9 +290,8 @@ class Validators
|
||||
/**
|
||||
* Checks if the value is in the given range [min, max], where the upper or lower limit can be omitted (null).
|
||||
* Numbers, strings and DateTime objects can be compared.
|
||||
* @param mixed $value
|
||||
*/
|
||||
public static function isInRange($value, array $range): bool
|
||||
public static function isInRange(mixed $value, array $range): bool
|
||||
{
|
||||
if ($value === null || !(isset($range[0]) || isset($range[1]))) {
|
||||
return false;
|
||||
@@ -320,14 +322,13 @@ class Validators
|
||||
$atom = "[-a-z0-9!#$%&'*+/=?^_`{|}~]"; // RFC 5322 unquoted characters in local-part
|
||||
$alpha = "a-z\x80-\xFF"; // superset of IDN
|
||||
return (bool) preg_match(<<<XX
|
||||
(^
|
||||
("([ !#-[\\]-~]*|\\\\[ -~])+"|$atom+(\\.$atom+)*) # quoted or unquoted
|
||||
@
|
||||
([0-9$alpha]([-0-9$alpha]{0,61}[0-9$alpha])?\\.)+ # domain - RFC 1034
|
||||
[$alpha]([-0-9$alpha]{0,17}[$alpha])? # top domain
|
||||
$)Dix
|
||||
XX
|
||||
, $value);
|
||||
(^(?n)
|
||||
("([ !#-[\\]-~]*|\\\\[ -~])+"|$atom+(\\.$atom+)*) # quoted or unquoted
|
||||
@
|
||||
([0-9$alpha]([-0-9$alpha]{0,61}[0-9$alpha])?\\.)+ # domain - RFC 1034
|
||||
[$alpha]([-0-9$alpha]{0,17}[$alpha])? # top domain
|
||||
$)Dix
|
||||
XX, $value);
|
||||
}
|
||||
|
||||
|
||||
@@ -338,20 +339,19 @@ XX
|
||||
{
|
||||
$alpha = "a-z\x80-\xFF";
|
||||
return (bool) preg_match(<<<XX
|
||||
(^
|
||||
https?://(
|
||||
(([-_0-9$alpha]+\\.)* # subdomain
|
||||
[0-9$alpha]([-0-9$alpha]{0,61}[0-9$alpha])?\\.)? # domain
|
||||
[$alpha]([-0-9$alpha]{0,17}[$alpha])? # top domain
|
||||
|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3} # IPv4
|
||||
|\\[[0-9a-f:]{3,39}\\] # IPv6
|
||||
)(:\\d{1,5})? # port
|
||||
(/\\S*)? # path
|
||||
(\\?\\S*)? # query
|
||||
(\\#\\S*)? # fragment
|
||||
$)Dix
|
||||
XX
|
||||
, $value);
|
||||
(^(?n)
|
||||
https?://(
|
||||
(([-_0-9$alpha]+\\.)* # subdomain
|
||||
[0-9$alpha]([-0-9$alpha]{0,61}[0-9$alpha])?\\.)? # domain
|
||||
[$alpha]([-0-9$alpha]{0,17}[$alpha])? # top domain
|
||||
|\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3} # IPv4
|
||||
|\\[[0-9a-f:]{3,39}\\] # IPv6
|
||||
)(:\\d{1,5})? # port
|
||||
(/\\S*)? # path
|
||||
(\\?\\S*)? # query
|
||||
(\\#\\S*)? # fragment
|
||||
$)Dix
|
||||
XX, $value);
|
||||
}
|
||||
|
||||
|
||||
@@ -366,6 +366,7 @@ XX
|
||||
|
||||
/**
|
||||
* Checks whether the input is a class, interface or trait.
|
||||
* @deprecated
|
||||
*/
|
||||
public static function isType(string $type): bool
|
||||
{
|
||||
@@ -380,4 +381,37 @@ XX
|
||||
{
|
||||
return preg_match('#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#D', $value) === 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines if type is PHP built-in type. Otherwise, it is the class name.
|
||||
*/
|
||||
public static function isBuiltinType(string $type): bool
|
||||
{
|
||||
return isset(self::BuiltinTypes[strtolower($type)]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines if type is special class name self/parent/static.
|
||||
*/
|
||||
public static function isClassKeyword(string $name): bool
|
||||
{
|
||||
return (bool) preg_match('#^(self|parent|static)$#Di', $name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether the given type declaration is syntactically valid.
|
||||
*/
|
||||
public static function isTypeDeclaration(string $type): bool
|
||||
{
|
||||
return (bool) preg_match(<<<'XX'
|
||||
~((?n)
|
||||
\?? (?<type> \\? (?<name> [a-zA-Z_\x7f-\xff][\w\x7f-\xff]*) (\\ (?&name))* ) |
|
||||
(?<intersection> (?&type) (& (?&type))+ ) |
|
||||
(?<upart> (?&type) | \( (?&intersection) \) ) (\| (?&upart))+
|
||||
)$~xAD
|
||||
XX, $type);
|
||||
}
|
||||
}
|
||||
|
||||
20
vendor/nette/utils/src/Utils/exceptions.php
vendored
20
vendor/nette/utils/src/Utils/exceptions.php
vendored
@@ -11,7 +11,7 @@ namespace Nette\Utils;
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when an image error occurs.
|
||||
* An error occurred while working with the image.
|
||||
*/
|
||||
class ImageException extends \Exception
|
||||
{
|
||||
@@ -19,7 +19,7 @@ class ImageException extends \Exception
|
||||
|
||||
|
||||
/**
|
||||
* The exception that indicates invalid image file.
|
||||
* The image file is invalid or in an unsupported format.
|
||||
*/
|
||||
class UnknownImageFileException extends ImageException
|
||||
{
|
||||
@@ -27,31 +27,23 @@ class UnknownImageFileException extends ImageException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that indicates error of JSON encoding/decoding.
|
||||
* JSON encoding or decoding failed.
|
||||
*/
|
||||
class JsonException extends \Exception
|
||||
class JsonException extends \JsonException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The exception that indicates error of the last Regexp execution.
|
||||
* Regular expression pattern or execution failed.
|
||||
*/
|
||||
class RegexpException extends \Exception
|
||||
{
|
||||
public const MESSAGES = [
|
||||
PREG_INTERNAL_ERROR => 'Internal error',
|
||||
PREG_BACKTRACK_LIMIT_ERROR => 'Backtrack limit was exhausted',
|
||||
PREG_RECURSION_LIMIT_ERROR => 'Recursion limit was exhausted',
|
||||
PREG_BAD_UTF8_ERROR => 'Malformed UTF-8 data',
|
||||
PREG_BAD_UTF8_OFFSET_ERROR => 'Offset didn\'t correspond to the begin of a valid UTF-8 code point',
|
||||
6 => 'Failed due to limited JIT stack space', // PREG_JIT_STACKLIMIT_ERROR
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The exception that indicates assertion error.
|
||||
* Type validation failed. The value doesn't match the expected type constraints.
|
||||
*/
|
||||
class AssertionException extends \Exception
|
||||
{
|
||||
|
||||
35
vendor/nette/utils/src/exceptions.php
vendored
35
vendor/nette/utils/src/exceptions.php
vendored
@@ -11,8 +11,7 @@ namespace Nette;
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when the value of an argument is
|
||||
* outside the allowable range of values as defined by the invoked method.
|
||||
* The value is outside the allowed range.
|
||||
*/
|
||||
class ArgumentOutOfRangeException extends \InvalidArgumentException
|
||||
{
|
||||
@@ -20,8 +19,7 @@ class ArgumentOutOfRangeException extends \InvalidArgumentException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when a method call is invalid for the object's
|
||||
* current state, method has been invoked at an illegal or inappropriate time.
|
||||
* The object is in a state that does not allow the requested operation.
|
||||
*/
|
||||
class InvalidStateException extends \RuntimeException
|
||||
{
|
||||
@@ -29,7 +27,7 @@ class InvalidStateException extends \RuntimeException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when a requested method or operation is not implemented.
|
||||
* The requested feature is not implemented.
|
||||
*/
|
||||
class NotImplementedException extends \LogicException
|
||||
{
|
||||
@@ -37,8 +35,7 @@ class NotImplementedException extends \LogicException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when an invoked method is not supported. For scenarios where
|
||||
* it is sometimes possible to perform the requested operation, see InvalidStateException.
|
||||
* The requested operation is not supported.
|
||||
*/
|
||||
class NotSupportedException extends \LogicException
|
||||
{
|
||||
@@ -46,7 +43,7 @@ class NotSupportedException extends \LogicException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when a requested method or operation is deprecated.
|
||||
* The requested feature is deprecated and no longer available.
|
||||
*/
|
||||
class DeprecatedException extends NotSupportedException
|
||||
{
|
||||
@@ -54,7 +51,7 @@ class DeprecatedException extends NotSupportedException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when accessing a class member (property or method) fails.
|
||||
* Cannot access the requested class property or method.
|
||||
*/
|
||||
class MemberAccessException extends \Error
|
||||
{
|
||||
@@ -62,7 +59,7 @@ class MemberAccessException extends \Error
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when an I/O error occurs.
|
||||
* Failed to read from or write to a file or stream.
|
||||
*/
|
||||
class IOException extends \RuntimeException
|
||||
{
|
||||
@@ -70,7 +67,7 @@ class IOException extends \RuntimeException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when accessing a file that does not exist on disk.
|
||||
* The requested file does not exist.
|
||||
*/
|
||||
class FileNotFoundException extends IOException
|
||||
{
|
||||
@@ -78,7 +75,7 @@ class FileNotFoundException extends IOException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when part of a file or directory cannot be found.
|
||||
* The requested directory does not exist.
|
||||
*/
|
||||
class DirectoryNotFoundException extends IOException
|
||||
{
|
||||
@@ -86,7 +83,7 @@ class DirectoryNotFoundException extends IOException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when an argument does not match with the expected value.
|
||||
* The provided argument has invalid type or format.
|
||||
*/
|
||||
class InvalidArgumentException extends \InvalidArgumentException
|
||||
{
|
||||
@@ -94,7 +91,7 @@ class InvalidArgumentException extends \InvalidArgumentException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when an illegal index was requested.
|
||||
* The requested array or collection index does not exist.
|
||||
*/
|
||||
class OutOfRangeException extends \OutOfRangeException
|
||||
{
|
||||
@@ -102,8 +99,16 @@ class OutOfRangeException extends \OutOfRangeException
|
||||
|
||||
|
||||
/**
|
||||
* The exception that is thrown when a value (typically returned by function) does not match with the expected value.
|
||||
* The returned value has unexpected type or format.
|
||||
*/
|
||||
class UnexpectedValueException extends \UnexpectedValueException
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Houston, we have a problem.
|
||||
*/
|
||||
class ShouldNotHappenException extends \LogicException
|
||||
{
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user