INIT
This commit is contained in:
commit
d78b38e80f
24
.env.case
Normal file
24
.env.case
Normal file
@ -0,0 +1,24 @@
|
||||
appid=""
|
||||
#企业微信的企业ID
|
||||
|
||||
|
||||
agentid=""
|
||||
#企业微信APPID
|
||||
|
||||
token=""
|
||||
#企业微信APP Token
|
||||
|
||||
weburl="auth.laysense.cn"
|
||||
#本站URL
|
||||
|
||||
|
||||
aeskey=""
|
||||
#16位AES加密key
|
||||
|
||||
|
||||
aesiv=""
|
||||
#16位AES加密iv
|
||||
|
||||
dbuser=""
|
||||
dbpass=""
|
||||
#数据库用户名和密码
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
.env
|
||||
token/qywx/*.token
|
||||
runtime/*/*.log
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 walkor<walkor@workerman.net> and contributors (see https://github.com/walkor/webman/contributors)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
10
README.md
Normal file
10
README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Webman INIT
|
||||
|
||||
Webman的启动模板:
|
||||
|
||||
```
|
||||
wget https://git.laysense.com/enoch/webmaninit/archive/main.tar.gz
|
||||
tar -zxvf ./main.tar.gz
|
||||
rm -rf ./main.tar.gz
|
||||
mv webmaninit/* ./
|
||||
```
|
28
app/controller/IndexController.php
Normal file
28
app/controller/IndexController.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use support\Request;
|
||||
|
||||
class IndexController
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
static $readme;
|
||||
if (!$readme) {
|
||||
$readme = file_get_contents(base_path('README.md'));
|
||||
}
|
||||
return $readme;
|
||||
}
|
||||
|
||||
public function view(Request $request)
|
||||
{
|
||||
return view('index/view', ['name' => 'webman']);
|
||||
}
|
||||
|
||||
public function json(Request $request)
|
||||
{
|
||||
return json(['code' => 0, 'msg' => 'ok']);
|
||||
}
|
||||
|
||||
}
|
88
app/controller/LayAuth.php
Normal file
88
app/controller/LayAuth.php
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use support\Request;
|
||||
use support\Db;
|
||||
use yzh52521\EasyHttp\Http;
|
||||
use yzh52521\EasyHttp\Response;
|
||||
use yzh52521\EasyHttp\RequestException;
|
||||
|
||||
class LayAuth
|
||||
{
|
||||
public function index(Request $request,$appid)
|
||||
{
|
||||
$appquery= Db::table('App')->where('ID', $appid);
|
||||
if($appquery->doesntExist()){
|
||||
return view('404');
|
||||
}
|
||||
$app=$appquery->first();
|
||||
$provider= Db::table('Provider')->where('ID', $app->provider)->first();
|
||||
return view('auth', ['app'=>$app,'provider'=>$provider]);
|
||||
|
||||
}
|
||||
|
||||
public function callback(Request $request,$appid,$gateway)
|
||||
{
|
||||
$appquery= Db::table('App')->where('ID', $appid);
|
||||
if($appquery->doesntExist()){
|
||||
return view('404');
|
||||
}
|
||||
$app=$appquery->first();
|
||||
$provider= Db::table('Provider')->where('ID', $app->provider)->first();
|
||||
switch ($gateway) {
|
||||
case "qywx":
|
||||
$code = $request->input('code','null');
|
||||
if($code=='null'){
|
||||
return view('auth', ['app'=>$app,'provider'=>$provider,'special'=>'登陆信息无效']);
|
||||
}
|
||||
$tokenfile=base_path().'/token/qywx/innerQYWX.token';
|
||||
if(file_exists($tokenfile)){
|
||||
$tokencontent=json_decode(file_get_contents($tokenfile));
|
||||
$fulltoken=$tokencontent->token;
|
||||
$ddl=$tokencontent->ddl;
|
||||
if($ddl-time()<180){
|
||||
$reapply=true;
|
||||
}else{
|
||||
$reapply=false;
|
||||
}
|
||||
}else{
|
||||
$reapply=true;
|
||||
}
|
||||
if($reapply==true){
|
||||
$apply=$response = Http::get('https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid='.getenv('appid').'&corpsecret='.getenv('token'))->json();
|
||||
$fulltoken=$apply->access_token;
|
||||
$ddl=time()+$apply->expires_in;
|
||||
$file=fopen($tokenfile,"w");
|
||||
fwrite($file, json_encode(array('token'=>$fulltoken,'ddl'=>$ddl)));
|
||||
fclose($file);
|
||||
}
|
||||
$lookup= Http::get('https://qyapi.weixin.qq.com/cgi-bin/auth/getuserinfo?access_token='.$fulltoken.'&code='.$code)->json();
|
||||
if($lookup->errcode!=0){
|
||||
return view('auth', ['app'=>$app,'provider'=>$provider,'special'=>'登陆信息无效']);
|
||||
}else{
|
||||
$userid=$lookup->userid;
|
||||
$userinfo=Http::get('https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token='.$fulltoken.'&userid='.$userid)->json();
|
||||
$username=$userinfo->name;
|
||||
$userposition=$userinfo->position;
|
||||
$WT=json_encode(['id'=>$userid,'name'=>$username,'position'=>$userposition,'time'=>time()]);
|
||||
$key = getenv('aeskey');
|
||||
$iv = getenv('aesiv');
|
||||
$WT = encryptAES($WT, $key, $iv);
|
||||
$dest=$app->redirect;
|
||||
return view('success', ['app'=>$app,'provider'=>$provider,'dest'=>$dest,'userinfo'=>$userinfo])->cookie('WT', $WT,time()+9600,'/','.laysense.cn');
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return view('auth', ['app'=>$app,'provider'=>$provider,'special'=>'验证方式无效或不存在']);
|
||||
}
|
||||
|
||||
return view('auth', ['app'=>$app,'provider'=>$provider,'special'=>'验证方式无效或不存在']);
|
||||
}
|
||||
|
||||
public function check(Request $request,$appid)
|
||||
{
|
||||
return json(['code' => 0, 'msg' => 'ok']);
|
||||
}
|
||||
|
||||
}
|
4
app/functions.php
Normal file
4
app/functions.php
Normal file
@ -0,0 +1,4 @@
|
||||
<?php
|
||||
/**
|
||||
* Here is your custom functions.
|
||||
*/
|
42
app/middleware/StaticFile.php
Normal file
42
app/middleware/StaticFile.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace app\middleware;
|
||||
|
||||
use Webman\MiddlewareInterface;
|
||||
use Webman\Http\Response;
|
||||
use Webman\Http\Request;
|
||||
|
||||
/**
|
||||
* Class StaticFile
|
||||
* @package app\middleware
|
||||
*/
|
||||
class StaticFile implements MiddlewareInterface
|
||||
{
|
||||
public function process(Request $request, callable $next): Response
|
||||
{
|
||||
// Access to files beginning with. Is prohibited
|
||||
if (strpos($request->path(), '/.') !== false) {
|
||||
return response('<h1>403 forbidden</h1>', 403);
|
||||
}
|
||||
/** @var Response $response */
|
||||
$response = $next($request);
|
||||
// Add cross domain HTTP header
|
||||
/*$response->withHeaders([
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Credentials' => 'true',
|
||||
]);*/
|
||||
return $response;
|
||||
}
|
||||
}
|
29
app/model/Test.php
Normal file
29
app/model/Test.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace app\model;
|
||||
|
||||
use support\Model;
|
||||
|
||||
class Test extends Model
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'test';
|
||||
|
||||
/**
|
||||
* The primary key associated with the table.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $primaryKey = 'id';
|
||||
|
||||
/**
|
||||
* Indicates if the model should be timestamped.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $timestamps = false;
|
||||
}
|
68
app/view/404.html
Normal file
68
app/view/404.html
Normal file
@ -0,0 +1,68 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh" class="h-full">
|
||||
<head>
|
||||
<!-- Required Meta Tags Always Come First -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="Laysense Auth">
|
||||
|
||||
|
||||
<!-- Title -->
|
||||
<title>Laysense Auth</title>
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="shortcut icon" href="https://static.laysense.cn/data/file/_4335ce.png?1709117691655">
|
||||
|
||||
<!-- Font -->
|
||||
|
||||
<!-- Theme Check and Update -->
|
||||
<script>
|
||||
const html = document.querySelector('html');
|
||||
const isLightOrAuto = localStorage.getItem('hs_theme') === 'light' || (localStorage.getItem('hs_theme') === 'auto' && !window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
const isDarkOrAuto = localStorage.getItem('hs_theme') === 'dark' || (localStorage.getItem('hs_theme') === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
|
||||
if (isLightOrAuto && html.classList.contains('dark')) html.classList.remove('dark');
|
||||
else if (isDarkOrAuto && html.classList.contains('light')) html.classList.remove('light');
|
||||
else if (isDarkOrAuto && !html.classList.contains('dark')) html.classList.add('dark');
|
||||
else if (isLightOrAuto && !html.classList.contains('light')) html.classList.add('light');
|
||||
</script>
|
||||
|
||||
<!-- CSS HS -->
|
||||
<link rel="stylesheet" href="https://static.laysense.cn/data/file/cdn/preline.css">
|
||||
</head>
|
||||
|
||||
<body class="flex h-full items-center py-16 dark:bg-neutral-800" background="https://bing.img.run/1920x1080.php">
|
||||
<!-- ========== MAIN CONTENT ========== -->
|
||||
<main id="content" class="w-full max-w-md mx-auto p-6">
|
||||
|
||||
<div class="mt-7 border border-gray-200 rounded-xl shadow-sm dark:bg-neutral-900 dark:border-neutral-700" style="background: rgba(255, 255, 255, 0.6);-webkit-backdrop-filter: blur(10px);backdrop-filter: blur(10px);">
|
||||
<div class="p-4 sm:p-7">
|
||||
<div class="text-center">
|
||||
<h1 class="block text-2xl font-bold text-gray-800 dark:text-white" ><img src="https://static.laysense.cn/data/file/laysenseW.png" style="height:30px;" />应用未注册或不存在</h1>
|
||||
<p class="mt-2 text-sm text-gray-600 dark:text-neutral-400">
|
||||
抱歉,未能找到该应用的注册信息,可能是应用不存在或已被删除,您可以选择
|
||||
<a class="text-blue-600 decoration-2 hover:underline focus:outline-none focus:underline font-medium dark:text-blue-500" href="https://laysense.cn/" target="_blank">
|
||||
前往来笙
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="mt-3 flex justify-center items-center text-center divide-x divide-gray-300 dark:divide-neutral-700">
|
||||
©上海来笙信息科技有限公司 2024 </p>
|
||||
|
||||
</main>
|
||||
<!-- ========== END MAIN CONTENT ========== -->
|
||||
|
||||
<!-- JS Implementing Plugins -->
|
||||
|
||||
<!-- JS PLUGINS -->
|
||||
<!-- Required plugins -->
|
||||
<script src="https://static.laysense.cn/data/file/cdn/preline.js"></script>
|
||||
|
||||
<!-- JS THIRD PARTY PLUGINS -->
|
||||
</body>
|
||||
</html>
|
146
app/view/auth.html
Normal file
146
app/view/auth.html
Normal file
@ -0,0 +1,146 @@
|
||||
<?php if(!isset($special)){ $special=false;}?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh" class="h-full">
|
||||
<head>
|
||||
<!-- Required Meta Tags Always Come First -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="Laysense Auth">
|
||||
|
||||
|
||||
<!-- Title -->
|
||||
<title>Laysense Auth</title>
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="shortcut icon" href="https://static.laysense.cn/data/file/_4335ce.png?1709117691655">
|
||||
|
||||
<!-- Font -->
|
||||
|
||||
<!-- Theme Check and Update -->
|
||||
<script>
|
||||
const html = document.querySelector('html');
|
||||
const isLightOrAuto = localStorage.getItem('hs_theme') === 'light' || (localStorage.getItem('hs_theme') === 'auto' && !window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
const isDarkOrAuto = localStorage.getItem('hs_theme') === 'dark' || (localStorage.getItem('hs_theme') === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
|
||||
if (isLightOrAuto && html.classList.contains('dark')) html.classList.remove('dark');
|
||||
else if (isDarkOrAuto && html.classList.contains('light')) html.classList.remove('light');
|
||||
else if (isDarkOrAuto && !html.classList.contains('dark')) html.classList.add('dark');
|
||||
else if (isLightOrAuto && !html.classList.contains('light')) html.classList.add('light');
|
||||
</script>
|
||||
|
||||
<!-- CSS HS -->
|
||||
<link rel="stylesheet" href="https://static.laysense.cn/data/file/cdn/preline.css">
|
||||
</head>
|
||||
|
||||
<body class="flex h-full items-center py-16 dark:bg-neutral-800" background="https://bing.img.run/1920x1080.php" style="background-size: 100% 100%;">
|
||||
<!-- ========== MAIN CONTENT ========== -->
|
||||
<main id="content" class="w-full max-w-md mx-auto p-6">
|
||||
|
||||
<div class="mt-7 border border-gray-200 rounded-xl shadow-sm dark:bg-neutral-900 dark:border-neutral-700" style="background: rgba(255, 255, 255, 0.6);-webkit-backdrop-filter: blur(10px);backdrop-filter: blur(10px);">
|
||||
<div class="p-4 sm:p-7">
|
||||
<div class="text-center">
|
||||
<h1 class="block text-2xl font-bold text-gray-800 dark:text-white" ><img src="https://static.laysense.cn/data/file/laysenseW.png" style="height:30px;" />来笙统一认证</h1>
|
||||
<p class="mt-2 text-sm text-gray-600 dark:text-neutral-400">
|
||||
没有账号?
|
||||
<a class="text-blue-600 decoration-2 hover:underline focus:outline-none focus:underline font-medium dark:text-blue-500" href="javascript:alert('暂未开放');">
|
||||
注册来笙账号
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="py-3 flex items-center text-xs text-gray-400 uppercase before:flex-1 before:border-t before:border-gray-200 before:me-6 after:flex-1 after:border-t after:border-gray-200 after:ms-6 dark:text-neutral-500 dark:before:border-neutral-600 dark:after:border-neutral-600">您即将登录</div>
|
||||
|
||||
<div class="shrink-0 group block ">
|
||||
<div class="flex items-center">
|
||||
<img class="inline-block shrink-0 size-[62px] rounded-full" src="<?php echo($app->img);?>" alt="Avatar">
|
||||
<div class="ms-3">
|
||||
<h3 class="font-semibold text-gray-800 dark:text-white"><?php echo($app->name);?></h3>
|
||||
<p class="text-sm font-medium text-gray-400 dark:text-neutral-500"><?php echo($app->info);?></p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-gray-600 dark:text-neutral-400 text-center" >
|
||||
由
|
||||
<a class="text-blue-600 decoration-2 hover:underline focus:outline-none focus:underline font-medium dark:text-blue-500" href="<?php echo($provider->url);?>" target="_blank">
|
||||
<?php echo($provider->name);?>
|
||||
</a>
|
||||
提供
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="py-3 flex items-center text-xs text-gray-400 uppercase before:flex-1 before:border-t before:border-gray-200 before:me-6 after:flex-1 after:border-t after:border-gray-200 after:ms-6 dark:text-neutral-500 dark:before:border-neutral-600 dark:after:border-neutral-600">公共登录</div>
|
||||
|
||||
<div class="flex flex-row <?php if($app->public==false){echo("hidden");}?>" >
|
||||
<button type="button" class="mx-2 px-6 mb-4 w-full py-3 px-4 inline-flex justify-center items-center gap-x-2 text-sm font-medium rounded-lg border border-blue-600 text-blue-600 hover:border-blue-500 hover:text-blue-500 focus:outline-none focus:border-blue-500 focus:text-blue-500 disabled:opacity-50 disabled:pointer-events-none dark:border-blue-500 dark:text-blue-500 dark:hover:text-blue-400 dark:hover:border-blue-400">
|
||||
<svg t="1722956204076" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5325" width="24" height="24"><path d="M0 0h486.592v486.592H0z" fill="#F25022" p-id="5326"></path><path d="M537.408 0H1024v486.592H537.408z" fill="#7FBA00" p-id="5327"></path><path d="M0 537.408h486.592V1024H0z" fill="#00A4EF" p-id="5328"></path><path d="M537.408 537.408H1024V1024H537.408z" fill="#FFB900" p-id="5329"></path></svg>
|
||||
Microsoft
|
||||
</button><button type="button" class="mx-2 mb-4 w-full py-3 px-4 inline-flex justify-center items-center gap-x-2 text-sm font-medium rounded-lg border border-gray-800 text-gray-800 hover:border-gray-500 hover:text-gray-500 focus:outline-none focus:border-gray-500 focus:text-gray-500 disabled:opacity-50 disabled:pointer-events-none dark:border-white dark:text-white dark:hover:text-neutral-300 dark:hover:border-neutral-300">
|
||||
<svg t="1722956228995" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6336" width="24" height="24"><path d="M64 512c0 195.2 124.8 361.6 300.8 422.4 22.4 6.4 19.2-9.6 19.2-22.4v-76.8c-134.4 16-140.8-73.6-150.4-89.6-19.2-32-60.8-38.4-48-54.4 32-16 64 3.2 99.2 57.6 25.6 38.4 76.8 32 105.6 25.6 6.4-22.4 19.2-44.8 35.2-60.8-144-22.4-201.6-108.8-201.6-211.2 0-48 16-96 48-131.2-22.4-60.8 0-115.2 3.2-121.6 57.6-6.4 118.4 41.6 124.8 44.8 32-9.6 70.4-12.8 112-12.8 41.6 0 80 6.4 112 12.8 12.8-9.6 67.2-48 121.6-44.8 3.2 6.4 25.6 57.6 6.4 118.4 32 38.4 48 83.2 48 131.2 0 102.4-57.6 188.8-201.6 214.4 22.4 22.4 38.4 54.4 38.4 92.8v112c0 9.6 0 19.2 16 19.2C832 876.8 960 710.4 960 512c0-246.4-201.6-448-448-448S64 265.6 64 512z" fill="#040000" p-id="6337"></path></svg>
|
||||
Github
|
||||
</button>
|
||||
</div>
|
||||
<p class="text-center mt-2 text-sm text-gray-600 dark:text-neutral-400 <?php if($app->public==true){echo("hidden");}?>" >
|
||||
当前应用仅支持来笙内部使用,不对外开放
|
||||
</p>
|
||||
|
||||
<div class="py-3 flex items-center text-xs text-gray-400 uppercase before:flex-1 before:border-t before:border-gray-200 before:me-6 after:flex-1 after:border-t after:border-gray-200 after:ms-6 dark:text-neutral-500 dark:before:border-neutral-600 dark:after:border-neutral-600">来笙内部登录</div>
|
||||
|
||||
<div >
|
||||
<a href="https://login.work.weixin.qq.com/wwlogin/sso/login?login_type=CorpApp&appid=<?php echo(getenv('appid'));?>&agentid=<?php echo(getenv('agentid'));?>&redirect_uri=https://<?php echo(getenv('weburl'));?>/auth/lay/<?php echo($app->ID);?>/qywx/callback" class=" mb-4 w-full py-3 px-4 inline-flex justify-center items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-teal-100 text-teal-800 hover:bg-teal-200 focus:outline-none focus:bg-teal-200 disabled:opacity-50 disabled:pointer-events-none dark:text-teal-500 dark:bg-teal-800/30 dark:hover:bg-teal-800/20 dark:focus:bg-teal-800/20">
|
||||
<svg t="1722953611732" class="icon" viewBox="0 0 1229 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4326" width="24" height="24"><path d="M702.72 849.92c-76.8 30.72-158.72 35.84-240.64 30.72-35.84-5.12-71.68-10.24-107.52-20.48-5.12 0-10.24 0-15.36 5.12-46.08 20.48-92.16 46.08-133.12 66.56-15.36 10.24-30.72 10.24-46.08 0s-15.36-25.6-15.36-46.08c10.24-35.84 10.24-71.68 15.36-107.52 0-5.12-5.12-10.24-5.12-15.36-51.2-51.2-92.16-102.4-122.88-168.96-51.2-122.88-40.96-245.76 30.72-358.4C134.4 112.64 247.04 46.08 380.16 15.36S641.28 0 764.16 61.44c112.64 56.32 194.56 143.36 230.4 266.24 15.36 46.08 20.48 92.16 15.36 138.24-25.6-25.6-56.32-30.72-87.04-15.36 0-30.72 0-61.44-10.24-92.16-20.48-71.68-61.44-128-112.64-174.08-87.04-71.68-194.56-102.4-307.2-102.4-117.76 10.24-220.16 51.2-302.08 133.12-66.56 66.56-102.4 148.48-97.28 245.76 5.12 81.92 40.96 148.48 92.16 204.8l40.96 40.96c20.48 15.36 25.6 30.72 15.36 51.2-5.12 20.48-10.24 46.08-15.36 66.56 0 5.12-5.12 10.24 0 10.24 5.12 5.12 10.24 0 10.24 0 25.6-15.36 56.32-30.72 81.92-51.2 15.36-10.24 30.72-10.24 51.2-5.12 87.04 25.6 179.2 25.6 266.24 0 5.12 0 10.24-5.12 10.24 5.12 10.24 30.72 25.6 51.2 56.32 66.56z" fill="#0082EF" p-id="4327"></path><path d="M1214.72 747.52c0 35.84-25.6 61.44-56.32 66.56-51.2 10.24-92.16 30.72-128 66.56-10.24 10.24-15.36 10.24-25.6 5.12-5.12-5.12-5.12-15.36 0-25.6 35.84-35.84 56.32-81.92 66.56-128 5.12-35.84 40.96-56.32 76.8-56.32 40.96 5.12 66.56 35.84 66.56 71.68z" fill="#0081EE" p-id="4328"></path><path d="M953.6 1024c-35.84 0-66.56-25.6-71.68-56.32-5.12-51.2-30.72-92.16-66.56-122.88-5.12-5.12-10.24-10.24-5.12-20.48 5.12-15.36 15.36-15.36 25.6-10.24 10.24 5.12 15.36 15.36 20.48 20.48 30.72 25.6 66.56 40.96 102.4 46.08 35.84 5.12 61.44 40.96 56.32 76.8 5.12 35.84-25.6 66.56-61.44 66.56z" fill="#FA6202" p-id="4329"></path><path d="M682.24 757.76c0-35.84 20.48-61.44 56.32-71.68 51.2-10.24 92.16-30.72 128-66.56 10.24-10.24 20.48-10.24 25.6 0 5.12 5.12 5.12 15.36-5.12 25.6-30.72 30.72-51.2 66.56-61.44 112.64 0 5.12 0 15.36-5.12 20.48-10.24 35.84-40.96 56.32-76.8 51.2-35.84-5.12-61.44-35.84-61.44-71.68z" fill="#FECD00" p-id="4330"></path><path d="M1035.52 578.56c15.36 30.72 30.72 56.32 51.2 76.8 10.24 10.24 10.24 20.48 5.12 25.6-5.12 10.24-15.36 10.24-25.6 0-25.6-30.72-61.44-51.2-97.28-61.44-10.24-5.12-20.48-5.12-30.72-5.12-20.48-5.12-40.96-15.36-46.08-40.96-10.24-25.6-10.24-51.2 10.24-71.68 20.48-25.6 46.08-30.72 71.68-25.6 25.6 10.24 46.08 25.6 51.2 56.32 0 15.36 5.12 30.72 10.24 46.08z" fill="#2CBD00" p-id="4331"></path></svg>
|
||||
企业微信登录
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toast -->
|
||||
<div class="absolute top-3 end-3 <?php if($special=='false'){echo "hidden";}?>" >
|
||||
<div class="max-w-xs bg-white border border-gray-200 rounded-xl shadow-lg dark:bg-neutral-800 dark:border-neutral-700" role="alert" tabindex="-1" aria-labelledby="hs-toast-warning-example-label">
|
||||
<div class="flex p-4">
|
||||
<div class="shrink-0">
|
||||
<svg class="shrink-0 size-4 text-yellow-500 mt-0.5" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16">
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM8 4a.905.905 0 0 0-.9.995l.35 3.507a.552.552 0 0 0 1.1 0l.35-3.507A.905.905 0 0 0 8 4zm.002 6a1 1 0 1 0 0 2 1 1 0 0 0 0-2z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="ms-3">
|
||||
<p id="hs-toast-warning-example-label" class="text-sm text-gray-700 dark:text-neutral-400">
|
||||
登录未能完成:<?php echo($special);?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- End Toast -->
|
||||
|
||||
|
||||
<p class="mt-3 flex justify-center items-center text-center divide-x divide-gray-300 dark:divide-neutral-700">
|
||||
<a class="pe-3.5 inline-flex items-center gap-x-2 text-sm text-gray-600 decoration-2 hover:underline hover:text-blue-600 dark:text-neutral-500 dark:hover:text-neutral-200" href="../../examples.html">
|
||||
<svg t="1722960727914" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3218" width="20" height="20"><path d="M960.288 787.488c-98.88-154.08-287.36-469.568-385.76-622.912-21.44-27.968-71.872-44-102.88 0L61.504 803.872c-23.36 33.888-23.008 79.872 49.376 82.432h824.64c48.416-2.784 48.416-62.496 24.768-98.816z m-437.44-27.776a47.296 47.296 0 1 1 0-94.592 47.296 47.296 0 0 1 0 94.592z m35.456-165.536c0.448 11.52-10.944 23.68-23.648 23.68h-23.648c-12.672 0-23.2-12.16-23.616-23.68l-23.68-224.64c0-19.552 15.904-35.456 35.488-35.456h47.296c19.584 0 35.456 15.904 35.456 35.488l-23.648 224.64z" fill="#111111" p-id="3219"></path></svg>
|
||||
举报该应用
|
||||
</a>
|
||||
</p>
|
||||
<p class="mt-3 flex justify-center items-center text-center divide-x divide-gray-300 dark:divide-neutral-700">
|
||||
©上海来笙信息科技有限公司 2024 </p>
|
||||
|
||||
<p class="text-sm mt-3 flex justify-center items-center text-center divide-x divide-gray-300 dark:divide-neutral-700">
|
||||
|
||||
<a class="mx text-blue-600 decoration-2 hover:underline focus:outline-none focus:underline dark:text-blue-500" href="https://laysense.cn" target="_blank">
|
||||
来笙隐私条款 
|
||||
</a><a class="text-blue-600 decoration-2 hover:underline focus:outline-none focus:underline dark:text-blue-500" href="https://laysense.cn" target="_blank">
|
||||
 此应用的隐私政策
|
||||
</a>
|
||||
</p>
|
||||
</main>
|
||||
<!-- ========== END MAIN CONTENT ========== -->
|
||||
|
||||
<!-- JS Implementing Plugins -->
|
||||
|
||||
<!-- JS PLUGINS -->
|
||||
<!-- Required plugins -->
|
||||
<script src="https://static.laysense.cn/data/file/cdn/preline.js"></script>
|
||||
|
||||
<!-- JS THIRD PARTY PLUGINS -->
|
||||
</body>
|
||||
</html>
|
101
app/view/success.html
Normal file
101
app/view/success.html
Normal file
@ -0,0 +1,101 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh" class="h-full">
|
||||
<head>
|
||||
<!-- Required Meta Tags Always Come First -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="description" content="Laysense Auth">
|
||||
|
||||
|
||||
<!-- Title -->
|
||||
<title>Laysense Auth</title>
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="shortcut icon" href="https://static.laysense.cn/data/file/_4335ce.png?1709117691655">
|
||||
|
||||
<!-- Font -->
|
||||
|
||||
<!-- Theme Check and Update -->
|
||||
<script>
|
||||
const html = document.querySelector('html');
|
||||
const isLightOrAuto = localStorage.getItem('hs_theme') === 'light' || (localStorage.getItem('hs_theme') === 'auto' && !window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
const isDarkOrAuto = localStorage.getItem('hs_theme') === 'dark' || (localStorage.getItem('hs_theme') === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
||||
|
||||
if (isLightOrAuto && html.classList.contains('dark')) html.classList.remove('dark');
|
||||
else if (isDarkOrAuto && html.classList.contains('light')) html.classList.remove('light');
|
||||
else if (isDarkOrAuto && !html.classList.contains('dark')) html.classList.add('dark');
|
||||
else if (isLightOrAuto && !html.classList.contains('light')) html.classList.add('light');
|
||||
</script>
|
||||
|
||||
<!-- CSS HS -->
|
||||
<link rel="stylesheet" href="https://static.laysense.cn/data/file/cdn/preline.css">
|
||||
</head>
|
||||
|
||||
<body class="flex h-full items-center py-16 dark:bg-neutral-800" background="https://bing.img.run/1920x1080.php" style="background-size: 100% 100%;">
|
||||
<!-- ========== MAIN CONTENT ========== -->
|
||||
<main id="content" class="w-full max-w-md mx-auto p-6">
|
||||
|
||||
<div class="mt-7 border border-gray-200 rounded-xl shadow-sm dark:bg-neutral-900 dark:border-neutral-700" style="background: rgba(255, 255, 255, 0.6);-webkit-backdrop-filter: blur(10px);backdrop-filter: blur(10px);">
|
||||
<div class="p-4 sm:p-7">
|
||||
<div class="text-center">
|
||||
<h1 class="block text-2xl font-bold text-gray-800 dark:text-white" ><img src="https://static.laysense.cn/data/file/laysenseW.png" style="height:30px;" />欢迎回来,<?php echo($userinfo->name);?></h1>
|
||||
<p class="mt-2 text-sm text-gray-600 dark:text-neutral-400">
|
||||
<?php echo($userinfo->position);?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="py-3 flex items-center text-xs text-gray-400 uppercase before:flex-1 before:border-t before:border-gray-200 before:me-6 after:flex-1 after:border-t after:border-gray-200 after:ms-6 dark:text-neutral-500 dark:before:border-neutral-600 dark:after:border-neutral-600">您即将进入</div>
|
||||
|
||||
<div class="shrink-0 group block ">
|
||||
<div class="flex items-center">
|
||||
<img class="inline-block shrink-0 size-[62px] rounded-full" src="<?php echo($app->img);?>" alt="Avatar">
|
||||
<div class="ms-3">
|
||||
<h3 class="font-semibold text-gray-800 dark:text-white"><?php echo($app->name);?></h3>
|
||||
<p class="text-sm font-medium text-gray-400 dark:text-neutral-500"><?php echo($app->info);?></p>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-2 text-sm text-gray-600 dark:text-neutral-400 text-center" >
|
||||
由
|
||||
<a class="text-blue-600 decoration-2 hover:underline focus:outline-none focus:underline font-medium dark:text-blue-500" href="<?php echo($provider->url);?>" target="_blank">
|
||||
<?php echo($provider->name);?>
|
||||
</a>
|
||||
提供
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="py-3 flex items-center text-xs text-gray-400 uppercase before:flex-1 before:border-t before:border-gray-200 before:me-6 after:flex-1 after:border-t after:border-gray-200 after:ms-6 dark:text-neutral-500 dark:before:border-neutral-600 dark:after:border-neutral-600">正在前往,请稍等</div>
|
||||
|
||||
<div >
|
||||
<button class=" mb-4 w-full py-3 px-4 inline-flex justify-center items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-teal-100 text-teal-800 hover:bg-teal-200 focus:outline-none focus:bg-teal-200 disabled:opacity-50 disabled:pointer-events-none dark:text-teal-500 dark:bg-teal-800/30 dark:hover:bg-teal-800/20 dark:focus:bg-teal-800/20">
|
||||
<div class="animate-spin inline-block size-6 border-[3px] border-current border-t-transparent text-blue-600 rounded-full dark:text-blue-500" role="status" aria-label="loading">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<p class="mt-3 flex justify-center items-center text-center divide-x divide-gray-300 dark:divide-neutral-700">
|
||||
©上海来笙信息科技有限公司 2024 </p>
|
||||
|
||||
|
||||
</main>
|
||||
<!-- ========== END MAIN CONTENT ========== -->
|
||||
|
||||
<!-- JS Implementing Plugins -->
|
||||
|
||||
<!-- JS PLUGINS -->
|
||||
<!-- Required plugins -->
|
||||
<script src="https://static.laysense.cn/data/file/cdn/preline.js"></script>
|
||||
<script>
|
||||
setTimeout("location.href='<?php echo($dest);?>'",3000);
|
||||
</script>
|
||||
<!-- JS THIRD PARTY PLUGINS -->
|
||||
</body>
|
||||
</html>
|
71
composer.json
Normal file
71
composer.json
Normal file
@ -0,0 +1,71 @@
|
||||
{
|
||||
"name": "workerman/webman",
|
||||
"type": "project",
|
||||
"keywords": [
|
||||
"high performance",
|
||||
"http service"
|
||||
],
|
||||
"homepage": "https://www.workerman.net",
|
||||
"license": "MIT",
|
||||
"description": "High performance HTTP Service Framework.",
|
||||
"authors": [
|
||||
{
|
||||
"name": "walkor",
|
||||
"email": "walkor@workerman.net",
|
||||
"homepage": "https://www.workerman.net",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"email": "walkor@workerman.net",
|
||||
"issues": "https://github.com/walkor/webman/issues",
|
||||
"forum": "https://wenda.workerman.net/",
|
||||
"wiki": "https://workerman.net/doc/webman",
|
||||
"source": "https://github.com/walkor/webman"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2",
|
||||
"workerman/webman-framework": "^1.5.0",
|
||||
"monolog/monolog": "^2.0",
|
||||
"illuminate/database": "^9.52",
|
||||
"illuminate/pagination": "^9.52",
|
||||
"illuminate/events": "^9.52",
|
||||
"symfony/var-dumper": "^6.0",
|
||||
"jasongrimes/paginator": "~1.0",
|
||||
"robmorgan/phinx": "^0.14.0",
|
||||
"illuminate/redis": "^9.52",
|
||||
"symfony/cache": "^6.0",
|
||||
"workerman/validation": "^3.1",
|
||||
"symfony/translation": "^6.0",
|
||||
"webman/captcha": "^1.0",
|
||||
"webman/event": "^1.0",
|
||||
"vlucas/phpdotenv": "^5.6",
|
||||
"workerman/crontab": "^1.0",
|
||||
"yzh52521/easyhttp": "^1.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-event": "For better performance. "
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"": "./",
|
||||
"app\\": "./app",
|
||||
"App\\": "./app",
|
||||
"app\\View\\Components\\": "./app/view/components"
|
||||
},
|
||||
"files": [
|
||||
"./support/helpers.php"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"post-package-install": [
|
||||
"support\\Plugin::install"
|
||||
],
|
||||
"post-package-update": [
|
||||
"support\\Plugin::install"
|
||||
],
|
||||
"pre-package-uninstall": [
|
||||
"support\\Plugin::uninstall"
|
||||
]
|
||||
}
|
||||
}
|
4863
composer.lock
generated
Normal file
4863
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
26
config/app.php
Normal file
26
config/app.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use support\Request;
|
||||
|
||||
return [
|
||||
'debug' => true,
|
||||
'error_reporting' => E_ALL,
|
||||
'default_timezone' => 'Asia/Shanghai',
|
||||
'request_class' => Request::class,
|
||||
'public_path' => base_path() . DIRECTORY_SEPARATOR . 'public',
|
||||
'runtime_path' => base_path(false) . DIRECTORY_SEPARATOR . 'runtime',
|
||||
'controller_suffix' => 'Controller',
|
||||
'controller_reuse' => false,
|
||||
];
|
21
config/autoload.php
Normal file
21
config/autoload.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [
|
||||
'files' => [
|
||||
base_path() . '/app/functions.php',
|
||||
base_path() . '/support/Request.php',
|
||||
base_path() . '/support/Response.php',
|
||||
]
|
||||
];
|
18
config/bootstrap.php
Normal file
18
config/bootstrap.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [
|
||||
support\bootstrap\Session::class,
|
||||
support\bootstrap\LaravelDb::class,
|
||||
];
|
15
config/container.php
Normal file
15
config/container.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return new Webman\Container;
|
38
config/database.php
Normal file
38
config/database.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [
|
||||
|
||||
'default' => 'mysql',
|
||||
// 各种数据库配置
|
||||
'connections' => [
|
||||
|
||||
'mysql' => [
|
||||
'driver' => 'mysql',
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 3306,
|
||||
'database' => 'LaysenseSSO',
|
||||
'username' => getenv('dbuser'),
|
||||
'password' => getenv('dbpass'),
|
||||
'unix_socket' => '',
|
||||
'charset' => 'utf8',
|
||||
'collation' => 'utf8_unicode_ci',
|
||||
'prefix' => '',
|
||||
'strict' => true,
|
||||
'engine' => null,
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
];
|
15
config/dependence.php
Normal file
15
config/dependence.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [];
|
5
config/event.php
Normal file
5
config/event.php
Normal file
@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
];
|
17
config/exception.php
Normal file
17
config/exception.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [
|
||||
'' => support\exception\Handler::class,
|
||||
];
|
32
config/log.php
Normal file
32
config/log.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [
|
||||
'default' => [
|
||||
'handlers' => [
|
||||
[
|
||||
'class' => Monolog\Handler\RotatingFileHandler::class,
|
||||
'constructor' => [
|
||||
runtime_path() . '/logs/webman.log',
|
||||
7, //$maxFiles
|
||||
Monolog\Logger::DEBUG,
|
||||
],
|
||||
'formatter' => [
|
||||
'class' => Monolog\Formatter\LineFormatter::class,
|
||||
'constructor' => [null, 'Y-m-d H:i:s', true],
|
||||
],
|
||||
]
|
||||
],
|
||||
],
|
||||
];
|
15
config/middleware.php
Normal file
15
config/middleware.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [];
|
4
config/plugin/webman/event/app.php
Normal file
4
config/plugin/webman/event/app.php
Normal file
@ -0,0 +1,4 @@
|
||||
<?php
|
||||
return [
|
||||
'enable' => true,
|
||||
];
|
17
config/plugin/webman/event/bootstrap.php
Normal file
17
config/plugin/webman/event/bootstrap.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [
|
||||
Webman\Event\BootStrap::class,
|
||||
];
|
7
config/plugin/webman/event/command.php
Normal file
7
config/plugin/webman/event/command.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Webman\Event\EventListCommand;
|
||||
|
||||
return [
|
||||
EventListCommand::class
|
||||
];
|
42
config/process.php
Normal file
42
config/process.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
global $argv;
|
||||
|
||||
return [
|
||||
// File update detection and automatic reload
|
||||
'monitor' => [
|
||||
'handler' => process\Monitor::class,
|
||||
'reloadable' => false,
|
||||
'constructor' => [
|
||||
// Monitor these directories
|
||||
'monitorDir' => array_merge([
|
||||
app_path(),
|
||||
config_path(),
|
||||
base_path() . '/process',
|
||||
base_path() . '/support',
|
||||
base_path() . '/resource',
|
||||
base_path() . '/.env',
|
||||
], glob(base_path() . '/plugin/*/app'), glob(base_path() . '/plugin/*/config'), glob(base_path() . '/plugin/*/api')),
|
||||
// Files with these suffixes will be monitored
|
||||
'monitorExtensions' => [
|
||||
'php', 'html', 'htm', 'env'
|
||||
],
|
||||
'options' => [
|
||||
'enable_file_monitor' => !in_array('-d', $argv) && DIRECTORY_SEPARATOR === '/',
|
||||
'enable_memory_monitor' => DIRECTORY_SEPARATOR === '/',
|
||||
]
|
||||
]
|
||||
]
|
||||
];
|
22
config/redis.php
Normal file
22
config/redis.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [
|
||||
'default' => [
|
||||
'host' => '127.0.0.1',
|
||||
'password' => null,
|
||||
'port' => 6379,
|
||||
'database' => 0,
|
||||
],
|
||||
];
|
23
config/route.php
Normal file
23
config/route.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use Webman\Route;
|
||||
|
||||
|
||||
#Laysense Simple Login
|
||||
Route::group('/auth/lay', function () {
|
||||
Route::any('/{appid}',[app\controller\LayAuth::class, 'index']);
|
||||
Route::any('/{appid}/{gateway}/callback',[app\controller\LayAuth::class, 'callback']);
|
||||
Route::any('/{appid}/check',[app\controller\LayAuth::class, 'check']);
|
||||
});
|
31
config/server.php
Normal file
31
config/server.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
return [
|
||||
'listen' => 'http://0.0.0.0:15566',
|
||||
'transport' => 'tcp',
|
||||
'context' => [],
|
||||
'name' => 'webman',
|
||||
'count' => cpu_count() * 1,
|
||||
'user' => '',
|
||||
'group' => '',
|
||||
'reusePort' => false,
|
||||
'event_loop' => '',
|
||||
'stop_timeout' => 2,
|
||||
'pid_file' => runtime_path() . '/webman.pid',
|
||||
'status_file' => runtime_path() . '/webman.status',
|
||||
'stdout_file' => runtime_path() . '/logs/stdout.log',
|
||||
'log_file' => runtime_path() . '/logs/workerman.log',
|
||||
'max_package_size' => 10 * 1024 * 1024
|
||||
];
|
65
config/session.php
Normal file
65
config/session.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use Webman\Session\FileSessionHandler;
|
||||
use Webman\Session\RedisSessionHandler;
|
||||
use Webman\Session\RedisClusterSessionHandler;
|
||||
|
||||
return [
|
||||
|
||||
'type' => 'file', // or redis or redis_cluster
|
||||
|
||||
'handler' => FileSessionHandler::class,
|
||||
|
||||
'config' => [
|
||||
'file' => [
|
||||
'save_path' => runtime_path() . '/sessions',
|
||||
],
|
||||
'redis' => [
|
||||
'host' => '127.0.0.1',
|
||||
'port' => 6379,
|
||||
'auth' => '',
|
||||
'timeout' => 2,
|
||||
'database' => '',
|
||||
'prefix' => 'redis_session_',
|
||||
],
|
||||
'redis_cluster' => [
|
||||
'host' => ['127.0.0.1:7000', '127.0.0.1:7001', '127.0.0.1:7001'],
|
||||
'timeout' => 2,
|
||||
'auth' => '',
|
||||
'prefix' => 'redis_session_',
|
||||
]
|
||||
],
|
||||
|
||||
'session_name' => 'PHPSID',
|
||||
|
||||
'auto_update_timestamp' => false,
|
||||
|
||||
'lifetime' => 7*24*60*60,
|
||||
|
||||
'cookie_lifetime' => 365*24*60*60,
|
||||
|
||||
'cookie_path' => '/',
|
||||
|
||||
'domain' => '',
|
||||
|
||||
'http_only' => true,
|
||||
|
||||
'secure' => false,
|
||||
|
||||
'same_site' => '',
|
||||
|
||||
'gc_probability' => [1, 1000],
|
||||
|
||||
];
|
23
config/static.php
Normal file
23
config/static.php
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
* Static file settings
|
||||
*/
|
||||
return [
|
||||
'enable' => true,
|
||||
'middleware' => [ // Static file Middleware
|
||||
//app\middleware\StaticFile::class,
|
||||
],
|
||||
];
|
25
config/translation.php
Normal file
25
config/translation.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
* Multilingual configuration
|
||||
*/
|
||||
return [
|
||||
// Default language
|
||||
'locale' => 'zh_CN',
|
||||
// Fallback language
|
||||
'fallback_locale' => ['zh_CN', 'en'],
|
||||
// Folder where language files are stored
|
||||
'path' => base_path() . '/resource/translations',
|
||||
];
|
22
config/view.php
Normal file
22
config/view.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use support\view\Raw;
|
||||
use support\view\Twig;
|
||||
use support\view\Blade;
|
||||
use support\view\ThinkPHP;
|
||||
|
||||
return [
|
||||
'handler' => Raw::class
|
||||
];
|
243
process/Monitor.php
Normal file
243
process/Monitor.php
Normal file
@ -0,0 +1,243 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace process;
|
||||
|
||||
use FilesystemIterator;
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
use SplFileInfo;
|
||||
use Workerman\Timer;
|
||||
use Workerman\Worker;
|
||||
|
||||
/**
|
||||
* Class FileMonitor
|
||||
* @package process
|
||||
*/
|
||||
class Monitor
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $paths = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $extensions = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public static $lockFile = __DIR__ . '/../runtime/monitor.lock';
|
||||
|
||||
/**
|
||||
* Pause monitor
|
||||
* @return void
|
||||
*/
|
||||
public static function pause()
|
||||
{
|
||||
file_put_contents(static::$lockFile, time());
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume monitor
|
||||
* @return void
|
||||
*/
|
||||
public static function resume(): void
|
||||
{
|
||||
clearstatcache();
|
||||
if (is_file(static::$lockFile)) {
|
||||
unlink(static::$lockFile);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether monitor is paused
|
||||
* @return bool
|
||||
*/
|
||||
public static function isPaused(): bool
|
||||
{
|
||||
clearstatcache();
|
||||
return file_exists(static::$lockFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* FileMonitor constructor.
|
||||
* @param $monitorDir
|
||||
* @param $monitorExtensions
|
||||
* @param array $options
|
||||
*/
|
||||
public function __construct($monitorDir, $monitorExtensions, array $options = [])
|
||||
{
|
||||
static::resume();
|
||||
$this->paths = (array)$monitorDir;
|
||||
$this->extensions = $monitorExtensions;
|
||||
if (!Worker::getAllWorkers()) {
|
||||
return;
|
||||
}
|
||||
$disableFunctions = explode(',', ini_get('disable_functions'));
|
||||
if (in_array('exec', $disableFunctions, true)) {
|
||||
echo "\nMonitor file change turned off because exec() has been disabled by disable_functions setting in " . PHP_CONFIG_FILE_PATH . "/php.ini\n";
|
||||
} else {
|
||||
if ($options['enable_file_monitor'] ?? true) {
|
||||
Timer::add(1, function () {
|
||||
$this->checkAllFilesChange();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$memoryLimit = $this->getMemoryLimit($options['memory_limit'] ?? null);
|
||||
if ($memoryLimit && ($options['enable_memory_monitor'] ?? true)) {
|
||||
Timer::add(60, [$this, 'checkMemory'], [$memoryLimit]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $monitorDir
|
||||
* @return bool
|
||||
*/
|
||||
public function checkFilesChange($monitorDir): bool
|
||||
{
|
||||
static $lastMtime, $tooManyFilesCheck;
|
||||
if (!$lastMtime) {
|
||||
$lastMtime = time();
|
||||
}
|
||||
clearstatcache();
|
||||
if (!is_dir($monitorDir)) {
|
||||
if (!is_file($monitorDir)) {
|
||||
return false;
|
||||
}
|
||||
$iterator = [new SplFileInfo($monitorDir)];
|
||||
} else {
|
||||
// recursive traversal directory
|
||||
$dirIterator = new RecursiveDirectoryIterator($monitorDir, FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS);
|
||||
$iterator = new RecursiveIteratorIterator($dirIterator);
|
||||
}
|
||||
$count = 0;
|
||||
foreach ($iterator as $file) {
|
||||
$count ++;
|
||||
/** var SplFileInfo $file */
|
||||
if (is_dir($file->getRealPath())) {
|
||||
continue;
|
||||
}
|
||||
// check mtime
|
||||
if (in_array($file->getExtension(), $this->extensions, true) && $lastMtime < $file->getMTime()) {
|
||||
$var = 0;
|
||||
exec('"'.PHP_BINARY . '" -l ' . $file, $out, $var);
|
||||
$lastMtime = $file->getMTime();
|
||||
if ($var) {
|
||||
continue;
|
||||
}
|
||||
echo $file . " update and reload\n";
|
||||
// send SIGUSR1 signal to master process for reload
|
||||
if (DIRECTORY_SEPARATOR === '/') {
|
||||
posix_kill(posix_getppid(), SIGUSR1);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$tooManyFilesCheck && $count > 1000) {
|
||||
echo "Monitor: There are too many files ($count files) in $monitorDir which makes file monitoring very slow\n";
|
||||
$tooManyFilesCheck = 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function checkAllFilesChange(): bool
|
||||
{
|
||||
if (static::isPaused()) {
|
||||
return false;
|
||||
}
|
||||
foreach ($this->paths as $path) {
|
||||
if ($this->checkFilesChange($path)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $memoryLimit
|
||||
* @return void
|
||||
*/
|
||||
public function checkMemory($memoryLimit)
|
||||
{
|
||||
if (static::isPaused() || $memoryLimit <= 0) {
|
||||
return;
|
||||
}
|
||||
$ppid = posix_getppid();
|
||||
$childrenFile = "/proc/$ppid/task/$ppid/children";
|
||||
if (!is_file($childrenFile) || !($children = file_get_contents($childrenFile))) {
|
||||
return;
|
||||
}
|
||||
foreach (explode(' ', $children) as $pid) {
|
||||
$pid = (int)$pid;
|
||||
$statusFile = "/proc/$pid/status";
|
||||
if (!is_file($statusFile) || !($status = file_get_contents($statusFile))) {
|
||||
continue;
|
||||
}
|
||||
$mem = 0;
|
||||
if (preg_match('/VmRSS\s*?:\s*?(\d+?)\s*?kB/', $status, $match)) {
|
||||
$mem = $match[1];
|
||||
}
|
||||
$mem = (int)($mem / 1024);
|
||||
if ($mem >= $memoryLimit) {
|
||||
posix_kill($pid, SIGINT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get memory limit
|
||||
* @return float
|
||||
*/
|
||||
protected function getMemoryLimit($memoryLimit)
|
||||
{
|
||||
if ($memoryLimit === 0) {
|
||||
return 0;
|
||||
}
|
||||
$usePhpIni = false;
|
||||
if (!$memoryLimit) {
|
||||
$memoryLimit = ini_get('memory_limit');
|
||||
$usePhpIni = true;
|
||||
}
|
||||
|
||||
if ($memoryLimit == -1) {
|
||||
return 0;
|
||||
}
|
||||
$unit = strtolower($memoryLimit[strlen($memoryLimit) - 1]);
|
||||
if ($unit === 'g') {
|
||||
$memoryLimit = 1024 * (int)$memoryLimit;
|
||||
} else if ($unit === 'm') {
|
||||
$memoryLimit = (int)$memoryLimit;
|
||||
} else if ($unit === 'k') {
|
||||
$memoryLimit = ((int)$memoryLimit / 1024);
|
||||
} else {
|
||||
$memoryLimit = ((int)$memoryLimit / (1024 * 1024));
|
||||
}
|
||||
if ($memoryLimit < 30) {
|
||||
$memoryLimit = 30;
|
||||
}
|
||||
if ($usePhpIni) {
|
||||
$memoryLimit = (int)(0.8 * $memoryLimit);
|
||||
}
|
||||
return $memoryLimit;
|
||||
}
|
||||
}
|
12
public/404.html
Normal file
12
public/404.html
Normal file
@ -0,0 +1,12 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>404 Not Found - webman</title>
|
||||
</head>
|
||||
<body>
|
||||
<center>
|
||||
<h1>404 Not Found</h1>
|
||||
</center>
|
||||
<hr>
|
||||
<center>webman</center>
|
||||
</body>
|
||||
</html>
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
4
start.php
Executable file
4
start.php
Executable file
@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
support\App::run();
|
24
support/Request.php
Normal file
24
support/Request.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace support;
|
||||
|
||||
/**
|
||||
* Class Request
|
||||
* @package support
|
||||
*/
|
||||
class Request extends \Webman\Http\Request
|
||||
{
|
||||
|
||||
}
|
24
support/Response.php
Normal file
24
support/Response.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
namespace support;
|
||||
|
||||
/**
|
||||
* Class Response
|
||||
* @package support
|
||||
*/
|
||||
class Response extends \Webman\Http\Response
|
||||
{
|
||||
|
||||
}
|
133
support/bootstrap.php
Normal file
133
support/bootstrap.php
Normal file
@ -0,0 +1,133 @@
|
||||
<?php
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use Dotenv\Dotenv;
|
||||
use support\Log;
|
||||
use Webman\Bootstrap;
|
||||
use Webman\Config;
|
||||
use Webman\Middleware;
|
||||
use Webman\Route;
|
||||
use Webman\Util;
|
||||
|
||||
$worker = $worker ?? null;
|
||||
|
||||
set_error_handler(function ($level, $message, $file = '', $line = 0) {
|
||||
if (error_reporting() & $level) {
|
||||
throw new ErrorException($message, 0, $level, $file, $line);
|
||||
}
|
||||
});
|
||||
|
||||
if ($worker) {
|
||||
register_shutdown_function(function ($startTime) {
|
||||
if (time() - $startTime <= 0.1) {
|
||||
sleep(1);
|
||||
}
|
||||
}, time());
|
||||
}
|
||||
|
||||
if (class_exists('Dotenv\Dotenv') && file_exists(base_path(false) . '/.env')) {
|
||||
if (method_exists('Dotenv\Dotenv', 'createUnsafeMutable')) {
|
||||
Dotenv::createUnsafeMutable(base_path(false))->load();
|
||||
} else {
|
||||
Dotenv::createMutable(base_path(false))->load();
|
||||
}
|
||||
}
|
||||
|
||||
Config::clear();
|
||||
support\App::loadAllConfig(['route']);
|
||||
if ($timezone = config('app.default_timezone')) {
|
||||
date_default_timezone_set($timezone);
|
||||
}
|
||||
|
||||
foreach (config('autoload.files', []) as $file) {
|
||||
include_once $file;
|
||||
}
|
||||
foreach (config('plugin', []) as $firm => $projects) {
|
||||
foreach ($projects as $name => $project) {
|
||||
if (!is_array($project)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($project['autoload']['files'] ?? [] as $file) {
|
||||
include_once $file;
|
||||
}
|
||||
}
|
||||
foreach ($projects['autoload']['files'] ?? [] as $file) {
|
||||
include_once $file;
|
||||
}
|
||||
}
|
||||
|
||||
Middleware::load(config('middleware', []));
|
||||
foreach (config('plugin', []) as $firm => $projects) {
|
||||
foreach ($projects as $name => $project) {
|
||||
if (!is_array($project) || $name === 'static') {
|
||||
continue;
|
||||
}
|
||||
Middleware::load($project['middleware'] ?? []);
|
||||
}
|
||||
Middleware::load($projects['middleware'] ?? [], $firm);
|
||||
if ($staticMiddlewares = config("plugin.$firm.static.middleware")) {
|
||||
Middleware::load(['__static__' => $staticMiddlewares], $firm);
|
||||
}
|
||||
}
|
||||
Middleware::load(['__static__' => config('static.middleware', [])]);
|
||||
|
||||
foreach (config('bootstrap', []) as $className) {
|
||||
if (!class_exists($className)) {
|
||||
$log = "Warning: Class $className setting in config/bootstrap.php not found\r\n";
|
||||
echo $log;
|
||||
Log::error($log);
|
||||
continue;
|
||||
}
|
||||
/** @var Bootstrap $className */
|
||||
$className::start($worker);
|
||||
}
|
||||
|
||||
foreach (config('plugin', []) as $firm => $projects) {
|
||||
foreach ($projects as $name => $project) {
|
||||
if (!is_array($project)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($project['bootstrap'] ?? [] as $className) {
|
||||
if (!class_exists($className)) {
|
||||
$log = "Warning: Class $className setting in config/plugin/$firm/$name/bootstrap.php not found\r\n";
|
||||
echo $log;
|
||||
Log::error($log);
|
||||
continue;
|
||||
}
|
||||
/** @var Bootstrap $className */
|
||||
$className::start($worker);
|
||||
}
|
||||
}
|
||||
foreach ($projects['bootstrap'] ?? [] as $className) {
|
||||
/** @var string $className */
|
||||
if (!class_exists($className)) {
|
||||
$log = "Warning: Class $className setting in plugin/$firm/config/bootstrap.php not found\r\n";
|
||||
echo $log;
|
||||
Log::error($log);
|
||||
continue;
|
||||
}
|
||||
/** @var Bootstrap $className */
|
||||
$className::start($worker);
|
||||
}
|
||||
}
|
||||
|
||||
$directory = base_path() . '/plugin';
|
||||
$paths = [config_path()];
|
||||
foreach (Util::scanDir($directory) as $path) {
|
||||
if (is_dir($path = "$path/config")) {
|
||||
$paths[] = $path;
|
||||
}
|
||||
}
|
||||
Route::load($paths);
|
||||
|
540
support/helpers.php
Normal file
540
support/helpers.php
Normal file
@ -0,0 +1,540 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of webman.
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the MIT-LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @author walkor<walkor@workerman.net>
|
||||
* @copyright walkor<walkor@workerman.net>
|
||||
* @link http://www.workerman.net/
|
||||
* @license http://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use support\Container;
|
||||
use support\Request;
|
||||
use support\Response;
|
||||
use support\Translation;
|
||||
use support\view\Blade;
|
||||
use support\view\Raw;
|
||||
use support\view\ThinkPHP;
|
||||
use support\view\Twig;
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Error\RuntimeError;
|
||||
use Twig\Error\SyntaxError;
|
||||
use Webman\App;
|
||||
use Webman\Config;
|
||||
use Webman\Route;
|
||||
use Workerman\Protocols\Http\Session;
|
||||
use Workerman\Worker;
|
||||
|
||||
// Project base path
|
||||
define('BASE_PATH', dirname(__DIR__));
|
||||
|
||||
/**
|
||||
* return the program execute directory
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function run_path(string $path = ''): string
|
||||
{
|
||||
static $runPath = '';
|
||||
if (!$runPath) {
|
||||
$runPath = is_phar() ? dirname(Phar::running(false)) : BASE_PATH;
|
||||
}
|
||||
return path_combine($runPath, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* if the param $path equal false,will return this program current execute directory
|
||||
* @param string|false $path
|
||||
* @return string
|
||||
*/
|
||||
function base_path($path = ''): string
|
||||
{
|
||||
if (false === $path) {
|
||||
return run_path();
|
||||
}
|
||||
return path_combine(BASE_PATH, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* App path
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function app_path(string $path = ''): string
|
||||
{
|
||||
return path_combine(BASE_PATH . DIRECTORY_SEPARATOR . 'app', $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public path
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function public_path(string $path = ''): string
|
||||
{
|
||||
static $publicPath = '';
|
||||
if (!$publicPath) {
|
||||
$publicPath = \config('app.public_path') ?: run_path('public');
|
||||
}
|
||||
return path_combine($publicPath, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Config path
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function config_path(string $path = ''): string
|
||||
{
|
||||
return path_combine(BASE_PATH . DIRECTORY_SEPARATOR . 'config', $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runtime path
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
function runtime_path(string $path = ''): string
|
||||
{
|
||||
static $runtimePath = '';
|
||||
if (!$runtimePath) {
|
||||
$runtimePath = \config('app.runtime_path') ?: run_path('runtime');
|
||||
}
|
||||
return path_combine($runtimePath, $path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate paths based on given information
|
||||
* @param string $front
|
||||
* @param string $back
|
||||
* @return string
|
||||
*/
|
||||
function path_combine(string $front, string $back): string
|
||||
{
|
||||
return $front . ($back ? (DIRECTORY_SEPARATOR . ltrim($back, DIRECTORY_SEPARATOR)) : $back);
|
||||
}
|
||||
|
||||
/**
|
||||
* Response
|
||||
* @param int $status
|
||||
* @param array $headers
|
||||
* @param string $body
|
||||
* @return Response
|
||||
*/
|
||||
function response(string $body = '', int $status = 200, array $headers = []): Response
|
||||
{
|
||||
return new Response($status, $headers, $body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Json response
|
||||
* @param $data
|
||||
* @param int $options
|
||||
* @return Response
|
||||
*/
|
||||
function json($data, int $options = JSON_UNESCAPED_UNICODE): Response
|
||||
{
|
||||
return new Response(200, ['Content-Type' => 'application/json'], json_encode($data, $options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Xml response
|
||||
* @param $xml
|
||||
* @return Response
|
||||
*/
|
||||
function xml($xml): Response
|
||||
{
|
||||
if ($xml instanceof SimpleXMLElement) {
|
||||
$xml = $xml->asXML();
|
||||
}
|
||||
return new Response(200, ['Content-Type' => 'text/xml'], $xml);
|
||||
}
|
||||
|
||||
/**
|
||||
* Jsonp response
|
||||
* @param $data
|
||||
* @param string $callbackName
|
||||
* @return Response
|
||||
*/
|
||||
function jsonp($data, string $callbackName = 'callback'): Response
|
||||
{
|
||||
if (!is_scalar($data) && null !== $data) {
|
||||
$data = json_encode($data);
|
||||
}
|
||||
return new Response(200, [], "$callbackName($data)");
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect response
|
||||
* @param string $location
|
||||
* @param int $status
|
||||
* @param array $headers
|
||||
* @return Response
|
||||
*/
|
||||
function redirect(string $location, int $status = 302, array $headers = []): Response
|
||||
{
|
||||
$response = new Response($status, ['Location' => $location]);
|
||||
if (!empty($headers)) {
|
||||
$response->withHeaders($headers);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* View response
|
||||
* @param string $template
|
||||
* @param array $vars
|
||||
* @param string|null $app
|
||||
* @param string|null $plugin
|
||||
* @return Response
|
||||
*/
|
||||
function view(string $template, array $vars = [], string $app = null, string $plugin = null): Response
|
||||
{
|
||||
$request = \request();
|
||||
$plugin = $plugin === null ? ($request->plugin ?? '') : $plugin;
|
||||
$handler = \config($plugin ? "plugin.$plugin.view.handler" : 'view.handler');
|
||||
return new Response(200, [], $handler::render($template, $vars, $app, $plugin));
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw view response
|
||||
* @param string $template
|
||||
* @param array $vars
|
||||
* @param string|null $app
|
||||
* @return Response
|
||||
* @throws Throwable
|
||||
*/
|
||||
function raw_view(string $template, array $vars = [], string $app = null): Response
|
||||
{
|
||||
return new Response(200, [], Raw::render($template, $vars, $app));
|
||||
}
|
||||
|
||||
/**
|
||||
* Blade view response
|
||||
* @param string $template
|
||||
* @param array $vars
|
||||
* @param string|null $app
|
||||
* @return Response
|
||||
*/
|
||||
function blade_view(string $template, array $vars = [], string $app = null): Response
|
||||
{
|
||||
return new Response(200, [], Blade::render($template, $vars, $app));
|
||||
}
|
||||
|
||||
/**
|
||||
* Think view response
|
||||
* @param string $template
|
||||
* @param array $vars
|
||||
* @param string|null $app
|
||||
* @return Response
|
||||
*/
|
||||
function think_view(string $template, array $vars = [], string $app = null): Response
|
||||
{
|
||||
return new Response(200, [], ThinkPHP::render($template, $vars, $app));
|
||||
}
|
||||
|
||||
/**
|
||||
* Twig view response
|
||||
* @param string $template
|
||||
* @param array $vars
|
||||
* @param string|null $app
|
||||
* @return Response
|
||||
* @throws LoaderError
|
||||
* @throws RuntimeError
|
||||
* @throws SyntaxError
|
||||
*/
|
||||
function twig_view(string $template, array $vars = [], string $app = null): Response
|
||||
{
|
||||
return new Response(200, [], Twig::render($template, $vars, $app));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get request
|
||||
* @return \Webman\Http\Request|Request|null
|
||||
*/
|
||||
function request()
|
||||
{
|
||||
return App::request();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get config
|
||||
* @param string|null $key
|
||||
* @param $default
|
||||
* @return array|mixed|null
|
||||
*/
|
||||
function config(string $key = null, $default = null)
|
||||
{
|
||||
return Config::get($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create url
|
||||
* @param string $name
|
||||
* @param ...$parameters
|
||||
* @return string
|
||||
*/
|
||||
function route(string $name, ...$parameters): string
|
||||
{
|
||||
$route = Route::getByName($name);
|
||||
if (!$route) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (!$parameters) {
|
||||
return $route->url();
|
||||
}
|
||||
|
||||
if (is_array(current($parameters))) {
|
||||
$parameters = current($parameters);
|
||||
}
|
||||
|
||||
return $route->url($parameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Session
|
||||
* @param mixed $key
|
||||
* @param mixed $default
|
||||
* @return mixed|bool|Session
|
||||
*/
|
||||
function session($key = null, $default = null)
|
||||
{
|
||||
$session = \request()->session();
|
||||
if (null === $key) {
|
||||
return $session;
|
||||
}
|
||||
if (is_array($key)) {
|
||||
$session->put($key);
|
||||
return null;
|
||||
}
|
||||
if (strpos($key, '.')) {
|
||||
$keyArray = explode('.', $key);
|
||||
$value = $session->all();
|
||||
foreach ($keyArray as $index) {
|
||||
if (!isset($value[$index])) {
|
||||
return $default;
|
||||
}
|
||||
$value = $value[$index];
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
return $session->get($key, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translation
|
||||
* @param string $id
|
||||
* @param array $parameters
|
||||
* @param string|null $domain
|
||||
* @param string|null $locale
|
||||
* @return string
|
||||
*/
|
||||
function trans(string $id, array $parameters = [], string $domain = null, string $locale = null): string
|
||||
{
|
||||
$res = Translation::trans($id, $parameters, $domain, $locale);
|
||||
return $res === '' ? $id : $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locale
|
||||
* @param string|null $locale
|
||||
* @return string
|
||||
*/
|
||||
function locale(string $locale = null): string
|
||||
{
|
||||
if (!$locale) {
|
||||
return Translation::getLocale();
|
||||
}
|
||||
Translation::setLocale($locale);
|
||||
return $locale;
|
||||
}
|
||||
|
||||
/**
|
||||
* 404 not found
|
||||
* @return Response
|
||||
*/
|
||||
function not_found(): Response
|
||||
{
|
||||
return new Response(404, [], file_get_contents(public_path() . '/404.html'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy dir
|
||||
* @param string $source
|
||||
* @param string $dest
|
||||
* @param bool $overwrite
|
||||
* @return void
|
||||
*/
|
||||
function copy_dir(string $source, string $dest, bool $overwrite = false)
|
||||
{
|
||||
if (is_dir($source)) {
|
||||
if (!is_dir($dest)) {
|
||||
mkdir($dest);
|
||||
}
|
||||
$files = scandir($source);
|
||||
foreach ($files as $file) {
|
||||
if ($file !== "." && $file !== "..") {
|
||||
copy_dir("$source/$file", "$dest/$file", $overwrite);
|
||||
}
|
||||
}
|
||||
} else if (file_exists($source) && ($overwrite || !file_exists($dest))) {
|
||||
copy($source, $dest);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove dir
|
||||
* @param string $dir
|
||||
* @return bool
|
||||
*/
|
||||
function remove_dir(string $dir): bool
|
||||
{
|
||||
if (is_link($dir) || is_file($dir)) {
|
||||
return unlink($dir);
|
||||
}
|
||||
$files = array_diff(scandir($dir), array('.', '..'));
|
||||
foreach ($files as $file) {
|
||||
(is_dir("$dir/$file") && !is_link($dir)) ? remove_dir("$dir/$file") : unlink("$dir/$file");
|
||||
}
|
||||
return rmdir($dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind worker
|
||||
* @param $worker
|
||||
* @param $class
|
||||
*/
|
||||
function worker_bind($worker, $class)
|
||||
{
|
||||
$callbackMap = [
|
||||
'onConnect',
|
||||
'onMessage',
|
||||
'onClose',
|
||||
'onError',
|
||||
'onBufferFull',
|
||||
'onBufferDrain',
|
||||
'onWorkerStop',
|
||||
'onWebSocketConnect',
|
||||
'onWorkerReload'
|
||||
];
|
||||
foreach ($callbackMap as $name) {
|
||||
if (method_exists($class, $name)) {
|
||||
$worker->$name = [$class, $name];
|
||||
}
|
||||
}
|
||||
if (method_exists($class, 'onWorkerStart')) {
|
||||
call_user_func([$class, 'onWorkerStart'], $worker);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start worker
|
||||
* @param $processName
|
||||
* @param $config
|
||||
* @return void
|
||||
*/
|
||||
function worker_start($processName, $config)
|
||||
{
|
||||
$worker = new Worker($config['listen'] ?? null, $config['context'] ?? []);
|
||||
$propertyMap = [
|
||||
'count',
|
||||
'user',
|
||||
'group',
|
||||
'reloadable',
|
||||
'reusePort',
|
||||
'transport',
|
||||
'protocol',
|
||||
];
|
||||
$worker->name = $processName;
|
||||
foreach ($propertyMap as $property) {
|
||||
if (isset($config[$property])) {
|
||||
$worker->$property = $config[$property];
|
||||
}
|
||||
}
|
||||
|
||||
$worker->onWorkerStart = function ($worker) use ($config) {
|
||||
require_once base_path('/support/bootstrap.php');
|
||||
if (isset($config['handler'])) {
|
||||
if (!class_exists($config['handler'])) {
|
||||
echo "process error: class {$config['handler']} not exists\r\n";
|
||||
return;
|
||||
}
|
||||
|
||||
$instance = Container::make($config['handler'], $config['constructor'] ?? []);
|
||||
worker_bind($worker, $instance);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get realpath
|
||||
* @param string $filePath
|
||||
* @return string
|
||||
*/
|
||||
function get_realpath(string $filePath): string
|
||||
{
|
||||
if (strpos($filePath, 'phar://') === 0) {
|
||||
return $filePath;
|
||||
} else {
|
||||
return realpath($filePath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is phar
|
||||
* @return bool
|
||||
*/
|
||||
function is_phar(): bool
|
||||
{
|
||||
return class_exists(Phar::class, false) && Phar::running();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cpu count
|
||||
* @return int
|
||||
*/
|
||||
function cpu_count(): int
|
||||
{
|
||||
// Windows does not support the number of processes setting.
|
||||
if (DIRECTORY_SEPARATOR === '\\') {
|
||||
return 1;
|
||||
}
|
||||
$count = 4;
|
||||
if (is_callable('shell_exec')) {
|
||||
if (strtolower(PHP_OS) === 'darwin') {
|
||||
$count = (int)shell_exec('sysctl -n machdep.cpu.core_count');
|
||||
} else {
|
||||
$count = (int)shell_exec('nproc');
|
||||
}
|
||||
}
|
||||
return $count > 0 ? $count : 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get request parameters, if no parameter name is passed, an array of all values is returned, default values is supported
|
||||
* @param string|null $param param's name
|
||||
* @param mixed|null $default default value
|
||||
* @return mixed|null
|
||||
*/
|
||||
function input(string $param = null, $default = null)
|
||||
{
|
||||
return is_null($param) ? request()->all() : request()->input($param, $default);
|
||||
}
|
||||
|
||||
function encryptAES($data, $key, $iv) {
|
||||
$encrypted = openssl_encrypt($data, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
|
||||
$encrypted = base64_encode($encrypted);
|
||||
return $encrypted;
|
||||
}
|
||||
|
||||
function decryptAES($encryptedData, $key, $iv) {
|
||||
$encryptedData = base64_decode($encryptedData);
|
||||
$decrypted = openssl_decrypt($encryptedData, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
|
||||
return $decrypted;
|
||||
}
|
25
vendor/autoload.php
vendored
Normal file
25
vendor/autoload.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
if (PHP_VERSION_ID < 50600) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, $err);
|
||||
} elseif (!headers_sent()) {
|
||||
echo $err;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
$err,
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInitd3b2156d13053c4f5c49b090883e3785::getLoader();
|
119
vendor/bin/carbon
vendored
Executable file
119
vendor/bin/carbon
vendored
Executable file
@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Proxy PHP file generated by Composer
|
||||
*
|
||||
* This file includes the referenced bin path (../nesbot/carbon/bin/carbon)
|
||||
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||
*
|
||||
* @generated
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
|
||||
|
||||
if (PHP_VERSION_ID < 80000) {
|
||||
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BinProxyWrapper
|
||||
{
|
||||
private $handle;
|
||||
private $position;
|
||||
private $realpath;
|
||||
|
||||
public function stream_open($path, $mode, $options, &$opened_path)
|
||||
{
|
||||
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||
$opened_path = substr($path, 17);
|
||||
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||
$opened_path = $this->realpath;
|
||||
$this->handle = fopen($this->realpath, $mode);
|
||||
$this->position = 0;
|
||||
|
||||
return (bool) $this->handle;
|
||||
}
|
||||
|
||||
public function stream_read($count)
|
||||
{
|
||||
$data = fread($this->handle, $count);
|
||||
|
||||
if ($this->position === 0) {
|
||||
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||
}
|
||||
|
||||
$this->position += strlen($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function stream_cast($castAs)
|
||||
{
|
||||
return $this->handle;
|
||||
}
|
||||
|
||||
public function stream_close()
|
||||
{
|
||||
fclose($this->handle);
|
||||
}
|
||||
|
||||
public function stream_lock($operation)
|
||||
{
|
||||
return $operation ? flock($this->handle, $operation) : true;
|
||||
}
|
||||
|
||||
public function stream_seek($offset, $whence)
|
||||
{
|
||||
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||
$this->position = ftell($this->handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function stream_tell()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function stream_eof()
|
||||
{
|
||||
return feof($this->handle);
|
||||
}
|
||||
|
||||
public function stream_stat()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function stream_set_option($option, $arg1, $arg2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function url_stat($path, $flags)
|
||||
{
|
||||
$path = substr($path, 17);
|
||||
if (file_exists($path)) {
|
||||
return stat($path);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||
) {
|
||||
return include("phpvfscomposer://" . __DIR__ . '/..'.'/nesbot/carbon/bin/carbon');
|
||||
}
|
||||
}
|
||||
|
||||
return include __DIR__ . '/..'.'/nesbot/carbon/bin/carbon';
|
119
vendor/bin/phinx
vendored
Executable file
119
vendor/bin/phinx
vendored
Executable file
@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Proxy PHP file generated by Composer
|
||||
*
|
||||
* This file includes the referenced bin path (../robmorgan/phinx/bin/phinx)
|
||||
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||
*
|
||||
* @generated
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
|
||||
|
||||
if (PHP_VERSION_ID < 80000) {
|
||||
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BinProxyWrapper
|
||||
{
|
||||
private $handle;
|
||||
private $position;
|
||||
private $realpath;
|
||||
|
||||
public function stream_open($path, $mode, $options, &$opened_path)
|
||||
{
|
||||
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||
$opened_path = substr($path, 17);
|
||||
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||
$opened_path = $this->realpath;
|
||||
$this->handle = fopen($this->realpath, $mode);
|
||||
$this->position = 0;
|
||||
|
||||
return (bool) $this->handle;
|
||||
}
|
||||
|
||||
public function stream_read($count)
|
||||
{
|
||||
$data = fread($this->handle, $count);
|
||||
|
||||
if ($this->position === 0) {
|
||||
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||
}
|
||||
|
||||
$this->position += strlen($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function stream_cast($castAs)
|
||||
{
|
||||
return $this->handle;
|
||||
}
|
||||
|
||||
public function stream_close()
|
||||
{
|
||||
fclose($this->handle);
|
||||
}
|
||||
|
||||
public function stream_lock($operation)
|
||||
{
|
||||
return $operation ? flock($this->handle, $operation) : true;
|
||||
}
|
||||
|
||||
public function stream_seek($offset, $whence)
|
||||
{
|
||||
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||
$this->position = ftell($this->handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function stream_tell()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function stream_eof()
|
||||
{
|
||||
return feof($this->handle);
|
||||
}
|
||||
|
||||
public function stream_stat()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function stream_set_option($option, $arg1, $arg2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function url_stat($path, $flags)
|
||||
{
|
||||
$path = substr($path, 17);
|
||||
if (file_exists($path)) {
|
||||
return stat($path);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||
) {
|
||||
return include("phpvfscomposer://" . __DIR__ . '/..'.'/robmorgan/phinx/bin/phinx');
|
||||
}
|
||||
}
|
||||
|
||||
return include __DIR__ . '/..'.'/robmorgan/phinx/bin/phinx';
|
119
vendor/bin/var-dump-server
vendored
Executable file
119
vendor/bin/var-dump-server
vendored
Executable file
@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Proxy PHP file generated by Composer
|
||||
*
|
||||
* This file includes the referenced bin path (../symfony/var-dumper/Resources/bin/var-dump-server)
|
||||
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||
*
|
||||
* @generated
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
|
||||
|
||||
if (PHP_VERSION_ID < 80000) {
|
||||
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BinProxyWrapper
|
||||
{
|
||||
private $handle;
|
||||
private $position;
|
||||
private $realpath;
|
||||
|
||||
public function stream_open($path, $mode, $options, &$opened_path)
|
||||
{
|
||||
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||
$opened_path = substr($path, 17);
|
||||
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||
$opened_path = $this->realpath;
|
||||
$this->handle = fopen($this->realpath, $mode);
|
||||
$this->position = 0;
|
||||
|
||||
return (bool) $this->handle;
|
||||
}
|
||||
|
||||
public function stream_read($count)
|
||||
{
|
||||
$data = fread($this->handle, $count);
|
||||
|
||||
if ($this->position === 0) {
|
||||
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||
}
|
||||
|
||||
$this->position += strlen($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function stream_cast($castAs)
|
||||
{
|
||||
return $this->handle;
|
||||
}
|
||||
|
||||
public function stream_close()
|
||||
{
|
||||
fclose($this->handle);
|
||||
}
|
||||
|
||||
public function stream_lock($operation)
|
||||
{
|
||||
return $operation ? flock($this->handle, $operation) : true;
|
||||
}
|
||||
|
||||
public function stream_seek($offset, $whence)
|
||||
{
|
||||
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||
$this->position = ftell($this->handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function stream_tell()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function stream_eof()
|
||||
{
|
||||
return feof($this->handle);
|
||||
}
|
||||
|
||||
public function stream_stat()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function stream_set_option($option, $arg1, $arg2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function url_stat($path, $flags)
|
||||
{
|
||||
$path = substr($path, 17);
|
||||
if (file_exists($path)) {
|
||||
return stat($path);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||
) {
|
||||
return include("phpvfscomposer://" . __DIR__ . '/..'.'/symfony/var-dumper/Resources/bin/var-dump-server');
|
||||
}
|
||||
}
|
||||
|
||||
return include __DIR__ . '/..'.'/symfony/var-dumper/Resources/bin/var-dump-server';
|
445
vendor/brick/math/CHANGELOG.md
vendored
Normal file
445
vendor/brick/math/CHANGELOG.md
vendored
Normal file
@ -0,0 +1,445 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [0.11.0](https://github.com/brick/math/releases/tag/0.11.0) - 2023-01-16
|
||||
|
||||
💥 **Breaking changes**
|
||||
|
||||
- Minimum PHP version is now 8.0
|
||||
- Methods accepting a union of types are now strongly typed<sup>*</sup>
|
||||
- `MathException` now extends `Exception` instead of `RuntimeException`
|
||||
|
||||
<sup>* You may now run into type errors if you were passing `Stringable` objects to `of()` or any of the methods
|
||||
internally calling `of()`, with `strict_types` enabled. You can fix this by casting `Stringable` objects to `string`
|
||||
first.</sup>
|
||||
|
||||
## [0.10.2](https://github.com/brick/math/releases/tag/0.10.2) - 2022-08-11
|
||||
|
||||
👌 **Improvements**
|
||||
|
||||
- `BigRational::toFloat()` now simplifies the fraction before performing division (#73) thanks to @olsavmic
|
||||
|
||||
## [0.10.1](https://github.com/brick/math/releases/tag/0.10.1) - 2022-08-02
|
||||
|
||||
✨ **New features**
|
||||
|
||||
- `BigInteger::gcdMultiple()` returns the GCD of multiple `BigInteger` numbers
|
||||
|
||||
## [0.10.0](https://github.com/brick/math/releases/tag/0.10.0) - 2022-06-18
|
||||
|
||||
💥 **Breaking changes**
|
||||
|
||||
- Minimum PHP version is now 7.4
|
||||
|
||||
## [0.9.3](https://github.com/brick/math/releases/tag/0.9.3) - 2021-08-15
|
||||
|
||||
🚀 **Compatibility with PHP 8.1**
|
||||
|
||||
- Support for custom object serialization; this removes a warning on PHP 8.1 due to the `Serializable` interface being deprecated (#60) thanks @TRowbotham
|
||||
|
||||
## [0.9.2](https://github.com/brick/math/releases/tag/0.9.2) - 2021-01-20
|
||||
|
||||
🐛 **Bug fix**
|
||||
|
||||
- Incorrect results could be returned when using the BCMath calculator, with a default scale set with `bcscale()`, on PHP >= 7.2 (#55).
|
||||
|
||||
## [0.9.1](https://github.com/brick/math/releases/tag/0.9.1) - 2020-08-19
|
||||
|
||||
✨ **New features**
|
||||
|
||||
- `BigInteger::not()` returns the bitwise `NOT` value
|
||||
|
||||
🐛 **Bug fixes**
|
||||
|
||||
- `BigInteger::toBytes()` could return an incorrect binary representation for some numbers
|
||||
- The bitwise operations `and()`, `or()`, `xor()` on `BigInteger` could return an incorrect result when the GMP extension is not available
|
||||
|
||||
## [0.9.0](https://github.com/brick/math/releases/tag/0.9.0) - 2020-08-18
|
||||
|
||||
👌 **Improvements**
|
||||
|
||||
- `BigNumber::of()` now accepts `.123` and `123.` formats, both of which return a `BigDecimal`
|
||||
|
||||
💥 **Breaking changes**
|
||||
|
||||
- Deprecated method `BigInteger::powerMod()` has been removed - use `modPow()` instead
|
||||
- Deprecated method `BigInteger::parse()` has been removed - use `fromBase()` instead
|
||||
|
||||
## [0.8.17](https://github.com/brick/math/releases/tag/0.8.17) - 2020-08-19
|
||||
|
||||
🐛 **Bug fix**
|
||||
|
||||
- `BigInteger::toBytes()` could return an incorrect binary representation for some numbers
|
||||
- The bitwise operations `and()`, `or()`, `xor()` on `BigInteger` could return an incorrect result when the GMP extension is not available
|
||||
|
||||
## [0.8.16](https://github.com/brick/math/releases/tag/0.8.16) - 2020-08-18
|
||||
|
||||
🚑 **Critical fix**
|
||||
|
||||
- This version reintroduces the deprecated `BigInteger::parse()` method, that has been removed by mistake in version `0.8.9` and should have lasted for the whole `0.8` release cycle.
|
||||
|
||||
✨ **New features**
|
||||
|
||||
- `BigInteger::modInverse()` calculates a modular multiplicative inverse
|
||||
- `BigInteger::fromBytes()` creates a `BigInteger` from a byte string
|
||||
- `BigInteger::toBytes()` converts a `BigInteger` to a byte string
|
||||
- `BigInteger::randomBits()` creates a pseudo-random `BigInteger` of a given bit length
|
||||
- `BigInteger::randomRange()` creates a pseudo-random `BigInteger` between two bounds
|
||||
|
||||
💩 **Deprecations**
|
||||
|
||||
- `BigInteger::powerMod()` is now deprecated in favour of `modPow()`
|
||||
|
||||
## [0.8.15](https://github.com/brick/math/releases/tag/0.8.15) - 2020-04-15
|
||||
|
||||
🐛 **Fixes**
|
||||
|
||||
- added missing `ext-json` requirement, due to `BigNumber` implementing `JsonSerializable`
|
||||
|
||||
⚡️ **Optimizations**
|
||||
|
||||
- additional optimization in `BigInteger::remainder()`
|
||||
|
||||
## [0.8.14](https://github.com/brick/math/releases/tag/0.8.14) - 2020-02-18
|
||||
|
||||
✨ **New features**
|
||||
|
||||
- `BigInteger::getLowestSetBit()` returns the index of the rightmost one bit
|
||||
|
||||
## [0.8.13](https://github.com/brick/math/releases/tag/0.8.13) - 2020-02-16
|
||||
|
||||
✨ **New features**
|
||||
|
||||
- `BigInteger::isEven()` tests whether the number is even
|
||||
- `BigInteger::isOdd()` tests whether the number is odd
|
||||
- `BigInteger::testBit()` tests if a bit is set
|
||||
- `BigInteger::getBitLength()` returns the number of bits in the minimal representation of the number
|
||||
|
||||
## [0.8.12](https://github.com/brick/math/releases/tag/0.8.12) - 2020-02-03
|
||||
|
||||
🛠️ **Maintenance release**
|
||||
|
||||
Classes are now annotated for better static analysis with [psalm](https://psalm.dev/).
|
||||
|
||||
This is a maintenance release: no bug fixes, no new features, no breaking changes.
|
||||
|
||||
## [0.8.11](https://github.com/brick/math/releases/tag/0.8.11) - 2020-01-23
|
||||
|
||||
✨ **New feature**
|
||||
|
||||
`BigInteger::powerMod()` performs a power-with-modulo operation. Useful for crypto.
|
||||
|
||||
## [0.8.10](https://github.com/brick/math/releases/tag/0.8.10) - 2020-01-21
|
||||
|
||||
✨ **New feature**
|
||||
|
||||
`BigInteger::mod()` returns the **modulo** of two numbers. The *modulo* differs from the *remainder* when the signs of the operands are different.
|
||||
|
||||
## [0.8.9](https://github.com/brick/math/releases/tag/0.8.9) - 2020-01-08
|
||||
|
||||
⚡️ **Performance improvements**
|
||||
|
||||
A few additional optimizations in `BigInteger` and `BigDecimal` when one of the operands can be returned as is. Thanks to @tomtomsen in #24.
|
||||
|
||||
## [0.8.8](https://github.com/brick/math/releases/tag/0.8.8) - 2019-04-25
|
||||
|
||||
🐛 **Bug fixes**
|
||||
|
||||
- `BigInteger::toBase()` could return an empty string for zero values (BCMath & Native calculators only, GMP calculator unaffected)
|
||||
|
||||
✨ **New features**
|
||||
|
||||
- `BigInteger::toArbitraryBase()` converts a number to an arbitrary base, using a custom alphabet
|
||||
- `BigInteger::fromArbitraryBase()` converts a string in an arbitrary base, using a custom alphabet, back to a number
|
||||
|
||||
These methods can be used as the foundation to convert strings between different bases/alphabets, using BigInteger as an intermediate representation.
|
||||
|
||||
💩 **Deprecations**
|
||||
|
||||
- `BigInteger::parse()` is now deprecated in favour of `fromBase()`
|
||||
|
||||
`BigInteger::fromBase()` works the same way as `parse()`, with 2 minor differences:
|
||||
|
||||
- the `$base` parameter is required, it does not default to `10`
|
||||
- it throws a `NumberFormatException` instead of an `InvalidArgumentException` when the number is malformed
|
||||
|
||||
## [0.8.7](https://github.com/brick/math/releases/tag/0.8.7) - 2019-04-20
|
||||
|
||||
**Improvements**
|
||||
|
||||
- Safer conversion from `float` when using custom locales
|
||||
- **Much faster** `NativeCalculator` implementation 🚀
|
||||
|
||||
You can expect **at least a 3x performance improvement** for common arithmetic operations when using the library on systems without GMP or BCMath; it gets exponentially faster on multiplications with a high number of digits. This is due to calculations now being performed on whole blocks of digits (the block size depending on the platform, 32-bit or 64-bit) instead of digit-by-digit as before.
|
||||
|
||||
## [0.8.6](https://github.com/brick/math/releases/tag/0.8.6) - 2019-04-11
|
||||
|
||||
**New method**
|
||||
|
||||
`BigNumber::sum()` returns the sum of one or more numbers.
|
||||
|
||||
## [0.8.5](https://github.com/brick/math/releases/tag/0.8.5) - 2019-02-12
|
||||
|
||||
**Bug fix**: `of()` factory methods could fail when passing a `float` in environments using a `LC_NUMERIC` locale with a decimal separator other than `'.'` (#20).
|
||||
|
||||
Thanks @manowark 👍
|
||||
|
||||
## [0.8.4](https://github.com/brick/math/releases/tag/0.8.4) - 2018-12-07
|
||||
|
||||
**New method**
|
||||
|
||||
`BigDecimal::sqrt()` calculates the square root of a decimal number, to a given scale.
|
||||
|
||||
## [0.8.3](https://github.com/brick/math/releases/tag/0.8.3) - 2018-12-06
|
||||
|
||||
**New method**
|
||||
|
||||
`BigInteger::sqrt()` calculates the square root of a number (thanks @peter279k).
|
||||
|
||||
**New exception**
|
||||
|
||||
`NegativeNumberException` is thrown when calling `sqrt()` on a negative number.
|
||||
|
||||
## [0.8.2](https://github.com/brick/math/releases/tag/0.8.2) - 2018-11-08
|
||||
|
||||
**Performance update**
|
||||
|
||||
- Further improvement of `toInt()` performance
|
||||
- `NativeCalculator` can now perform some multiplications more efficiently
|
||||
|
||||
## [0.8.1](https://github.com/brick/math/releases/tag/0.8.1) - 2018-11-07
|
||||
|
||||
Performance optimization of `toInt()` methods.
|
||||
|
||||
## [0.8.0](https://github.com/brick/math/releases/tag/0.8.0) - 2018-10-13
|
||||
|
||||
**Breaking changes**
|
||||
|
||||
The following deprecated methods have been removed. Use the new method name instead:
|
||||
|
||||
| Method removed | Replacement method |
|
||||
| --- | --- |
|
||||
| `BigDecimal::getIntegral()` | `BigDecimal::getIntegralPart()` |
|
||||
| `BigDecimal::getFraction()` | `BigDecimal::getFractionalPart()` |
|
||||
|
||||
---
|
||||
|
||||
**New features**
|
||||
|
||||
`BigInteger` has been augmented with 5 new methods for bitwise operations:
|
||||
|
||||
| New method | Description |
|
||||
| --- | --- |
|
||||
| `and()` | performs a bitwise `AND` operation on two numbers |
|
||||
| `or()` | performs a bitwise `OR` operation on two numbers |
|
||||
| `xor()` | performs a bitwise `XOR` operation on two numbers |
|
||||
| `shiftedLeft()` | returns the number shifted left by a number of bits |
|
||||
| `shiftedRight()` | returns the number shifted right by a number of bits |
|
||||
|
||||
Thanks to @DASPRiD 👍
|
||||
|
||||
## [0.7.3](https://github.com/brick/math/releases/tag/0.7.3) - 2018-08-20
|
||||
|
||||
**New method:** `BigDecimal::hasNonZeroFractionalPart()`
|
||||
|
||||
**Renamed/deprecated methods:**
|
||||
|
||||
- `BigDecimal::getIntegral()` has been renamed to `getIntegralPart()` and is now deprecated
|
||||
- `BigDecimal::getFraction()` has been renamed to `getFractionalPart()` and is now deprecated
|
||||
|
||||
## [0.7.2](https://github.com/brick/math/releases/tag/0.7.2) - 2018-07-21
|
||||
|
||||
**Performance update**
|
||||
|
||||
`BigInteger::parse()` and `toBase()` now use GMP's built-in base conversion features when available.
|
||||
|
||||
## [0.7.1](https://github.com/brick/math/releases/tag/0.7.1) - 2018-03-01
|
||||
|
||||
This is a maintenance release, no code has been changed.
|
||||
|
||||
- When installed with `--no-dev`, the autoloader does not autoload tests anymore
|
||||
- Tests and other files unnecessary for production are excluded from the dist package
|
||||
|
||||
This will help make installations more compact.
|
||||
|
||||
## [0.7.0](https://github.com/brick/math/releases/tag/0.7.0) - 2017-10-02
|
||||
|
||||
Methods renamed:
|
||||
|
||||
- `BigNumber:sign()` has been renamed to `getSign()`
|
||||
- `BigDecimal::unscaledValue()` has been renamed to `getUnscaledValue()`
|
||||
- `BigDecimal::scale()` has been renamed to `getScale()`
|
||||
- `BigDecimal::integral()` has been renamed to `getIntegral()`
|
||||
- `BigDecimal::fraction()` has been renamed to `getFraction()`
|
||||
- `BigRational::numerator()` has been renamed to `getNumerator()`
|
||||
- `BigRational::denominator()` has been renamed to `getDenominator()`
|
||||
|
||||
Classes renamed:
|
||||
|
||||
- `ArithmeticException` has been renamed to `MathException`
|
||||
|
||||
## [0.6.2](https://github.com/brick/math/releases/tag/0.6.2) - 2017-10-02
|
||||
|
||||
The base class for all exceptions is now `MathException`.
|
||||
`ArithmeticException` has been deprecated, and will be removed in 0.7.0.
|
||||
|
||||
## [0.6.1](https://github.com/brick/math/releases/tag/0.6.1) - 2017-10-02
|
||||
|
||||
A number of methods have been renamed:
|
||||
|
||||
- `BigNumber:sign()` is deprecated; use `getSign()` instead
|
||||
- `BigDecimal::unscaledValue()` is deprecated; use `getUnscaledValue()` instead
|
||||
- `BigDecimal::scale()` is deprecated; use `getScale()` instead
|
||||
- `BigDecimal::integral()` is deprecated; use `getIntegral()` instead
|
||||
- `BigDecimal::fraction()` is deprecated; use `getFraction()` instead
|
||||
- `BigRational::numerator()` is deprecated; use `getNumerator()` instead
|
||||
- `BigRational::denominator()` is deprecated; use `getDenominator()` instead
|
||||
|
||||
The old methods will be removed in version 0.7.0.
|
||||
|
||||
## [0.6.0](https://github.com/brick/math/releases/tag/0.6.0) - 2017-08-25
|
||||
|
||||
- Minimum PHP version is now [7.1](https://gophp71.org/); for PHP 5.6 and PHP 7.0 support, use version `0.5`
|
||||
- Deprecated method `BigDecimal::withScale()` has been removed; use `toScale()` instead
|
||||
- Method `BigNumber::toInteger()` has been renamed to `toInt()`
|
||||
|
||||
## [0.5.4](https://github.com/brick/math/releases/tag/0.5.4) - 2016-10-17
|
||||
|
||||
`BigNumber` classes now implement [JsonSerializable](http://php.net/manual/en/class.jsonserializable.php).
|
||||
The JSON output is always a string.
|
||||
|
||||
## [0.5.3](https://github.com/brick/math/releases/tag/0.5.3) - 2016-03-31
|
||||
|
||||
This is a bugfix release. Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6.
|
||||
|
||||
## [0.5.2](https://github.com/brick/math/releases/tag/0.5.2) - 2015-08-06
|
||||
|
||||
The `$scale` parameter of `BigDecimal::dividedBy()` is now optional again.
|
||||
|
||||
## [0.5.1](https://github.com/brick/math/releases/tag/0.5.1) - 2015-07-05
|
||||
|
||||
**New method: `BigNumber::toScale()`**
|
||||
|
||||
This allows to convert any `BigNumber` to a `BigDecimal` with a given scale, using rounding if necessary.
|
||||
|
||||
## [0.5.0](https://github.com/brick/math/releases/tag/0.5.0) - 2015-07-04
|
||||
|
||||
**New features**
|
||||
- Common `BigNumber` interface for all classes, with the following methods:
|
||||
- `sign()` and derived methods (`isZero()`, `isPositive()`, ...)
|
||||
- `compareTo()` and derived methods (`isEqualTo()`, `isGreaterThan()`, ...) that work across different `BigNumber` types
|
||||
- `toBigInteger()`, `toBigDecimal()`, `toBigRational`() conversion methods
|
||||
- `toInteger()` and `toFloat()` conversion methods to native types
|
||||
- Unified `of()` behaviour: every class now accepts any type of number, provided that it can be safely converted to the current type
|
||||
- New method: `BigDecimal::exactlyDividedBy()`; this method automatically computes the scale of the result, provided that the division yields a finite number of digits
|
||||
- New methods: `BigRational::quotient()` and `remainder()`
|
||||
- Fine-grained exceptions: `DivisionByZeroException`, `RoundingNecessaryException`, `NumberFormatException`
|
||||
- Factory methods `zero()`, `one()` and `ten()` available in all classes
|
||||
- Rounding mode reintroduced in `BigInteger::dividedBy()`
|
||||
|
||||
This release also comes with many performance improvements.
|
||||
|
||||
---
|
||||
|
||||
**Breaking changes**
|
||||
- `BigInteger`:
|
||||
- `getSign()` is renamed to `sign()`
|
||||
- `toString()` is renamed to `toBase()`
|
||||
- `BigInteger::dividedBy()` now throws an exception by default if the remainder is not zero; use `quotient()` to get the previous behaviour
|
||||
- `BigDecimal`:
|
||||
- `getSign()` is renamed to `sign()`
|
||||
- `getUnscaledValue()` is renamed to `unscaledValue()`
|
||||
- `getScale()` is renamed to `scale()`
|
||||
- `getIntegral()` is renamed to `integral()`
|
||||
- `getFraction()` is renamed to `fraction()`
|
||||
- `divideAndRemainder()` is renamed to `quotientAndRemainder()`
|
||||
- `dividedBy()` now takes a **mandatory** `$scale` parameter **before** the rounding mode
|
||||
- `toBigInteger()` does not accept a `$roundingMode` parameter anymore
|
||||
- `toBigRational()` does not simplify the fraction anymore; explicitly add `->simplified()` to get the previous behaviour
|
||||
- `BigRational`:
|
||||
- `getSign()` is renamed to `sign()`
|
||||
- `getNumerator()` is renamed to `numerator()`
|
||||
- `getDenominator()` is renamed to `denominator()`
|
||||
- `of()` is renamed to `nd()`, while `parse()` is renamed to `of()`
|
||||
- Miscellaneous:
|
||||
- `ArithmeticException` is moved to an `Exception\` sub-namespace
|
||||
- `of()` factory methods now throw `NumberFormatException` instead of `InvalidArgumentException`
|
||||
|
||||
## [0.4.3](https://github.com/brick/math/releases/tag/0.4.3) - 2016-03-31
|
||||
|
||||
Backport of two bug fixes from the 0.5 branch:
|
||||
- `BigInteger::parse()` did not always throw `InvalidArgumentException` as expected
|
||||
- Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6.
|
||||
|
||||
## [0.4.2](https://github.com/brick/math/releases/tag/0.4.2) - 2015-06-16
|
||||
|
||||
New method: `BigDecimal::stripTrailingZeros()`
|
||||
|
||||
## [0.4.1](https://github.com/brick/math/releases/tag/0.4.1) - 2015-06-12
|
||||
|
||||
Introducing a `BigRational` class, to perform calculations on fractions of any size.
|
||||
|
||||
## [0.4.0](https://github.com/brick/math/releases/tag/0.4.0) - 2015-06-12
|
||||
|
||||
Rounding modes have been removed from `BigInteger`, and are now a concept specific to `BigDecimal`.
|
||||
|
||||
`BigInteger::dividedBy()` now always returns the quotient of the division.
|
||||
|
||||
## [0.3.5](https://github.com/brick/math/releases/tag/0.3.5) - 2016-03-31
|
||||
|
||||
Backport of two bug fixes from the 0.5 branch:
|
||||
|
||||
- `BigInteger::parse()` did not always throw `InvalidArgumentException` as expected
|
||||
- Dividing by a negative power of 1 with the same scale as the dividend could trigger an incorrect optimization which resulted in a wrong result. See #6.
|
||||
|
||||
## [0.3.4](https://github.com/brick/math/releases/tag/0.3.4) - 2015-06-11
|
||||
|
||||
New methods:
|
||||
- `BigInteger::remainder()` returns the remainder of a division only
|
||||
- `BigInteger::gcd()` returns the greatest common divisor of two numbers
|
||||
|
||||
## [0.3.3](https://github.com/brick/math/releases/tag/0.3.3) - 2015-06-07
|
||||
|
||||
Fix `toString()` not handling negative numbers.
|
||||
|
||||
## [0.3.2](https://github.com/brick/math/releases/tag/0.3.2) - 2015-06-07
|
||||
|
||||
`BigInteger` and `BigDecimal` now have a `getSign()` method that returns:
|
||||
- `-1` if the number is negative
|
||||
- `0` if the number is zero
|
||||
- `1` if the number is positive
|
||||
|
||||
## [0.3.1](https://github.com/brick/math/releases/tag/0.3.1) - 2015-06-05
|
||||
|
||||
Minor performance improvements
|
||||
|
||||
## [0.3.0](https://github.com/brick/math/releases/tag/0.3.0) - 2015-06-04
|
||||
|
||||
The `$roundingMode` and `$scale` parameters have been swapped in `BigDecimal::dividedBy()`.
|
||||
|
||||
## [0.2.2](https://github.com/brick/math/releases/tag/0.2.2) - 2015-06-04
|
||||
|
||||
Stronger immutability guarantee for `BigInteger` and `BigDecimal`.
|
||||
|
||||
So far, it would have been possible to break immutability of these classes by calling the `unserialize()` internal function. This release fixes that.
|
||||
|
||||
## [0.2.1](https://github.com/brick/math/releases/tag/0.2.1) - 2015-06-02
|
||||
|
||||
Added `BigDecimal::divideAndRemainder()`
|
||||
|
||||
## [0.2.0](https://github.com/brick/math/releases/tag/0.2.0) - 2015-05-22
|
||||
|
||||
- `min()` and `max()` do not accept an `array` anymore, but a variable number of parameters
|
||||
- **minimum PHP version is now 5.6**
|
||||
- continuous integration with PHP 7
|
||||
|
||||
## [0.1.1](https://github.com/brick/math/releases/tag/0.1.1) - 2014-09-01
|
||||
|
||||
- Added `BigInteger::power()`
|
||||
- Added HHVM support
|
||||
|
||||
## [0.1.0](https://github.com/brick/math/releases/tag/0.1.0) - 2014-08-31
|
||||
|
||||
First beta release.
|
||||
|
20
vendor/brick/math/LICENSE
vendored
Normal file
20
vendor/brick/math/LICENSE
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-present Benjamin Morel
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
34
vendor/brick/math/composer.json
vendored
Normal file
34
vendor/brick/math/composer.json
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"name": "brick/math",
|
||||
"description": "Arbitrary-precision arithmetic library",
|
||||
"type": "library",
|
||||
"keywords": [
|
||||
"Brick",
|
||||
"Math",
|
||||
"Arbitrary-precision",
|
||||
"Arithmetic",
|
||||
"BigInteger",
|
||||
"BigDecimal",
|
||||
"BigRational",
|
||||
"Bignum"
|
||||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.0",
|
||||
"php-coveralls/php-coveralls": "^2.2",
|
||||
"vimeo/psalm": "5.0.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Brick\\Math\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Brick\\Math\\Tests\\": "tests/"
|
||||
}
|
||||
}
|
||||
}
|
786
vendor/brick/math/src/BigDecimal.php
vendored
Normal file
786
vendor/brick/math/src/BigDecimal.php
vendored
Normal file
@ -0,0 +1,786 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math;
|
||||
|
||||
use Brick\Math\Exception\DivisionByZeroException;
|
||||
use Brick\Math\Exception\MathException;
|
||||
use Brick\Math\Exception\NegativeNumberException;
|
||||
use Brick\Math\Internal\Calculator;
|
||||
|
||||
/**
|
||||
* Immutable, arbitrary-precision signed decimal numbers.
|
||||
*
|
||||
* @psalm-immutable
|
||||
*/
|
||||
final class BigDecimal extends BigNumber
|
||||
{
|
||||
/**
|
||||
* The unscaled value of this decimal number.
|
||||
*
|
||||
* This is a string of digits with an optional leading minus sign.
|
||||
* No leading zero must be present.
|
||||
* No leading minus sign must be present if the value is 0.
|
||||
*/
|
||||
private string $value;
|
||||
|
||||
/**
|
||||
* The scale (number of digits after the decimal point) of this decimal number.
|
||||
*
|
||||
* This must be zero or more.
|
||||
*/
|
||||
private int $scale;
|
||||
|
||||
/**
|
||||
* Protected constructor. Use a factory method to obtain an instance.
|
||||
*
|
||||
* @param string $value The unscaled value, validated.
|
||||
* @param int $scale The scale, validated.
|
||||
*/
|
||||
protected function __construct(string $value, int $scale = 0)
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->scale = $scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BigDecimal of the given value.
|
||||
*
|
||||
* @throws MathException If the value cannot be converted to a BigDecimal.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function of(BigNumber|int|float|string $value) : BigDecimal
|
||||
{
|
||||
return parent::of($value)->toBigDecimal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BigDecimal from an unscaled value and a scale.
|
||||
*
|
||||
* Example: `(12345, 3)` will result in the BigDecimal `12.345`.
|
||||
*
|
||||
* @param BigNumber|int|float|string $value The unscaled value. Must be convertible to a BigInteger.
|
||||
* @param int $scale The scale of the number, positive or zero.
|
||||
*
|
||||
* @throws \InvalidArgumentException If the scale is negative.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function ofUnscaledValue(BigNumber|int|float|string $value, int $scale = 0) : BigDecimal
|
||||
{
|
||||
if ($scale < 0) {
|
||||
throw new \InvalidArgumentException('The scale cannot be negative.');
|
||||
}
|
||||
|
||||
return new BigDecimal((string) BigInteger::of($value), $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a BigDecimal representing zero, with a scale of zero.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function zero() : BigDecimal
|
||||
{
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticVariable
|
||||
* @var BigDecimal|null $zero
|
||||
*/
|
||||
static $zero;
|
||||
|
||||
if ($zero === null) {
|
||||
$zero = new BigDecimal('0');
|
||||
}
|
||||
|
||||
return $zero;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a BigDecimal representing one, with a scale of zero.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function one() : BigDecimal
|
||||
{
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticVariable
|
||||
* @var BigDecimal|null $one
|
||||
*/
|
||||
static $one;
|
||||
|
||||
if ($one === null) {
|
||||
$one = new BigDecimal('1');
|
||||
}
|
||||
|
||||
return $one;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a BigDecimal representing ten, with a scale of zero.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function ten() : BigDecimal
|
||||
{
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticVariable
|
||||
* @var BigDecimal|null $ten
|
||||
*/
|
||||
static $ten;
|
||||
|
||||
if ($ten === null) {
|
||||
$ten = new BigDecimal('10');
|
||||
}
|
||||
|
||||
return $ten;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of this number and the given one.
|
||||
*
|
||||
* The result has a scale of `max($this->scale, $that->scale)`.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigDecimal.
|
||||
*
|
||||
* @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
|
||||
*/
|
||||
public function plus(BigNumber|int|float|string $that) : BigDecimal
|
||||
{
|
||||
$that = BigDecimal::of($that);
|
||||
|
||||
if ($that->value === '0' && $that->scale <= $this->scale) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($this->value === '0' && $this->scale <= $that->scale) {
|
||||
return $that;
|
||||
}
|
||||
|
||||
[$a, $b] = $this->scaleValues($this, $that);
|
||||
|
||||
$value = Calculator::get()->add($a, $b);
|
||||
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
|
||||
|
||||
return new BigDecimal($value, $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the difference of this number and the given one.
|
||||
*
|
||||
* The result has a scale of `max($this->scale, $that->scale)`.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigDecimal.
|
||||
*
|
||||
* @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
|
||||
*/
|
||||
public function minus(BigNumber|int|float|string $that) : BigDecimal
|
||||
{
|
||||
$that = BigDecimal::of($that);
|
||||
|
||||
if ($that->value === '0' && $that->scale <= $this->scale) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
[$a, $b] = $this->scaleValues($this, $that);
|
||||
|
||||
$value = Calculator::get()->sub($a, $b);
|
||||
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
|
||||
|
||||
return new BigDecimal($value, $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the product of this number and the given one.
|
||||
*
|
||||
* The result has a scale of `$this->scale + $that->scale`.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigDecimal.
|
||||
*
|
||||
* @throws MathException If the multiplier is not a valid number, or is not convertible to a BigDecimal.
|
||||
*/
|
||||
public function multipliedBy(BigNumber|int|float|string $that) : BigDecimal
|
||||
{
|
||||
$that = BigDecimal::of($that);
|
||||
|
||||
if ($that->value === '1' && $that->scale === 0) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($this->value === '1' && $this->scale === 0) {
|
||||
return $that;
|
||||
}
|
||||
|
||||
$value = Calculator::get()->mul($this->value, $that->value);
|
||||
$scale = $this->scale + $that->scale;
|
||||
|
||||
return new BigDecimal($value, $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result of the division of this number by the given one, at the given scale.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The divisor.
|
||||
* @param int|null $scale The desired scale, or null to use the scale of this number.
|
||||
* @param int $roundingMode An optional rounding mode.
|
||||
*
|
||||
* @throws \InvalidArgumentException If the scale or rounding mode is invalid.
|
||||
* @throws MathException If the number is invalid, is zero, or rounding was necessary.
|
||||
*/
|
||||
public function dividedBy(BigNumber|int|float|string $that, ?int $scale = null, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
|
||||
{
|
||||
$that = BigDecimal::of($that);
|
||||
|
||||
if ($that->isZero()) {
|
||||
throw DivisionByZeroException::divisionByZero();
|
||||
}
|
||||
|
||||
if ($scale === null) {
|
||||
$scale = $this->scale;
|
||||
} elseif ($scale < 0) {
|
||||
throw new \InvalidArgumentException('Scale cannot be negative.');
|
||||
}
|
||||
|
||||
if ($that->value === '1' && $that->scale === 0 && $scale === $this->scale) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$p = $this->valueWithMinScale($that->scale + $scale);
|
||||
$q = $that->valueWithMinScale($this->scale - $scale);
|
||||
|
||||
$result = Calculator::get()->divRound($p, $q, $roundingMode);
|
||||
|
||||
return new BigDecimal($result, $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exact result of the division of this number by the given one.
|
||||
*
|
||||
* The scale of the result is automatically calculated to fit all the fraction digits.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
|
||||
*
|
||||
* @throws MathException If the divisor is not a valid number, is not convertible to a BigDecimal, is zero,
|
||||
* or the result yields an infinite number of digits.
|
||||
*/
|
||||
public function exactlyDividedBy(BigNumber|int|float|string $that) : BigDecimal
|
||||
{
|
||||
$that = BigDecimal::of($that);
|
||||
|
||||
if ($that->value === '0') {
|
||||
throw DivisionByZeroException::divisionByZero();
|
||||
}
|
||||
|
||||
[, $b] = $this->scaleValues($this, $that);
|
||||
|
||||
$d = \rtrim($b, '0');
|
||||
$scale = \strlen($b) - \strlen($d);
|
||||
|
||||
$calculator = Calculator::get();
|
||||
|
||||
foreach ([5, 2] as $prime) {
|
||||
for (;;) {
|
||||
$lastDigit = (int) $d[-1];
|
||||
|
||||
if ($lastDigit % $prime !== 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
$d = $calculator->divQ($d, (string) $prime);
|
||||
$scale++;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->dividedBy($that, $scale)->stripTrailingZeros();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this number exponentiated to the given value.
|
||||
*
|
||||
* The result has a scale of `$this->scale * $exponent`.
|
||||
*
|
||||
* @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
|
||||
*/
|
||||
public function power(int $exponent) : BigDecimal
|
||||
{
|
||||
if ($exponent === 0) {
|
||||
return BigDecimal::one();
|
||||
}
|
||||
|
||||
if ($exponent === 1) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($exponent < 0 || $exponent > Calculator::MAX_POWER) {
|
||||
throw new \InvalidArgumentException(\sprintf(
|
||||
'The exponent %d is not in the range 0 to %d.',
|
||||
$exponent,
|
||||
Calculator::MAX_POWER
|
||||
));
|
||||
}
|
||||
|
||||
return new BigDecimal(Calculator::get()->pow($this->value, $exponent), $this->scale * $exponent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the quotient of the division of this number by this given one.
|
||||
*
|
||||
* The quotient has a scale of `0`.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
|
||||
*
|
||||
* @throws MathException If the divisor is not a valid decimal number, or is zero.
|
||||
*/
|
||||
public function quotient(BigNumber|int|float|string $that) : BigDecimal
|
||||
{
|
||||
$that = BigDecimal::of($that);
|
||||
|
||||
if ($that->isZero()) {
|
||||
throw DivisionByZeroException::divisionByZero();
|
||||
}
|
||||
|
||||
$p = $this->valueWithMinScale($that->scale);
|
||||
$q = $that->valueWithMinScale($this->scale);
|
||||
|
||||
$quotient = Calculator::get()->divQ($p, $q);
|
||||
|
||||
return new BigDecimal($quotient, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the remainder of the division of this number by this given one.
|
||||
*
|
||||
* The remainder has a scale of `max($this->scale, $that->scale)`.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
|
||||
*
|
||||
* @throws MathException If the divisor is not a valid decimal number, or is zero.
|
||||
*/
|
||||
public function remainder(BigNumber|int|float|string $that) : BigDecimal
|
||||
{
|
||||
$that = BigDecimal::of($that);
|
||||
|
||||
if ($that->isZero()) {
|
||||
throw DivisionByZeroException::divisionByZero();
|
||||
}
|
||||
|
||||
$p = $this->valueWithMinScale($that->scale);
|
||||
$q = $that->valueWithMinScale($this->scale);
|
||||
|
||||
$remainder = Calculator::get()->divR($p, $q);
|
||||
|
||||
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
|
||||
|
||||
return new BigDecimal($remainder, $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the quotient and remainder of the division of this number by the given one.
|
||||
*
|
||||
* The quotient has a scale of `0`, and the remainder has a scale of `max($this->scale, $that->scale)`.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
|
||||
*
|
||||
* @return BigDecimal[] An array containing the quotient and the remainder.
|
||||
*
|
||||
* @throws MathException If the divisor is not a valid decimal number, or is zero.
|
||||
*/
|
||||
public function quotientAndRemainder(BigNumber|int|float|string $that) : array
|
||||
{
|
||||
$that = BigDecimal::of($that);
|
||||
|
||||
if ($that->isZero()) {
|
||||
throw DivisionByZeroException::divisionByZero();
|
||||
}
|
||||
|
||||
$p = $this->valueWithMinScale($that->scale);
|
||||
$q = $that->valueWithMinScale($this->scale);
|
||||
|
||||
[$quotient, $remainder] = Calculator::get()->divQR($p, $q);
|
||||
|
||||
$scale = $this->scale > $that->scale ? $this->scale : $that->scale;
|
||||
|
||||
$quotient = new BigDecimal($quotient, 0);
|
||||
$remainder = new BigDecimal($remainder, $scale);
|
||||
|
||||
return [$quotient, $remainder];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the square root of this number, rounded down to the given number of decimals.
|
||||
*
|
||||
* @throws \InvalidArgumentException If the scale is negative.
|
||||
* @throws NegativeNumberException If this number is negative.
|
||||
*/
|
||||
public function sqrt(int $scale) : BigDecimal
|
||||
{
|
||||
if ($scale < 0) {
|
||||
throw new \InvalidArgumentException('Scale cannot be negative.');
|
||||
}
|
||||
|
||||
if ($this->value === '0') {
|
||||
return new BigDecimal('0', $scale);
|
||||
}
|
||||
|
||||
if ($this->value[0] === '-') {
|
||||
throw new NegativeNumberException('Cannot calculate the square root of a negative number.');
|
||||
}
|
||||
|
||||
$value = $this->value;
|
||||
$addDigits = 2 * $scale - $this->scale;
|
||||
|
||||
if ($addDigits > 0) {
|
||||
// add zeros
|
||||
$value .= \str_repeat('0', $addDigits);
|
||||
} elseif ($addDigits < 0) {
|
||||
// trim digits
|
||||
if (-$addDigits >= \strlen($this->value)) {
|
||||
// requesting a scale too low, will always yield a zero result
|
||||
return new BigDecimal('0', $scale);
|
||||
}
|
||||
|
||||
$value = \substr($value, 0, $addDigits);
|
||||
}
|
||||
|
||||
$value = Calculator::get()->sqrt($value);
|
||||
|
||||
return new BigDecimal($value, $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of this BigDecimal with the decimal point moved $n places to the left.
|
||||
*/
|
||||
public function withPointMovedLeft(int $n) : BigDecimal
|
||||
{
|
||||
if ($n === 0) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($n < 0) {
|
||||
return $this->withPointMovedRight(-$n);
|
||||
}
|
||||
|
||||
return new BigDecimal($this->value, $this->scale + $n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of this BigDecimal with the decimal point moved $n places to the right.
|
||||
*/
|
||||
public function withPointMovedRight(int $n) : BigDecimal
|
||||
{
|
||||
if ($n === 0) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($n < 0) {
|
||||
return $this->withPointMovedLeft(-$n);
|
||||
}
|
||||
|
||||
$value = $this->value;
|
||||
$scale = $this->scale - $n;
|
||||
|
||||
if ($scale < 0) {
|
||||
if ($value !== '0') {
|
||||
$value .= \str_repeat('0', -$scale);
|
||||
}
|
||||
$scale = 0;
|
||||
}
|
||||
|
||||
return new BigDecimal($value, $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a copy of this BigDecimal with any trailing zeros removed from the fractional part.
|
||||
*/
|
||||
public function stripTrailingZeros() : BigDecimal
|
||||
{
|
||||
if ($this->scale === 0) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$trimmedValue = \rtrim($this->value, '0');
|
||||
|
||||
if ($trimmedValue === '') {
|
||||
return BigDecimal::zero();
|
||||
}
|
||||
|
||||
$trimmableZeros = \strlen($this->value) - \strlen($trimmedValue);
|
||||
|
||||
if ($trimmableZeros === 0) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($trimmableZeros > $this->scale) {
|
||||
$trimmableZeros = $this->scale;
|
||||
}
|
||||
|
||||
$value = \substr($this->value, 0, -$trimmableZeros);
|
||||
$scale = $this->scale - $trimmableZeros;
|
||||
|
||||
return new BigDecimal($value, $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute value of this number.
|
||||
*/
|
||||
public function abs() : BigDecimal
|
||||
{
|
||||
return $this->isNegative() ? $this->negated() : $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the negated value of this number.
|
||||
*/
|
||||
public function negated() : BigDecimal
|
||||
{
|
||||
return new BigDecimal(Calculator::get()->neg($this->value), $this->scale);
|
||||
}
|
||||
|
||||
public function compareTo(BigNumber|int|float|string $that) : int
|
||||
{
|
||||
$that = BigNumber::of($that);
|
||||
|
||||
if ($that instanceof BigInteger) {
|
||||
$that = $that->toBigDecimal();
|
||||
}
|
||||
|
||||
if ($that instanceof BigDecimal) {
|
||||
[$a, $b] = $this->scaleValues($this, $that);
|
||||
|
||||
return Calculator::get()->cmp($a, $b);
|
||||
}
|
||||
|
||||
return - $that->compareTo($this);
|
||||
}
|
||||
|
||||
public function getSign() : int
|
||||
{
|
||||
return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1);
|
||||
}
|
||||
|
||||
public function getUnscaledValue() : BigInteger
|
||||
{
|
||||
return self::newBigInteger($this->value);
|
||||
}
|
||||
|
||||
public function getScale() : int
|
||||
{
|
||||
return $this->scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representing the integral part of this decimal number.
|
||||
*
|
||||
* Example: `-123.456` => `-123`.
|
||||
*/
|
||||
public function getIntegralPart() : string
|
||||
{
|
||||
if ($this->scale === 0) {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
$value = $this->getUnscaledValueWithLeadingZeros();
|
||||
|
||||
return \substr($value, 0, -$this->scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representing the fractional part of this decimal number.
|
||||
*
|
||||
* If the scale is zero, an empty string is returned.
|
||||
*
|
||||
* Examples: `-123.456` => '456', `123` => ''.
|
||||
*/
|
||||
public function getFractionalPart() : string
|
||||
{
|
||||
if ($this->scale === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$value = $this->getUnscaledValueWithLeadingZeros();
|
||||
|
||||
return \substr($value, -$this->scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this decimal number has a non-zero fractional part.
|
||||
*/
|
||||
public function hasNonZeroFractionalPart() : bool
|
||||
{
|
||||
return $this->getFractionalPart() !== \str_repeat('0', $this->scale);
|
||||
}
|
||||
|
||||
public function toBigInteger() : BigInteger
|
||||
{
|
||||
$zeroScaleDecimal = $this->scale === 0 ? $this : $this->dividedBy(1, 0);
|
||||
|
||||
return self::newBigInteger($zeroScaleDecimal->value);
|
||||
}
|
||||
|
||||
public function toBigDecimal() : BigDecimal
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toBigRational() : BigRational
|
||||
{
|
||||
$numerator = self::newBigInteger($this->value);
|
||||
$denominator = self::newBigInteger('1' . \str_repeat('0', $this->scale));
|
||||
|
||||
return self::newBigRational($numerator, $denominator, false);
|
||||
}
|
||||
|
||||
public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
|
||||
{
|
||||
if ($scale === $this->scale) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return $this->dividedBy(BigDecimal::one(), $scale, $roundingMode);
|
||||
}
|
||||
|
||||
public function toInt() : int
|
||||
{
|
||||
return $this->toBigInteger()->toInt();
|
||||
}
|
||||
|
||||
public function toFloat() : float
|
||||
{
|
||||
return (float) (string) $this;
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
{
|
||||
if ($this->scale === 0) {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
$value = $this->getUnscaledValueWithLeadingZeros();
|
||||
|
||||
return \substr($value, 0, -$this->scale) . '.' . \substr($value, -$this->scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is required for serializing the object and SHOULD NOT be accessed directly.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return array{value: string, scale: int}
|
||||
*/
|
||||
public function __serialize(): array
|
||||
{
|
||||
return ['value' => $this->value, 'scale' => $this->scale];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is only here to allow unserializing the object and cannot be accessed directly.
|
||||
*
|
||||
* @internal
|
||||
* @psalm-suppress RedundantPropertyInitializationCheck
|
||||
*
|
||||
* @param array{value: string, scale: int} $data
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
if (isset($this->value)) {
|
||||
throw new \LogicException('__unserialize() is an internal function, it must not be called directly.');
|
||||
}
|
||||
|
||||
$this->value = $data['value'];
|
||||
$this->scale = $data['scale'];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is required by interface Serializable and SHOULD NOT be accessed directly.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function serialize() : string
|
||||
{
|
||||
return $this->value . ':' . $this->scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is only here to implement interface Serializable and cannot be accessed directly.
|
||||
*
|
||||
* @internal
|
||||
* @psalm-suppress RedundantPropertyInitializationCheck
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function unserialize($value) : void
|
||||
{
|
||||
if (isset($this->value)) {
|
||||
throw new \LogicException('unserialize() is an internal function, it must not be called directly.');
|
||||
}
|
||||
|
||||
[$value, $scale] = \explode(':', $value);
|
||||
|
||||
$this->value = $value;
|
||||
$this->scale = (int) $scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the internal values of the given decimal numbers on the same scale.
|
||||
*
|
||||
* @return array{string, string} The scaled integer values of $x and $y.
|
||||
*/
|
||||
private function scaleValues(BigDecimal $x, BigDecimal $y) : array
|
||||
{
|
||||
$a = $x->value;
|
||||
$b = $y->value;
|
||||
|
||||
if ($b !== '0' && $x->scale > $y->scale) {
|
||||
$b .= \str_repeat('0', $x->scale - $y->scale);
|
||||
} elseif ($a !== '0' && $x->scale < $y->scale) {
|
||||
$a .= \str_repeat('0', $y->scale - $x->scale);
|
||||
}
|
||||
|
||||
return [$a, $b];
|
||||
}
|
||||
|
||||
private function valueWithMinScale(int $scale) : string
|
||||
{
|
||||
$value = $this->value;
|
||||
|
||||
if ($this->value !== '0' && $scale > $this->scale) {
|
||||
$value .= \str_repeat('0', $scale - $this->scale);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds leading zeros if necessary to the unscaled value to represent the full decimal number.
|
||||
*/
|
||||
private function getUnscaledValueWithLeadingZeros() : string
|
||||
{
|
||||
$value = $this->value;
|
||||
$targetLength = $this->scale + 1;
|
||||
$negative = ($value[0] === '-');
|
||||
$length = \strlen($value);
|
||||
|
||||
if ($negative) {
|
||||
$length--;
|
||||
}
|
||||
|
||||
if ($length >= $targetLength) {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
if ($negative) {
|
||||
$value = \substr($value, 1);
|
||||
}
|
||||
|
||||
$value = \str_pad($value, $targetLength, '0', STR_PAD_LEFT);
|
||||
|
||||
if ($negative) {
|
||||
$value = '-' . $value;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
}
|
1079
vendor/brick/math/src/BigInteger.php
vendored
Normal file
1079
vendor/brick/math/src/BigInteger.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
512
vendor/brick/math/src/BigNumber.php
vendored
Normal file
512
vendor/brick/math/src/BigNumber.php
vendored
Normal file
@ -0,0 +1,512 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math;
|
||||
|
||||
use Brick\Math\Exception\DivisionByZeroException;
|
||||
use Brick\Math\Exception\MathException;
|
||||
use Brick\Math\Exception\NumberFormatException;
|
||||
use Brick\Math\Exception\RoundingNecessaryException;
|
||||
|
||||
/**
|
||||
* Common interface for arbitrary-precision rational numbers.
|
||||
*
|
||||
* @psalm-immutable
|
||||
*/
|
||||
abstract class BigNumber implements \Serializable, \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* The regular expression used to parse integer, decimal and rational numbers.
|
||||
*/
|
||||
private const PARSE_REGEXP =
|
||||
'/^' .
|
||||
'(?<sign>[\-\+])?' .
|
||||
'(?:' .
|
||||
'(?:' .
|
||||
'(?<integral>[0-9]+)?' .
|
||||
'(?<point>\.)?' .
|
||||
'(?<fractional>[0-9]+)?' .
|
||||
'(?:[eE](?<exponent>[\-\+]?[0-9]+))?' .
|
||||
')|(?:' .
|
||||
'(?<numerator>[0-9]+)' .
|
||||
'\/?' .
|
||||
'(?<denominator>[0-9]+)' .
|
||||
')' .
|
||||
')' .
|
||||
'$/';
|
||||
|
||||
/**
|
||||
* Creates a BigNumber of the given value.
|
||||
*
|
||||
* The concrete return type is dependent on the given value, with the following rules:
|
||||
*
|
||||
* - BigNumber instances are returned as is
|
||||
* - integer numbers are returned as BigInteger
|
||||
* - floating point numbers are converted to a string then parsed as such
|
||||
* - strings containing a `/` character are returned as BigRational
|
||||
* - strings containing a `.` character or using an exponential notation are returned as BigDecimal
|
||||
* - strings containing only digits with an optional leading `+` or `-` sign are returned as BigInteger
|
||||
*
|
||||
* @throws NumberFormatException If the format of the number is not valid.
|
||||
* @throws DivisionByZeroException If the value represents a rational number with a denominator of zero.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function of(BigNumber|int|float|string $value) : BigNumber
|
||||
{
|
||||
if ($value instanceof BigNumber) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (\is_int($value)) {
|
||||
return new BigInteger((string) $value);
|
||||
}
|
||||
|
||||
$value = \is_float($value) ? self::floatToString($value) : $value;
|
||||
|
||||
$throw = static function() use ($value) : void {
|
||||
throw new NumberFormatException(\sprintf(
|
||||
'The given value "%s" does not represent a valid number.',
|
||||
$value
|
||||
));
|
||||
};
|
||||
|
||||
if (\preg_match(self::PARSE_REGEXP, $value, $matches) !== 1) {
|
||||
$throw();
|
||||
}
|
||||
|
||||
$getMatch = static fn(string $value): ?string => (($matches[$value] ?? '') !== '') ? $matches[$value] : null;
|
||||
|
||||
$sign = $getMatch('sign');
|
||||
$numerator = $getMatch('numerator');
|
||||
$denominator = $getMatch('denominator');
|
||||
|
||||
if ($numerator !== null) {
|
||||
assert($denominator !== null);
|
||||
|
||||
if ($sign !== null) {
|
||||
$numerator = $sign . $numerator;
|
||||
}
|
||||
|
||||
$numerator = self::cleanUp($numerator);
|
||||
$denominator = self::cleanUp($denominator);
|
||||
|
||||
if ($denominator === '0') {
|
||||
throw DivisionByZeroException::denominatorMustNotBeZero();
|
||||
}
|
||||
|
||||
return new BigRational(
|
||||
new BigInteger($numerator),
|
||||
new BigInteger($denominator),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
$point = $getMatch('point');
|
||||
$integral = $getMatch('integral');
|
||||
$fractional = $getMatch('fractional');
|
||||
$exponent = $getMatch('exponent');
|
||||
|
||||
if ($integral === null && $fractional === null) {
|
||||
$throw();
|
||||
}
|
||||
|
||||
if ($integral === null) {
|
||||
$integral = '0';
|
||||
}
|
||||
|
||||
if ($point !== null || $exponent !== null) {
|
||||
$fractional = ($fractional ?? '');
|
||||
$exponent = ($exponent !== null) ? (int) $exponent : 0;
|
||||
|
||||
if ($exponent === PHP_INT_MIN || $exponent === PHP_INT_MAX) {
|
||||
throw new NumberFormatException('Exponent too large.');
|
||||
}
|
||||
|
||||
$unscaledValue = self::cleanUp(($sign ?? ''). $integral . $fractional);
|
||||
|
||||
$scale = \strlen($fractional) - $exponent;
|
||||
|
||||
if ($scale < 0) {
|
||||
if ($unscaledValue !== '0') {
|
||||
$unscaledValue .= \str_repeat('0', - $scale);
|
||||
}
|
||||
$scale = 0;
|
||||
}
|
||||
|
||||
return new BigDecimal($unscaledValue, $scale);
|
||||
}
|
||||
|
||||
$integral = self::cleanUp(($sign ?? '') . $integral);
|
||||
|
||||
return new BigInteger($integral);
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely converts float to string, avoiding locale-dependent issues.
|
||||
*
|
||||
* @see https://github.com/brick/math/pull/20
|
||||
*
|
||||
* @psalm-pure
|
||||
* @psalm-suppress ImpureFunctionCall
|
||||
*/
|
||||
private static function floatToString(float $float) : string
|
||||
{
|
||||
$currentLocale = \setlocale(LC_NUMERIC, '0');
|
||||
\setlocale(LC_NUMERIC, 'C');
|
||||
|
||||
$result = (string) $float;
|
||||
|
||||
\setlocale(LC_NUMERIC, $currentLocale);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy method to access BigInteger's protected constructor from sibling classes.
|
||||
*
|
||||
* @internal
|
||||
* @psalm-pure
|
||||
*/
|
||||
protected function newBigInteger(string $value) : BigInteger
|
||||
{
|
||||
return new BigInteger($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy method to access BigDecimal's protected constructor from sibling classes.
|
||||
*
|
||||
* @internal
|
||||
* @psalm-pure
|
||||
*/
|
||||
protected function newBigDecimal(string $value, int $scale = 0) : BigDecimal
|
||||
{
|
||||
return new BigDecimal($value, $scale);
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy method to access BigRational's protected constructor from sibling classes.
|
||||
*
|
||||
* @internal
|
||||
* @psalm-pure
|
||||
*/
|
||||
protected function newBigRational(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator) : BigRational
|
||||
{
|
||||
return new BigRational($numerator, $denominator, $checkDenominator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum of the given values.
|
||||
*
|
||||
* @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
|
||||
* to an instance of the class this method is called on.
|
||||
*
|
||||
* @throws \InvalidArgumentException If no values are given.
|
||||
* @throws MathException If an argument is not valid.
|
||||
*
|
||||
* @psalm-suppress LessSpecificReturnStatement
|
||||
* @psalm-suppress MoreSpecificReturnType
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function min(BigNumber|int|float|string ...$values) : static
|
||||
{
|
||||
$min = null;
|
||||
|
||||
foreach ($values as $value) {
|
||||
$value = static::of($value);
|
||||
|
||||
if ($min === null || $value->isLessThan($min)) {
|
||||
$min = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if ($min === null) {
|
||||
throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
|
||||
}
|
||||
|
||||
return $min;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum of the given values.
|
||||
*
|
||||
* @param BigNumber|int|float|string ...$values The numbers to compare. All the numbers need to be convertible
|
||||
* to an instance of the class this method is called on.
|
||||
*
|
||||
* @throws \InvalidArgumentException If no values are given.
|
||||
* @throws MathException If an argument is not valid.
|
||||
*
|
||||
* @psalm-suppress LessSpecificReturnStatement
|
||||
* @psalm-suppress MoreSpecificReturnType
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function max(BigNumber|int|float|string ...$values) : static
|
||||
{
|
||||
$max = null;
|
||||
|
||||
foreach ($values as $value) {
|
||||
$value = static::of($value);
|
||||
|
||||
if ($max === null || $value->isGreaterThan($max)) {
|
||||
$max = $value;
|
||||
}
|
||||
}
|
||||
|
||||
if ($max === null) {
|
||||
throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
|
||||
}
|
||||
|
||||
return $max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of the given values.
|
||||
*
|
||||
* @param BigNumber|int|float|string ...$values The numbers to add. All the numbers need to be convertible
|
||||
* to an instance of the class this method is called on.
|
||||
*
|
||||
* @throws \InvalidArgumentException If no values are given.
|
||||
* @throws MathException If an argument is not valid.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function sum(BigNumber|int|float|string ...$values) : static
|
||||
{
|
||||
/** @var static|null $sum */
|
||||
$sum = null;
|
||||
|
||||
foreach ($values as $value) {
|
||||
$value = static::of($value);
|
||||
|
||||
$sum = $sum === null ? $value : self::add($sum, $value);
|
||||
}
|
||||
|
||||
if ($sum === null) {
|
||||
throw new \InvalidArgumentException(__METHOD__ . '() expects at least one value.');
|
||||
}
|
||||
|
||||
return $sum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds two BigNumber instances in the correct order to avoid a RoundingNecessaryException.
|
||||
*
|
||||
* @todo This could be better resolved by creating an abstract protected method in BigNumber, and leaving to
|
||||
* concrete classes the responsibility to perform the addition themselves or delegate it to the given number,
|
||||
* depending on their ability to perform the operation. This will also require a version bump because we're
|
||||
* potentially breaking custom BigNumber implementations (if any...)
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
private static function add(BigNumber $a, BigNumber $b) : BigNumber
|
||||
{
|
||||
if ($a instanceof BigRational) {
|
||||
return $a->plus($b);
|
||||
}
|
||||
|
||||
if ($b instanceof BigRational) {
|
||||
return $b->plus($a);
|
||||
}
|
||||
|
||||
if ($a instanceof BigDecimal) {
|
||||
return $a->plus($b);
|
||||
}
|
||||
|
||||
if ($b instanceof BigDecimal) {
|
||||
return $b->plus($a);
|
||||
}
|
||||
|
||||
/** @var BigInteger $a */
|
||||
|
||||
return $a->plus($b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes optional leading zeros and + sign from the given number.
|
||||
*
|
||||
* @param string $number The number, validated as a non-empty string of digits with optional leading sign.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
private static function cleanUp(string $number) : string
|
||||
{
|
||||
$firstChar = $number[0];
|
||||
|
||||
if ($firstChar === '+' || $firstChar === '-') {
|
||||
$number = \substr($number, 1);
|
||||
}
|
||||
|
||||
$number = \ltrim($number, '0');
|
||||
|
||||
if ($number === '') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
if ($firstChar === '-') {
|
||||
return '-' . $number;
|
||||
}
|
||||
|
||||
return $number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number is equal to the given one.
|
||||
*/
|
||||
public function isEqualTo(BigNumber|int|float|string $that) : bool
|
||||
{
|
||||
return $this->compareTo($that) === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number is strictly lower than the given one.
|
||||
*/
|
||||
public function isLessThan(BigNumber|int|float|string $that) : bool
|
||||
{
|
||||
return $this->compareTo($that) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number is lower than or equal to the given one.
|
||||
*/
|
||||
public function isLessThanOrEqualTo(BigNumber|int|float|string $that) : bool
|
||||
{
|
||||
return $this->compareTo($that) <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number is strictly greater than the given one.
|
||||
*/
|
||||
public function isGreaterThan(BigNumber|int|float|string $that) : bool
|
||||
{
|
||||
return $this->compareTo($that) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number is greater than or equal to the given one.
|
||||
*/
|
||||
public function isGreaterThanOrEqualTo(BigNumber|int|float|string $that) : bool
|
||||
{
|
||||
return $this->compareTo($that) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number equals zero.
|
||||
*/
|
||||
public function isZero() : bool
|
||||
{
|
||||
return $this->getSign() === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number is strictly negative.
|
||||
*/
|
||||
public function isNegative() : bool
|
||||
{
|
||||
return $this->getSign() < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number is negative or zero.
|
||||
*/
|
||||
public function isNegativeOrZero() : bool
|
||||
{
|
||||
return $this->getSign() <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number is strictly positive.
|
||||
*/
|
||||
public function isPositive() : bool
|
||||
{
|
||||
return $this->getSign() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this number is positive or zero.
|
||||
*/
|
||||
public function isPositiveOrZero() : bool
|
||||
{
|
||||
return $this->getSign() >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sign of this number.
|
||||
*
|
||||
* @return int -1 if the number is negative, 0 if zero, 1 if positive.
|
||||
*/
|
||||
abstract public function getSign() : int;
|
||||
|
||||
/**
|
||||
* Compares this number to the given one.
|
||||
*
|
||||
* @return int [-1,0,1] If `$this` is lower than, equal to, or greater than `$that`.
|
||||
*
|
||||
* @throws MathException If the number is not valid.
|
||||
*/
|
||||
abstract public function compareTo(BigNumber|int|float|string $that) : int;
|
||||
|
||||
/**
|
||||
* Converts this number to a BigInteger.
|
||||
*
|
||||
* @throws RoundingNecessaryException If this number cannot be converted to a BigInteger without rounding.
|
||||
*/
|
||||
abstract public function toBigInteger() : BigInteger;
|
||||
|
||||
/**
|
||||
* Converts this number to a BigDecimal.
|
||||
*
|
||||
* @throws RoundingNecessaryException If this number cannot be converted to a BigDecimal without rounding.
|
||||
*/
|
||||
abstract public function toBigDecimal() : BigDecimal;
|
||||
|
||||
/**
|
||||
* Converts this number to a BigRational.
|
||||
*/
|
||||
abstract public function toBigRational() : BigRational;
|
||||
|
||||
/**
|
||||
* Converts this number to a BigDecimal with the given scale, using rounding if necessary.
|
||||
*
|
||||
* @param int $scale The scale of the resulting `BigDecimal`.
|
||||
* @param int $roundingMode A `RoundingMode` constant.
|
||||
*
|
||||
* @throws RoundingNecessaryException If this number cannot be converted to the given scale without rounding.
|
||||
* This only applies when RoundingMode::UNNECESSARY is used.
|
||||
*/
|
||||
abstract public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal;
|
||||
|
||||
/**
|
||||
* Returns the exact value of this number as a native integer.
|
||||
*
|
||||
* If this number cannot be converted to a native integer without losing precision, an exception is thrown.
|
||||
* Note that the acceptable range for an integer depends on the platform and differs for 32-bit and 64-bit.
|
||||
*
|
||||
* @throws MathException If this number cannot be exactly converted to a native integer.
|
||||
*/
|
||||
abstract public function toInt() : int;
|
||||
|
||||
/**
|
||||
* Returns an approximation of this number as a floating-point value.
|
||||
*
|
||||
* Note that this method can discard information as the precision of a floating-point value
|
||||
* is inherently limited.
|
||||
*
|
||||
* If the number is greater than the largest representable floating point number, positive infinity is returned.
|
||||
* If the number is less than the smallest representable floating point number, negative infinity is returned.
|
||||
*/
|
||||
abstract public function toFloat() : float;
|
||||
|
||||
/**
|
||||
* Returns a string representation of this number.
|
||||
*
|
||||
* The output of this method can be parsed by the `of()` factory method;
|
||||
* this will yield an object equal to this one, without any information loss.
|
||||
*/
|
||||
abstract public function __toString() : string;
|
||||
|
||||
public function jsonSerialize() : string
|
||||
{
|
||||
return $this->__toString();
|
||||
}
|
||||
}
|
445
vendor/brick/math/src/BigRational.php
vendored
Normal file
445
vendor/brick/math/src/BigRational.php
vendored
Normal file
@ -0,0 +1,445 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math;
|
||||
|
||||
use Brick\Math\Exception\DivisionByZeroException;
|
||||
use Brick\Math\Exception\MathException;
|
||||
use Brick\Math\Exception\NumberFormatException;
|
||||
use Brick\Math\Exception\RoundingNecessaryException;
|
||||
|
||||
/**
|
||||
* An arbitrarily large rational number.
|
||||
*
|
||||
* This class is immutable.
|
||||
*
|
||||
* @psalm-immutable
|
||||
*/
|
||||
final class BigRational extends BigNumber
|
||||
{
|
||||
/**
|
||||
* The numerator.
|
||||
*/
|
||||
private BigInteger $numerator;
|
||||
|
||||
/**
|
||||
* The denominator. Always strictly positive.
|
||||
*/
|
||||
private BigInteger $denominator;
|
||||
|
||||
/**
|
||||
* Protected constructor. Use a factory method to obtain an instance.
|
||||
*
|
||||
* @param BigInteger $numerator The numerator.
|
||||
* @param BigInteger $denominator The denominator.
|
||||
* @param bool $checkDenominator Whether to check the denominator for negative and zero.
|
||||
*
|
||||
* @throws DivisionByZeroException If the denominator is zero.
|
||||
*/
|
||||
protected function __construct(BigInteger $numerator, BigInteger $denominator, bool $checkDenominator)
|
||||
{
|
||||
if ($checkDenominator) {
|
||||
if ($denominator->isZero()) {
|
||||
throw DivisionByZeroException::denominatorMustNotBeZero();
|
||||
}
|
||||
|
||||
if ($denominator->isNegative()) {
|
||||
$numerator = $numerator->negated();
|
||||
$denominator = $denominator->negated();
|
||||
}
|
||||
}
|
||||
|
||||
$this->numerator = $numerator;
|
||||
$this->denominator = $denominator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BigRational of the given value.
|
||||
*
|
||||
* @throws MathException If the value cannot be converted to a BigRational.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function of(BigNumber|int|float|string $value) : BigRational
|
||||
{
|
||||
return parent::of($value)->toBigRational();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BigRational out of a numerator and a denominator.
|
||||
*
|
||||
* If the denominator is negative, the signs of both the numerator and the denominator
|
||||
* will be inverted to ensure that the denominator is always positive.
|
||||
*
|
||||
* @param BigNumber|int|float|string $numerator The numerator. Must be convertible to a BigInteger.
|
||||
* @param BigNumber|int|float|string $denominator The denominator. Must be convertible to a BigInteger.
|
||||
*
|
||||
* @throws NumberFormatException If an argument does not represent a valid number.
|
||||
* @throws RoundingNecessaryException If an argument represents a non-integer number.
|
||||
* @throws DivisionByZeroException If the denominator is zero.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function nd(
|
||||
BigNumber|int|float|string $numerator,
|
||||
BigNumber|int|float|string $denominator,
|
||||
) : BigRational {
|
||||
$numerator = BigInteger::of($numerator);
|
||||
$denominator = BigInteger::of($denominator);
|
||||
|
||||
return new BigRational($numerator, $denominator, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a BigRational representing zero.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function zero() : BigRational
|
||||
{
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticVariable
|
||||
* @var BigRational|null $zero
|
||||
*/
|
||||
static $zero;
|
||||
|
||||
if ($zero === null) {
|
||||
$zero = new BigRational(BigInteger::zero(), BigInteger::one(), false);
|
||||
}
|
||||
|
||||
return $zero;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a BigRational representing one.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function one() : BigRational
|
||||
{
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticVariable
|
||||
* @var BigRational|null $one
|
||||
*/
|
||||
static $one;
|
||||
|
||||
if ($one === null) {
|
||||
$one = new BigRational(BigInteger::one(), BigInteger::one(), false);
|
||||
}
|
||||
|
||||
return $one;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a BigRational representing ten.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function ten() : BigRational
|
||||
{
|
||||
/**
|
||||
* @psalm-suppress ImpureStaticVariable
|
||||
* @var BigRational|null $ten
|
||||
*/
|
||||
static $ten;
|
||||
|
||||
if ($ten === null) {
|
||||
$ten = new BigRational(BigInteger::ten(), BigInteger::one(), false);
|
||||
}
|
||||
|
||||
return $ten;
|
||||
}
|
||||
|
||||
public function getNumerator() : BigInteger
|
||||
{
|
||||
return $this->numerator;
|
||||
}
|
||||
|
||||
public function getDenominator() : BigInteger
|
||||
{
|
||||
return $this->denominator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the quotient of the division of the numerator by the denominator.
|
||||
*/
|
||||
public function quotient() : BigInteger
|
||||
{
|
||||
return $this->numerator->quotient($this->denominator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the remainder of the division of the numerator by the denominator.
|
||||
*/
|
||||
public function remainder() : BigInteger
|
||||
{
|
||||
return $this->numerator->remainder($this->denominator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the quotient and remainder of the division of the numerator by the denominator.
|
||||
*
|
||||
* @return BigInteger[]
|
||||
*/
|
||||
public function quotientAndRemainder() : array
|
||||
{
|
||||
return $this->numerator->quotientAndRemainder($this->denominator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of this number and the given one.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The number to add.
|
||||
*
|
||||
* @throws MathException If the number is not valid.
|
||||
*/
|
||||
public function plus(BigNumber|int|float|string $that) : BigRational
|
||||
{
|
||||
$that = BigRational::of($that);
|
||||
|
||||
$numerator = $this->numerator->multipliedBy($that->denominator);
|
||||
$numerator = $numerator->plus($that->numerator->multipliedBy($this->denominator));
|
||||
$denominator = $this->denominator->multipliedBy($that->denominator);
|
||||
|
||||
return new BigRational($numerator, $denominator, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the difference of this number and the given one.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The number to subtract.
|
||||
*
|
||||
* @throws MathException If the number is not valid.
|
||||
*/
|
||||
public function minus(BigNumber|int|float|string $that) : BigRational
|
||||
{
|
||||
$that = BigRational::of($that);
|
||||
|
||||
$numerator = $this->numerator->multipliedBy($that->denominator);
|
||||
$numerator = $numerator->minus($that->numerator->multipliedBy($this->denominator));
|
||||
$denominator = $this->denominator->multipliedBy($that->denominator);
|
||||
|
||||
return new BigRational($numerator, $denominator, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the product of this number and the given one.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The multiplier.
|
||||
*
|
||||
* @throws MathException If the multiplier is not a valid number.
|
||||
*/
|
||||
public function multipliedBy(BigNumber|int|float|string $that) : BigRational
|
||||
{
|
||||
$that = BigRational::of($that);
|
||||
|
||||
$numerator = $this->numerator->multipliedBy($that->numerator);
|
||||
$denominator = $this->denominator->multipliedBy($that->denominator);
|
||||
|
||||
return new BigRational($numerator, $denominator, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result of the division of this number by the given one.
|
||||
*
|
||||
* @param BigNumber|int|float|string $that The divisor.
|
||||
*
|
||||
* @throws MathException If the divisor is not a valid number, or is zero.
|
||||
*/
|
||||
public function dividedBy(BigNumber|int|float|string $that) : BigRational
|
||||
{
|
||||
$that = BigRational::of($that);
|
||||
|
||||
$numerator = $this->numerator->multipliedBy($that->denominator);
|
||||
$denominator = $this->denominator->multipliedBy($that->numerator);
|
||||
|
||||
return new BigRational($numerator, $denominator, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this number exponentiated to the given value.
|
||||
*
|
||||
* @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
|
||||
*/
|
||||
public function power(int $exponent) : BigRational
|
||||
{
|
||||
if ($exponent === 0) {
|
||||
$one = BigInteger::one();
|
||||
|
||||
return new BigRational($one, $one, false);
|
||||
}
|
||||
|
||||
if ($exponent === 1) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
return new BigRational(
|
||||
$this->numerator->power($exponent),
|
||||
$this->denominator->power($exponent),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the reciprocal of this BigRational.
|
||||
*
|
||||
* The reciprocal has the numerator and denominator swapped.
|
||||
*
|
||||
* @throws DivisionByZeroException If the numerator is zero.
|
||||
*/
|
||||
public function reciprocal() : BigRational
|
||||
{
|
||||
return new BigRational($this->denominator, $this->numerator, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute value of this BigRational.
|
||||
*/
|
||||
public function abs() : BigRational
|
||||
{
|
||||
return new BigRational($this->numerator->abs(), $this->denominator, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the negated value of this BigRational.
|
||||
*/
|
||||
public function negated() : BigRational
|
||||
{
|
||||
return new BigRational($this->numerator->negated(), $this->denominator, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the simplified value of this BigRational.
|
||||
*/
|
||||
public function simplified() : BigRational
|
||||
{
|
||||
$gcd = $this->numerator->gcd($this->denominator);
|
||||
|
||||
$numerator = $this->numerator->quotient($gcd);
|
||||
$denominator = $this->denominator->quotient($gcd);
|
||||
|
||||
return new BigRational($numerator, $denominator, false);
|
||||
}
|
||||
|
||||
public function compareTo(BigNumber|int|float|string $that) : int
|
||||
{
|
||||
return $this->minus($that)->getSign();
|
||||
}
|
||||
|
||||
public function getSign() : int
|
||||
{
|
||||
return $this->numerator->getSign();
|
||||
}
|
||||
|
||||
public function toBigInteger() : BigInteger
|
||||
{
|
||||
$simplified = $this->simplified();
|
||||
|
||||
if (! $simplified->denominator->isEqualTo(1)) {
|
||||
throw new RoundingNecessaryException('This rational number cannot be represented as an integer value without rounding.');
|
||||
}
|
||||
|
||||
return $simplified->numerator;
|
||||
}
|
||||
|
||||
public function toBigDecimal() : BigDecimal
|
||||
{
|
||||
return $this->numerator->toBigDecimal()->exactlyDividedBy($this->denominator);
|
||||
}
|
||||
|
||||
public function toBigRational() : BigRational
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toScale(int $scale, int $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
|
||||
{
|
||||
return $this->numerator->toBigDecimal()->dividedBy($this->denominator, $scale, $roundingMode);
|
||||
}
|
||||
|
||||
public function toInt() : int
|
||||
{
|
||||
return $this->toBigInteger()->toInt();
|
||||
}
|
||||
|
||||
public function toFloat() : float
|
||||
{
|
||||
$simplified = $this->simplified();
|
||||
return $simplified->numerator->toFloat() / $simplified->denominator->toFloat();
|
||||
}
|
||||
|
||||
public function __toString() : string
|
||||
{
|
||||
$numerator = (string) $this->numerator;
|
||||
$denominator = (string) $this->denominator;
|
||||
|
||||
if ($denominator === '1') {
|
||||
return $numerator;
|
||||
}
|
||||
|
||||
return $this->numerator . '/' . $this->denominator;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is required for serializing the object and SHOULD NOT be accessed directly.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @return array{numerator: BigInteger, denominator: BigInteger}
|
||||
*/
|
||||
public function __serialize(): array
|
||||
{
|
||||
return ['numerator' => $this->numerator, 'denominator' => $this->denominator];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is only here to allow unserializing the object and cannot be accessed directly.
|
||||
*
|
||||
* @internal
|
||||
* @psalm-suppress RedundantPropertyInitializationCheck
|
||||
*
|
||||
* @param array{numerator: BigInteger, denominator: BigInteger} $data
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
if (isset($this->numerator)) {
|
||||
throw new \LogicException('__unserialize() is an internal function, it must not be called directly.');
|
||||
}
|
||||
|
||||
$this->numerator = $data['numerator'];
|
||||
$this->denominator = $data['denominator'];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is required by interface Serializable and SHOULD NOT be accessed directly.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function serialize() : string
|
||||
{
|
||||
return $this->numerator . '/' . $this->denominator;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is only here to implement interface Serializable and cannot be accessed directly.
|
||||
*
|
||||
* @internal
|
||||
* @psalm-suppress RedundantPropertyInitializationCheck
|
||||
*
|
||||
* @throws \LogicException
|
||||
*/
|
||||
public function unserialize($value) : void
|
||||
{
|
||||
if (isset($this->numerator)) {
|
||||
throw new \LogicException('unserialize() is an internal function, it must not be called directly.');
|
||||
}
|
||||
|
||||
[$numerator, $denominator] = \explode('/', $value);
|
||||
|
||||
$this->numerator = BigInteger::of($numerator);
|
||||
$this->denominator = BigInteger::of($denominator);
|
||||
}
|
||||
}
|
35
vendor/brick/math/src/Exception/DivisionByZeroException.php
vendored
Normal file
35
vendor/brick/math/src/Exception/DivisionByZeroException.php
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when a division by zero occurs.
|
||||
*/
|
||||
class DivisionByZeroException extends MathException
|
||||
{
|
||||
/**
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function divisionByZero() : DivisionByZeroException
|
||||
{
|
||||
return new self('Division by zero.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function modulusMustNotBeZero() : DivisionByZeroException
|
||||
{
|
||||
return new self('The modulus must not be zero.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function denominatorMustNotBeZero() : DivisionByZeroException
|
||||
{
|
||||
return new self('The denominator of a rational number cannot be zero.');
|
||||
}
|
||||
}
|
23
vendor/brick/math/src/Exception/IntegerOverflowException.php
vendored
Normal file
23
vendor/brick/math/src/Exception/IntegerOverflowException.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Exception;
|
||||
|
||||
use Brick\Math\BigInteger;
|
||||
|
||||
/**
|
||||
* Exception thrown when an integer overflow occurs.
|
||||
*/
|
||||
class IntegerOverflowException extends MathException
|
||||
{
|
||||
/**
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function toIntOverflow(BigInteger $value) : IntegerOverflowException
|
||||
{
|
||||
$message = '%s is out of range %d to %d and cannot be represented as an integer.';
|
||||
|
||||
return new self(\sprintf($message, (string) $value, PHP_INT_MIN, PHP_INT_MAX));
|
||||
}
|
||||
}
|
12
vendor/brick/math/src/Exception/MathException.php
vendored
Normal file
12
vendor/brick/math/src/Exception/MathException.php
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Exception;
|
||||
|
||||
/**
|
||||
* Base class for all math exceptions.
|
||||
*/
|
||||
class MathException extends \Exception
|
||||
{
|
||||
}
|
12
vendor/brick/math/src/Exception/NegativeNumberException.php
vendored
Normal file
12
vendor/brick/math/src/Exception/NegativeNumberException.php
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when attempting to perform an unsupported operation, such as a square root, on a negative number.
|
||||
*/
|
||||
class NegativeNumberException extends MathException
|
||||
{
|
||||
}
|
33
vendor/brick/math/src/Exception/NumberFormatException.php
vendored
Normal file
33
vendor/brick/math/src/Exception/NumberFormatException.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when attempting to create a number from a string with an invalid format.
|
||||
*/
|
||||
class NumberFormatException extends MathException
|
||||
{
|
||||
/**
|
||||
* @param string $char The failing character.
|
||||
*
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function charNotInAlphabet(string $char) : self
|
||||
{
|
||||
$ord = \ord($char);
|
||||
|
||||
if ($ord < 32 || $ord > 126) {
|
||||
$char = \strtoupper(\dechex($ord));
|
||||
|
||||
if ($ord < 10) {
|
||||
$char = '0' . $char;
|
||||
}
|
||||
} else {
|
||||
$char = '"' . $char . '"';
|
||||
}
|
||||
|
||||
return new self(sprintf('Char %s is not a valid character in the given alphabet.', $char));
|
||||
}
|
||||
}
|
19
vendor/brick/math/src/Exception/RoundingNecessaryException.php
vendored
Normal file
19
vendor/brick/math/src/Exception/RoundingNecessaryException.php
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Exception;
|
||||
|
||||
/**
|
||||
* Exception thrown when a number cannot be represented at the requested scale without rounding.
|
||||
*/
|
||||
class RoundingNecessaryException extends MathException
|
||||
{
|
||||
/**
|
||||
* @psalm-pure
|
||||
*/
|
||||
public static function roundingNecessary() : RoundingNecessaryException
|
||||
{
|
||||
return new self('Rounding is necessary to represent the result of the operation at this scale.');
|
||||
}
|
||||
}
|
676
vendor/brick/math/src/Internal/Calculator.php
vendored
Normal file
676
vendor/brick/math/src/Internal/Calculator.php
vendored
Normal file
@ -0,0 +1,676 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Internal;
|
||||
|
||||
use Brick\Math\Exception\RoundingNecessaryException;
|
||||
use Brick\Math\RoundingMode;
|
||||
|
||||
/**
|
||||
* Performs basic operations on arbitrary size integers.
|
||||
*
|
||||
* Unless otherwise specified, all parameters must be validated as non-empty strings of digits,
|
||||
* without leading zero, and with an optional leading minus sign if the number is not zero.
|
||||
*
|
||||
* Any other parameter format will lead to undefined behaviour.
|
||||
* All methods must return strings respecting this format, unless specified otherwise.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @psalm-immutable
|
||||
*/
|
||||
abstract class Calculator
|
||||
{
|
||||
/**
|
||||
* The maximum exponent value allowed for the pow() method.
|
||||
*/
|
||||
public const MAX_POWER = 1000000;
|
||||
|
||||
/**
|
||||
* The alphabet for converting from and to base 2 to 36, lowercase.
|
||||
*/
|
||||
public const ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyz';
|
||||
|
||||
/**
|
||||
* The Calculator instance in use.
|
||||
*/
|
||||
private static ?Calculator $instance = null;
|
||||
|
||||
/**
|
||||
* Sets the Calculator instance to use.
|
||||
*
|
||||
* An instance is typically set only in unit tests: the autodetect is usually the best option.
|
||||
*
|
||||
* @param Calculator|null $calculator The calculator instance, or NULL to revert to autodetect.
|
||||
*/
|
||||
final public static function set(?Calculator $calculator) : void
|
||||
{
|
||||
self::$instance = $calculator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Calculator instance to use.
|
||||
*
|
||||
* If none has been explicitly set, the fastest available implementation will be returned.
|
||||
*
|
||||
* @psalm-pure
|
||||
* @psalm-suppress ImpureStaticProperty
|
||||
*/
|
||||
final public static function get() : Calculator
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
/** @psalm-suppress ImpureMethodCall */
|
||||
self::$instance = self::detect();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the fastest available Calculator implementation.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private static function detect() : Calculator
|
||||
{
|
||||
if (\extension_loaded('gmp')) {
|
||||
return new Calculator\GmpCalculator();
|
||||
}
|
||||
|
||||
if (\extension_loaded('bcmath')) {
|
||||
return new Calculator\BcMathCalculator();
|
||||
}
|
||||
|
||||
return new Calculator\NativeCalculator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the sign & digits of the operands.
|
||||
*
|
||||
* @return array{bool, bool, string, string} Whether $a and $b are negative, followed by their digits.
|
||||
*/
|
||||
final protected function init(string $a, string $b) : array
|
||||
{
|
||||
return [
|
||||
$aNeg = ($a[0] === '-'),
|
||||
$bNeg = ($b[0] === '-'),
|
||||
|
||||
$aNeg ? \substr($a, 1) : $a,
|
||||
$bNeg ? \substr($b, 1) : $b,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute value of a number.
|
||||
*/
|
||||
final public function abs(string $n) : string
|
||||
{
|
||||
return ($n[0] === '-') ? \substr($n, 1) : $n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Negates a number.
|
||||
*/
|
||||
final public function neg(string $n) : string
|
||||
{
|
||||
if ($n === '0') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
if ($n[0] === '-') {
|
||||
return \substr($n, 1);
|
||||
}
|
||||
|
||||
return '-' . $n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two numbers.
|
||||
*
|
||||
* @return int [-1, 0, 1] If the first number is less than, equal to, or greater than the second number.
|
||||
*/
|
||||
final public function cmp(string $a, string $b) : int
|
||||
{
|
||||
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
|
||||
|
||||
if ($aNeg && ! $bNeg) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ($bNeg && ! $aNeg) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
$aLen = \strlen($aDig);
|
||||
$bLen = \strlen($bDig);
|
||||
|
||||
if ($aLen < $bLen) {
|
||||
$result = -1;
|
||||
} elseif ($aLen > $bLen) {
|
||||
$result = 1;
|
||||
} else {
|
||||
$result = $aDig <=> $bDig;
|
||||
}
|
||||
|
||||
return $aNeg ? -$result : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds two numbers.
|
||||
*/
|
||||
abstract public function add(string $a, string $b) : string;
|
||||
|
||||
/**
|
||||
* Subtracts two numbers.
|
||||
*/
|
||||
abstract public function sub(string $a, string $b) : string;
|
||||
|
||||
/**
|
||||
* Multiplies two numbers.
|
||||
*/
|
||||
abstract public function mul(string $a, string $b) : string;
|
||||
|
||||
/**
|
||||
* Returns the quotient of the division of two numbers.
|
||||
*
|
||||
* @param string $a The dividend.
|
||||
* @param string $b The divisor, must not be zero.
|
||||
*
|
||||
* @return string The quotient.
|
||||
*/
|
||||
abstract public function divQ(string $a, string $b) : string;
|
||||
|
||||
/**
|
||||
* Returns the remainder of the division of two numbers.
|
||||
*
|
||||
* @param string $a The dividend.
|
||||
* @param string $b The divisor, must not be zero.
|
||||
*
|
||||
* @return string The remainder.
|
||||
*/
|
||||
abstract public function divR(string $a, string $b) : string;
|
||||
|
||||
/**
|
||||
* Returns the quotient and remainder of the division of two numbers.
|
||||
*
|
||||
* @param string $a The dividend.
|
||||
* @param string $b The divisor, must not be zero.
|
||||
*
|
||||
* @return array{string, string} An array containing the quotient and remainder.
|
||||
*/
|
||||
abstract public function divQR(string $a, string $b) : array;
|
||||
|
||||
/**
|
||||
* Exponentiates a number.
|
||||
*
|
||||
* @param string $a The base number.
|
||||
* @param int $e The exponent, validated as an integer between 0 and MAX_POWER.
|
||||
*
|
||||
* @return string The power.
|
||||
*/
|
||||
abstract public function pow(string $a, int $e) : string;
|
||||
|
||||
/**
|
||||
* @param string $b The modulus; must not be zero.
|
||||
*/
|
||||
public function mod(string $a, string $b) : string
|
||||
{
|
||||
return $this->divR($this->add($this->divR($a, $b), $b), $b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the modular multiplicative inverse of $x modulo $m.
|
||||
*
|
||||
* If $x has no multiplicative inverse mod m, this method must return null.
|
||||
*
|
||||
* This method can be overridden by the concrete implementation if the underlying library has built-in support.
|
||||
*
|
||||
* @param string $m The modulus; must not be negative or zero.
|
||||
*/
|
||||
public function modInverse(string $x, string $m) : ?string
|
||||
{
|
||||
if ($m === '1') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
$modVal = $x;
|
||||
|
||||
if ($x[0] === '-' || ($this->cmp($this->abs($x), $m) >= 0)) {
|
||||
$modVal = $this->mod($x, $m);
|
||||
}
|
||||
|
||||
[$g, $x] = $this->gcdExtended($modVal, $m);
|
||||
|
||||
if ($g !== '1') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->mod($this->add($this->mod($x, $m), $m), $m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Raises a number into power with modulo.
|
||||
*
|
||||
* @param string $base The base number; must be positive or zero.
|
||||
* @param string $exp The exponent; must be positive or zero.
|
||||
* @param string $mod The modulus; must be strictly positive.
|
||||
*/
|
||||
abstract public function modPow(string $base, string $exp, string $mod) : string;
|
||||
|
||||
/**
|
||||
* Returns the greatest common divisor of the two numbers.
|
||||
*
|
||||
* This method can be overridden by the concrete implementation if the underlying library
|
||||
* has built-in support for GCD calculations.
|
||||
*
|
||||
* @return string The GCD, always positive, or zero if both arguments are zero.
|
||||
*/
|
||||
public function gcd(string $a, string $b) : string
|
||||
{
|
||||
if ($a === '0') {
|
||||
return $this->abs($b);
|
||||
}
|
||||
|
||||
if ($b === '0') {
|
||||
return $this->abs($a);
|
||||
}
|
||||
|
||||
return $this->gcd($b, $this->divR($a, $b));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{string, string, string} GCD, X, Y
|
||||
*/
|
||||
private function gcdExtended(string $a, string $b) : array
|
||||
{
|
||||
if ($a === '0') {
|
||||
return [$b, '0', '1'];
|
||||
}
|
||||
|
||||
[$gcd, $x1, $y1] = $this->gcdExtended($this->mod($b, $a), $a);
|
||||
|
||||
$x = $this->sub($y1, $this->mul($this->divQ($b, $a), $x1));
|
||||
$y = $x1;
|
||||
|
||||
return [$gcd, $x, $y];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the square root of the given number, rounded down.
|
||||
*
|
||||
* The result is the largest x such that x² ≤ n.
|
||||
* The input MUST NOT be negative.
|
||||
*/
|
||||
abstract public function sqrt(string $n) : string;
|
||||
|
||||
/**
|
||||
* Converts a number from an arbitrary base.
|
||||
*
|
||||
* This method can be overridden by the concrete implementation if the underlying library
|
||||
* has built-in support for base conversion.
|
||||
*
|
||||
* @param string $number The number, positive or zero, non-empty, case-insensitively validated for the given base.
|
||||
* @param int $base The base of the number, validated from 2 to 36.
|
||||
*
|
||||
* @return string The converted number, following the Calculator conventions.
|
||||
*/
|
||||
public function fromBase(string $number, int $base) : string
|
||||
{
|
||||
return $this->fromArbitraryBase(\strtolower($number), self::ALPHABET, $base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a number to an arbitrary base.
|
||||
*
|
||||
* This method can be overridden by the concrete implementation if the underlying library
|
||||
* has built-in support for base conversion.
|
||||
*
|
||||
* @param string $number The number to convert, following the Calculator conventions.
|
||||
* @param int $base The base to convert to, validated from 2 to 36.
|
||||
*
|
||||
* @return string The converted number, lowercase.
|
||||
*/
|
||||
public function toBase(string $number, int $base) : string
|
||||
{
|
||||
$negative = ($number[0] === '-');
|
||||
|
||||
if ($negative) {
|
||||
$number = \substr($number, 1);
|
||||
}
|
||||
|
||||
$number = $this->toArbitraryBase($number, self::ALPHABET, $base);
|
||||
|
||||
if ($negative) {
|
||||
return '-' . $number;
|
||||
}
|
||||
|
||||
return $number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a non-negative number in an arbitrary base using a custom alphabet, to base 10.
|
||||
*
|
||||
* @param string $number The number to convert, validated as a non-empty string,
|
||||
* containing only chars in the given alphabet/base.
|
||||
* @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum.
|
||||
* @param int $base The base of the number, validated from 2 to alphabet length.
|
||||
*
|
||||
* @return string The number in base 10, following the Calculator conventions.
|
||||
*/
|
||||
final public function fromArbitraryBase(string $number, string $alphabet, int $base) : string
|
||||
{
|
||||
// remove leading "zeros"
|
||||
$number = \ltrim($number, $alphabet[0]);
|
||||
|
||||
if ($number === '') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
// optimize for "one"
|
||||
if ($number === $alphabet[1]) {
|
||||
return '1';
|
||||
}
|
||||
|
||||
$result = '0';
|
||||
$power = '1';
|
||||
|
||||
$base = (string) $base;
|
||||
|
||||
for ($i = \strlen($number) - 1; $i >= 0; $i--) {
|
||||
$index = \strpos($alphabet, $number[$i]);
|
||||
|
||||
if ($index !== 0) {
|
||||
$result = $this->add($result, ($index === 1)
|
||||
? $power
|
||||
: $this->mul($power, (string) $index)
|
||||
);
|
||||
}
|
||||
|
||||
if ($i !== 0) {
|
||||
$power = $this->mul($power, $base);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a non-negative number to an arbitrary base using a custom alphabet.
|
||||
*
|
||||
* @param string $number The number to convert, positive or zero, following the Calculator conventions.
|
||||
* @param string $alphabet The alphabet that contains every digit, validated as 2 chars minimum.
|
||||
* @param int $base The base to convert to, validated from 2 to alphabet length.
|
||||
*
|
||||
* @return string The converted number in the given alphabet.
|
||||
*/
|
||||
final public function toArbitraryBase(string $number, string $alphabet, int $base) : string
|
||||
{
|
||||
if ($number === '0') {
|
||||
return $alphabet[0];
|
||||
}
|
||||
|
||||
$base = (string) $base;
|
||||
$result = '';
|
||||
|
||||
while ($number !== '0') {
|
||||
[$number, $remainder] = $this->divQR($number, $base);
|
||||
$remainder = (int) $remainder;
|
||||
|
||||
$result .= $alphabet[$remainder];
|
||||
}
|
||||
|
||||
return \strrev($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a rounded division.
|
||||
*
|
||||
* Rounding is performed when the remainder of the division is not zero.
|
||||
*
|
||||
* @param string $a The dividend.
|
||||
* @param string $b The divisor, must not be zero.
|
||||
* @param int $roundingMode The rounding mode.
|
||||
*
|
||||
* @throws \InvalidArgumentException If the rounding mode is invalid.
|
||||
* @throws RoundingNecessaryException If RoundingMode::UNNECESSARY is provided but rounding is necessary.
|
||||
*
|
||||
* @psalm-suppress ImpureFunctionCall
|
||||
*/
|
||||
final public function divRound(string $a, string $b, int $roundingMode) : string
|
||||
{
|
||||
[$quotient, $remainder] = $this->divQR($a, $b);
|
||||
|
||||
$hasDiscardedFraction = ($remainder !== '0');
|
||||
$isPositiveOrZero = ($a[0] === '-') === ($b[0] === '-');
|
||||
|
||||
$discardedFractionSign = function() use ($remainder, $b) : int {
|
||||
$r = $this->abs($this->mul($remainder, '2'));
|
||||
$b = $this->abs($b);
|
||||
|
||||
return $this->cmp($r, $b);
|
||||
};
|
||||
|
||||
$increment = false;
|
||||
|
||||
switch ($roundingMode) {
|
||||
case RoundingMode::UNNECESSARY:
|
||||
if ($hasDiscardedFraction) {
|
||||
throw RoundingNecessaryException::roundingNecessary();
|
||||
}
|
||||
break;
|
||||
|
||||
case RoundingMode::UP:
|
||||
$increment = $hasDiscardedFraction;
|
||||
break;
|
||||
|
||||
case RoundingMode::DOWN:
|
||||
break;
|
||||
|
||||
case RoundingMode::CEILING:
|
||||
$increment = $hasDiscardedFraction && $isPositiveOrZero;
|
||||
break;
|
||||
|
||||
case RoundingMode::FLOOR:
|
||||
$increment = $hasDiscardedFraction && ! $isPositiveOrZero;
|
||||
break;
|
||||
|
||||
case RoundingMode::HALF_UP:
|
||||
$increment = $discardedFractionSign() >= 0;
|
||||
break;
|
||||
|
||||
case RoundingMode::HALF_DOWN:
|
||||
$increment = $discardedFractionSign() > 0;
|
||||
break;
|
||||
|
||||
case RoundingMode::HALF_CEILING:
|
||||
$increment = $isPositiveOrZero ? $discardedFractionSign() >= 0 : $discardedFractionSign() > 0;
|
||||
break;
|
||||
|
||||
case RoundingMode::HALF_FLOOR:
|
||||
$increment = $isPositiveOrZero ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0;
|
||||
break;
|
||||
|
||||
case RoundingMode::HALF_EVEN:
|
||||
$lastDigit = (int) $quotient[-1];
|
||||
$lastDigitIsEven = ($lastDigit % 2 === 0);
|
||||
$increment = $lastDigitIsEven ? $discardedFractionSign() > 0 : $discardedFractionSign() >= 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \InvalidArgumentException('Invalid rounding mode.');
|
||||
}
|
||||
|
||||
if ($increment) {
|
||||
return $this->add($quotient, $isPositiveOrZero ? '1' : '-1');
|
||||
}
|
||||
|
||||
return $quotient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates bitwise AND of two numbers.
|
||||
*
|
||||
* This method can be overridden by the concrete implementation if the underlying library
|
||||
* has built-in support for bitwise operations.
|
||||
*/
|
||||
public function and(string $a, string $b) : string
|
||||
{
|
||||
return $this->bitwise('and', $a, $b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates bitwise OR of two numbers.
|
||||
*
|
||||
* This method can be overridden by the concrete implementation if the underlying library
|
||||
* has built-in support for bitwise operations.
|
||||
*/
|
||||
public function or(string $a, string $b) : string
|
||||
{
|
||||
return $this->bitwise('or', $a, $b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates bitwise XOR of two numbers.
|
||||
*
|
||||
* This method can be overridden by the concrete implementation if the underlying library
|
||||
* has built-in support for bitwise operations.
|
||||
*/
|
||||
public function xor(string $a, string $b) : string
|
||||
{
|
||||
return $this->bitwise('xor', $a, $b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a bitwise operation on a decimal number.
|
||||
*
|
||||
* @param 'and'|'or'|'xor' $operator The operator to use.
|
||||
* @param string $a The left operand.
|
||||
* @param string $b The right operand.
|
||||
*/
|
||||
private function bitwise(string $operator, string $a, string $b) : string
|
||||
{
|
||||
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
|
||||
|
||||
$aBin = $this->toBinary($aDig);
|
||||
$bBin = $this->toBinary($bDig);
|
||||
|
||||
$aLen = \strlen($aBin);
|
||||
$bLen = \strlen($bBin);
|
||||
|
||||
if ($aLen > $bLen) {
|
||||
$bBin = \str_repeat("\x00", $aLen - $bLen) . $bBin;
|
||||
} elseif ($bLen > $aLen) {
|
||||
$aBin = \str_repeat("\x00", $bLen - $aLen) . $aBin;
|
||||
}
|
||||
|
||||
if ($aNeg) {
|
||||
$aBin = $this->twosComplement($aBin);
|
||||
}
|
||||
if ($bNeg) {
|
||||
$bBin = $this->twosComplement($bBin);
|
||||
}
|
||||
|
||||
switch ($operator) {
|
||||
case 'and':
|
||||
$value = $aBin & $bBin;
|
||||
$negative = ($aNeg and $bNeg);
|
||||
break;
|
||||
|
||||
case 'or':
|
||||
$value = $aBin | $bBin;
|
||||
$negative = ($aNeg or $bNeg);
|
||||
break;
|
||||
|
||||
case 'xor':
|
||||
$value = $aBin ^ $bBin;
|
||||
$negative = ($aNeg xor $bNeg);
|
||||
break;
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
default:
|
||||
throw new \InvalidArgumentException('Invalid bitwise operator.');
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
|
||||
if ($negative) {
|
||||
$value = $this->twosComplement($value);
|
||||
}
|
||||
|
||||
$result = $this->toDecimal($value);
|
||||
|
||||
return $negative ? $this->neg($result) : $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $number A positive, binary number.
|
||||
*/
|
||||
private function twosComplement(string $number) : string
|
||||
{
|
||||
$xor = \str_repeat("\xff", \strlen($number));
|
||||
|
||||
$number ^= $xor;
|
||||
|
||||
for ($i = \strlen($number) - 1; $i >= 0; $i--) {
|
||||
$byte = \ord($number[$i]);
|
||||
|
||||
if (++$byte !== 256) {
|
||||
$number[$i] = \chr($byte);
|
||||
break;
|
||||
}
|
||||
|
||||
$number[$i] = "\x00";
|
||||
|
||||
if ($i === 0) {
|
||||
$number = "\x01" . $number;
|
||||
}
|
||||
}
|
||||
|
||||
return $number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a decimal number to a binary string.
|
||||
*
|
||||
* @param string $number The number to convert, positive or zero, only digits.
|
||||
*/
|
||||
private function toBinary(string $number) : string
|
||||
{
|
||||
$result = '';
|
||||
|
||||
while ($number !== '0') {
|
||||
[$number, $remainder] = $this->divQR($number, '256');
|
||||
$result .= \chr((int) $remainder);
|
||||
}
|
||||
|
||||
return \strrev($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the positive decimal representation of a binary number.
|
||||
*
|
||||
* @param string $bytes The bytes representing the number.
|
||||
*/
|
||||
private function toDecimal(string $bytes) : string
|
||||
{
|
||||
$result = '0';
|
||||
$power = '1';
|
||||
|
||||
for ($i = \strlen($bytes) - 1; $i >= 0; $i--) {
|
||||
$index = \ord($bytes[$i]);
|
||||
|
||||
if ($index !== 0) {
|
||||
$result = $this->add($result, ($index === 1)
|
||||
? $power
|
||||
: $this->mul($power, (string) $index)
|
||||
);
|
||||
}
|
||||
|
||||
if ($i !== 0) {
|
||||
$power = $this->mul($power, '256');
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
75
vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php
vendored
Normal file
75
vendor/brick/math/src/Internal/Calculator/BcMathCalculator.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Internal\Calculator;
|
||||
|
||||
use Brick\Math\Internal\Calculator;
|
||||
|
||||
/**
|
||||
* Calculator implementation built around the bcmath library.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @psalm-immutable
|
||||
*/
|
||||
class BcMathCalculator extends Calculator
|
||||
{
|
||||
public function add(string $a, string $b) : string
|
||||
{
|
||||
return \bcadd($a, $b, 0);
|
||||
}
|
||||
|
||||
public function sub(string $a, string $b) : string
|
||||
{
|
||||
return \bcsub($a, $b, 0);
|
||||
}
|
||||
|
||||
public function mul(string $a, string $b) : string
|
||||
{
|
||||
return \bcmul($a, $b, 0);
|
||||
}
|
||||
|
||||
public function divQ(string $a, string $b) : string
|
||||
{
|
||||
return \bcdiv($a, $b, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-suppress InvalidNullableReturnType
|
||||
* @psalm-suppress NullableReturnStatement
|
||||
*/
|
||||
public function divR(string $a, string $b) : string
|
||||
{
|
||||
return \bcmod($a, $b, 0);
|
||||
}
|
||||
|
||||
public function divQR(string $a, string $b) : array
|
||||
{
|
||||
$q = \bcdiv($a, $b, 0);
|
||||
$r = \bcmod($a, $b, 0);
|
||||
|
||||
assert($r !== null);
|
||||
|
||||
return [$q, $r];
|
||||
}
|
||||
|
||||
public function pow(string $a, int $e) : string
|
||||
{
|
||||
return \bcpow($a, (string) $e, 0);
|
||||
}
|
||||
|
||||
public function modPow(string $base, string $exp, string $mod) : string
|
||||
{
|
||||
return \bcpowmod($base, $exp, $mod, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-suppress InvalidNullableReturnType
|
||||
* @psalm-suppress NullableReturnStatement
|
||||
*/
|
||||
public function sqrt(string $n) : string
|
||||
{
|
||||
return \bcsqrt($n, 0);
|
||||
}
|
||||
}
|
108
vendor/brick/math/src/Internal/Calculator/GmpCalculator.php
vendored
Normal file
108
vendor/brick/math/src/Internal/Calculator/GmpCalculator.php
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Internal\Calculator;
|
||||
|
||||
use Brick\Math\Internal\Calculator;
|
||||
|
||||
/**
|
||||
* Calculator implementation built around the GMP library.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @psalm-immutable
|
||||
*/
|
||||
class GmpCalculator extends Calculator
|
||||
{
|
||||
public function add(string $a, string $b) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_add($a, $b));
|
||||
}
|
||||
|
||||
public function sub(string $a, string $b) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_sub($a, $b));
|
||||
}
|
||||
|
||||
public function mul(string $a, string $b) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_mul($a, $b));
|
||||
}
|
||||
|
||||
public function divQ(string $a, string $b) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_div_q($a, $b));
|
||||
}
|
||||
|
||||
public function divR(string $a, string $b) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_div_r($a, $b));
|
||||
}
|
||||
|
||||
public function divQR(string $a, string $b) : array
|
||||
{
|
||||
[$q, $r] = \gmp_div_qr($a, $b);
|
||||
|
||||
return [
|
||||
\gmp_strval($q),
|
||||
\gmp_strval($r)
|
||||
];
|
||||
}
|
||||
|
||||
public function pow(string $a, int $e) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_pow($a, $e));
|
||||
}
|
||||
|
||||
public function modInverse(string $x, string $m) : ?string
|
||||
{
|
||||
$result = \gmp_invert($x, $m);
|
||||
|
||||
if ($result === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return \gmp_strval($result);
|
||||
}
|
||||
|
||||
public function modPow(string $base, string $exp, string $mod) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_powm($base, $exp, $mod));
|
||||
}
|
||||
|
||||
public function gcd(string $a, string $b) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_gcd($a, $b));
|
||||
}
|
||||
|
||||
public function fromBase(string $number, int $base) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_init($number, $base));
|
||||
}
|
||||
|
||||
public function toBase(string $number, int $base) : string
|
||||
{
|
||||
return \gmp_strval($number, $base);
|
||||
}
|
||||
|
||||
public function and(string $a, string $b) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_and($a, $b));
|
||||
}
|
||||
|
||||
public function or(string $a, string $b) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_or($a, $b));
|
||||
}
|
||||
|
||||
public function xor(string $a, string $b) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_xor($a, $b));
|
||||
}
|
||||
|
||||
public function sqrt(string $n) : string
|
||||
{
|
||||
return \gmp_strval(\gmp_sqrt($n));
|
||||
}
|
||||
}
|
581
vendor/brick/math/src/Internal/Calculator/NativeCalculator.php
vendored
Normal file
581
vendor/brick/math/src/Internal/Calculator/NativeCalculator.php
vendored
Normal file
@ -0,0 +1,581 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math\Internal\Calculator;
|
||||
|
||||
use Brick\Math\Internal\Calculator;
|
||||
|
||||
/**
|
||||
* Calculator implementation using only native PHP code.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @psalm-immutable
|
||||
*/
|
||||
class NativeCalculator extends Calculator
|
||||
{
|
||||
/**
|
||||
* The max number of digits the platform can natively add, subtract, multiply or divide without overflow.
|
||||
* For multiplication, this represents the max sum of the lengths of both operands.
|
||||
*
|
||||
* In addition, it is assumed that an extra digit can hold a carry (1) without overflowing.
|
||||
* Example: 32-bit: max number 1,999,999,999 (9 digits + carry)
|
||||
* 64-bit: max number 1,999,999,999,999,999,999 (18 digits + carry)
|
||||
*/
|
||||
private int $maxDigits;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
switch (PHP_INT_SIZE) {
|
||||
case 4:
|
||||
$this->maxDigits = 9;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
$this->maxDigits = 18;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new \RuntimeException('The platform is not 32-bit or 64-bit as expected.');
|
||||
}
|
||||
}
|
||||
|
||||
public function add(string $a, string $b) : string
|
||||
{
|
||||
/**
|
||||
* @psalm-var numeric-string $a
|
||||
* @psalm-var numeric-string $b
|
||||
*/
|
||||
$result = $a + $b;
|
||||
|
||||
if (is_int($result)) {
|
||||
return (string) $result;
|
||||
}
|
||||
|
||||
if ($a === '0') {
|
||||
return $b;
|
||||
}
|
||||
|
||||
if ($b === '0') {
|
||||
return $a;
|
||||
}
|
||||
|
||||
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
|
||||
|
||||
$result = $aNeg === $bNeg ? $this->doAdd($aDig, $bDig) : $this->doSub($aDig, $bDig);
|
||||
|
||||
if ($aNeg) {
|
||||
$result = $this->neg($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function sub(string $a, string $b) : string
|
||||
{
|
||||
return $this->add($a, $this->neg($b));
|
||||
}
|
||||
|
||||
public function mul(string $a, string $b) : string
|
||||
{
|
||||
/**
|
||||
* @psalm-var numeric-string $a
|
||||
* @psalm-var numeric-string $b
|
||||
*/
|
||||
$result = $a * $b;
|
||||
|
||||
if (is_int($result)) {
|
||||
return (string) $result;
|
||||
}
|
||||
|
||||
if ($a === '0' || $b === '0') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
if ($a === '1') {
|
||||
return $b;
|
||||
}
|
||||
|
||||
if ($b === '1') {
|
||||
return $a;
|
||||
}
|
||||
|
||||
if ($a === '-1') {
|
||||
return $this->neg($b);
|
||||
}
|
||||
|
||||
if ($b === '-1') {
|
||||
return $this->neg($a);
|
||||
}
|
||||
|
||||
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
|
||||
|
||||
$result = $this->doMul($aDig, $bDig);
|
||||
|
||||
if ($aNeg !== $bNeg) {
|
||||
$result = $this->neg($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function divQ(string $a, string $b) : string
|
||||
{
|
||||
return $this->divQR($a, $b)[0];
|
||||
}
|
||||
|
||||
public function divR(string $a, string $b): string
|
||||
{
|
||||
return $this->divQR($a, $b)[1];
|
||||
}
|
||||
|
||||
public function divQR(string $a, string $b) : array
|
||||
{
|
||||
if ($a === '0') {
|
||||
return ['0', '0'];
|
||||
}
|
||||
|
||||
if ($a === $b) {
|
||||
return ['1', '0'];
|
||||
}
|
||||
|
||||
if ($b === '1') {
|
||||
return [$a, '0'];
|
||||
}
|
||||
|
||||
if ($b === '-1') {
|
||||
return [$this->neg($a), '0'];
|
||||
}
|
||||
|
||||
/** @psalm-var numeric-string $a */
|
||||
$na = $a * 1; // cast to number
|
||||
|
||||
if (is_int($na)) {
|
||||
/** @psalm-var numeric-string $b */
|
||||
$nb = $b * 1;
|
||||
|
||||
if (is_int($nb)) {
|
||||
// the only division that may overflow is PHP_INT_MIN / -1,
|
||||
// which cannot happen here as we've already handled a divisor of -1 above.
|
||||
$r = $na % $nb;
|
||||
$q = ($na - $r) / $nb;
|
||||
|
||||
assert(is_int($q));
|
||||
|
||||
return [
|
||||
(string) $q,
|
||||
(string) $r
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
[$aNeg, $bNeg, $aDig, $bDig] = $this->init($a, $b);
|
||||
|
||||
[$q, $r] = $this->doDiv($aDig, $bDig);
|
||||
|
||||
if ($aNeg !== $bNeg) {
|
||||
$q = $this->neg($q);
|
||||
}
|
||||
|
||||
if ($aNeg) {
|
||||
$r = $this->neg($r);
|
||||
}
|
||||
|
||||
return [$q, $r];
|
||||
}
|
||||
|
||||
public function pow(string $a, int $e) : string
|
||||
{
|
||||
if ($e === 0) {
|
||||
return '1';
|
||||
}
|
||||
|
||||
if ($e === 1) {
|
||||
return $a;
|
||||
}
|
||||
|
||||
$odd = $e % 2;
|
||||
$e -= $odd;
|
||||
|
||||
$aa = $this->mul($a, $a);
|
||||
|
||||
/** @psalm-suppress PossiblyInvalidArgument We're sure that $e / 2 is an int now */
|
||||
$result = $this->pow($aa, $e / 2);
|
||||
|
||||
if ($odd === 1) {
|
||||
$result = $this->mul($result, $a);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Algorithm from: https://www.geeksforgeeks.org/modular-exponentiation-power-in-modular-arithmetic/
|
||||
*/
|
||||
public function modPow(string $base, string $exp, string $mod) : string
|
||||
{
|
||||
// special case: the algorithm below fails with 0 power 0 mod 1 (returns 1 instead of 0)
|
||||
if ($base === '0' && $exp === '0' && $mod === '1') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
// special case: the algorithm below fails with power 0 mod 1 (returns 1 instead of 0)
|
||||
if ($exp === '0' && $mod === '1') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
$x = $base;
|
||||
|
||||
$res = '1';
|
||||
|
||||
// numbers are positive, so we can use remainder instead of modulo
|
||||
$x = $this->divR($x, $mod);
|
||||
|
||||
while ($exp !== '0') {
|
||||
if (in_array($exp[-1], ['1', '3', '5', '7', '9'])) { // odd
|
||||
$res = $this->divR($this->mul($res, $x), $mod);
|
||||
}
|
||||
|
||||
$exp = $this->divQ($exp, '2');
|
||||
$x = $this->divR($this->mul($x, $x), $mod);
|
||||
}
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapted from https://cp-algorithms.com/num_methods/roots_newton.html
|
||||
*/
|
||||
public function sqrt(string $n) : string
|
||||
{
|
||||
if ($n === '0') {
|
||||
return '0';
|
||||
}
|
||||
|
||||
// initial approximation
|
||||
$x = \str_repeat('9', \intdiv(\strlen($n), 2) ?: 1);
|
||||
|
||||
$decreased = false;
|
||||
|
||||
for (;;) {
|
||||
$nx = $this->divQ($this->add($x, $this->divQ($n, $x)), '2');
|
||||
|
||||
if ($x === $nx || $this->cmp($nx, $x) > 0 && $decreased) {
|
||||
break;
|
||||
}
|
||||
|
||||
$decreased = $this->cmp($nx, $x) < 0;
|
||||
$x = $nx;
|
||||
}
|
||||
|
||||
return $x;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the addition of two non-signed large integers.
|
||||
*/
|
||||
private function doAdd(string $a, string $b) : string
|
||||
{
|
||||
[$a, $b, $length] = $this->pad($a, $b);
|
||||
|
||||
$carry = 0;
|
||||
$result = '';
|
||||
|
||||
for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) {
|
||||
$blockLength = $this->maxDigits;
|
||||
|
||||
if ($i < 0) {
|
||||
$blockLength += $i;
|
||||
/** @psalm-suppress LoopInvalidation */
|
||||
$i = 0;
|
||||
}
|
||||
|
||||
/** @psalm-var numeric-string $blockA */
|
||||
$blockA = \substr($a, $i, $blockLength);
|
||||
|
||||
/** @psalm-var numeric-string $blockB */
|
||||
$blockB = \substr($b, $i, $blockLength);
|
||||
|
||||
$sum = (string) ($blockA + $blockB + $carry);
|
||||
$sumLength = \strlen($sum);
|
||||
|
||||
if ($sumLength > $blockLength) {
|
||||
$sum = \substr($sum, 1);
|
||||
$carry = 1;
|
||||
} else {
|
||||
if ($sumLength < $blockLength) {
|
||||
$sum = \str_repeat('0', $blockLength - $sumLength) . $sum;
|
||||
}
|
||||
$carry = 0;
|
||||
}
|
||||
|
||||
$result = $sum . $result;
|
||||
|
||||
if ($i === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($carry === 1) {
|
||||
$result = '1' . $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the subtraction of two non-signed large integers.
|
||||
*/
|
||||
private function doSub(string $a, string $b) : string
|
||||
{
|
||||
if ($a === $b) {
|
||||
return '0';
|
||||
}
|
||||
|
||||
// Ensure that we always subtract to a positive result: biggest minus smallest.
|
||||
$cmp = $this->doCmp($a, $b);
|
||||
|
||||
$invert = ($cmp === -1);
|
||||
|
||||
if ($invert) {
|
||||
$c = $a;
|
||||
$a = $b;
|
||||
$b = $c;
|
||||
}
|
||||
|
||||
[$a, $b, $length] = $this->pad($a, $b);
|
||||
|
||||
$carry = 0;
|
||||
$result = '';
|
||||
|
||||
$complement = 10 ** $this->maxDigits;
|
||||
|
||||
for ($i = $length - $this->maxDigits;; $i -= $this->maxDigits) {
|
||||
$blockLength = $this->maxDigits;
|
||||
|
||||
if ($i < 0) {
|
||||
$blockLength += $i;
|
||||
/** @psalm-suppress LoopInvalidation */
|
||||
$i = 0;
|
||||
}
|
||||
|
||||
/** @psalm-var numeric-string $blockA */
|
||||
$blockA = \substr($a, $i, $blockLength);
|
||||
|
||||
/** @psalm-var numeric-string $blockB */
|
||||
$blockB = \substr($b, $i, $blockLength);
|
||||
|
||||
$sum = $blockA - $blockB - $carry;
|
||||
|
||||
if ($sum < 0) {
|
||||
$sum += $complement;
|
||||
$carry = 1;
|
||||
} else {
|
||||
$carry = 0;
|
||||
}
|
||||
|
||||
$sum = (string) $sum;
|
||||
$sumLength = \strlen($sum);
|
||||
|
||||
if ($sumLength < $blockLength) {
|
||||
$sum = \str_repeat('0', $blockLength - $sumLength) . $sum;
|
||||
}
|
||||
|
||||
$result = $sum . $result;
|
||||
|
||||
if ($i === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Carry cannot be 1 when the loop ends, as a > b
|
||||
assert($carry === 0);
|
||||
|
||||
$result = \ltrim($result, '0');
|
||||
|
||||
if ($invert) {
|
||||
$result = $this->neg($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the multiplication of two non-signed large integers.
|
||||
*/
|
||||
private function doMul(string $a, string $b) : string
|
||||
{
|
||||
$x = \strlen($a);
|
||||
$y = \strlen($b);
|
||||
|
||||
$maxDigits = \intdiv($this->maxDigits, 2);
|
||||
$complement = 10 ** $maxDigits;
|
||||
|
||||
$result = '0';
|
||||
|
||||
for ($i = $x - $maxDigits;; $i -= $maxDigits) {
|
||||
$blockALength = $maxDigits;
|
||||
|
||||
if ($i < 0) {
|
||||
$blockALength += $i;
|
||||
/** @psalm-suppress LoopInvalidation */
|
||||
$i = 0;
|
||||
}
|
||||
|
||||
$blockA = (int) \substr($a, $i, $blockALength);
|
||||
|
||||
$line = '';
|
||||
$carry = 0;
|
||||
|
||||
for ($j = $y - $maxDigits;; $j -= $maxDigits) {
|
||||
$blockBLength = $maxDigits;
|
||||
|
||||
if ($j < 0) {
|
||||
$blockBLength += $j;
|
||||
/** @psalm-suppress LoopInvalidation */
|
||||
$j = 0;
|
||||
}
|
||||
|
||||
$blockB = (int) \substr($b, $j, $blockBLength);
|
||||
|
||||
$mul = $blockA * $blockB + $carry;
|
||||
$value = $mul % $complement;
|
||||
$carry = ($mul - $value) / $complement;
|
||||
|
||||
$value = (string) $value;
|
||||
$value = \str_pad($value, $maxDigits, '0', STR_PAD_LEFT);
|
||||
|
||||
$line = $value . $line;
|
||||
|
||||
if ($j === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($carry !== 0) {
|
||||
$line = $carry . $line;
|
||||
}
|
||||
|
||||
$line = \ltrim($line, '0');
|
||||
|
||||
if ($line !== '') {
|
||||
$line .= \str_repeat('0', $x - $blockALength - $i);
|
||||
$result = $this->add($result, $line);
|
||||
}
|
||||
|
||||
if ($i === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the division of two non-signed large integers.
|
||||
*
|
||||
* @return string[] The quotient and remainder.
|
||||
*/
|
||||
private function doDiv(string $a, string $b) : array
|
||||
{
|
||||
$cmp = $this->doCmp($a, $b);
|
||||
|
||||
if ($cmp === -1) {
|
||||
return ['0', $a];
|
||||
}
|
||||
|
||||
$x = \strlen($a);
|
||||
$y = \strlen($b);
|
||||
|
||||
// we now know that a >= b && x >= y
|
||||
|
||||
$q = '0'; // quotient
|
||||
$r = $a; // remainder
|
||||
$z = $y; // focus length, always $y or $y+1
|
||||
|
||||
for (;;) {
|
||||
$focus = \substr($a, 0, $z);
|
||||
|
||||
$cmp = $this->doCmp($focus, $b);
|
||||
|
||||
if ($cmp === -1) {
|
||||
if ($z === $x) { // remainder < dividend
|
||||
break;
|
||||
}
|
||||
|
||||
$z++;
|
||||
}
|
||||
|
||||
$zeros = \str_repeat('0', $x - $z);
|
||||
|
||||
$q = $this->add($q, '1' . $zeros);
|
||||
$a = $this->sub($a, $b . $zeros);
|
||||
|
||||
$r = $a;
|
||||
|
||||
if ($r === '0') { // remainder == 0
|
||||
break;
|
||||
}
|
||||
|
||||
$x = \strlen($a);
|
||||
|
||||
if ($x < $y) { // remainder < dividend
|
||||
break;
|
||||
}
|
||||
|
||||
$z = $y;
|
||||
}
|
||||
|
||||
return [$q, $r];
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two non-signed large numbers.
|
||||
*
|
||||
* @return int [-1, 0, 1]
|
||||
*/
|
||||
private function doCmp(string $a, string $b) : int
|
||||
{
|
||||
$x = \strlen($a);
|
||||
$y = \strlen($b);
|
||||
|
||||
$cmp = $x <=> $y;
|
||||
|
||||
if ($cmp !== 0) {
|
||||
return $cmp;
|
||||
}
|
||||
|
||||
return \strcmp($a, $b) <=> 0; // enforce [-1, 0, 1]
|
||||
}
|
||||
|
||||
/**
|
||||
* Pads the left of one of the given numbers with zeros if necessary to make both numbers the same length.
|
||||
*
|
||||
* The numbers must only consist of digits, without leading minus sign.
|
||||
*
|
||||
* @return array{string, string, int}
|
||||
*/
|
||||
private function pad(string $a, string $b) : array
|
||||
{
|
||||
$x = \strlen($a);
|
||||
$y = \strlen($b);
|
||||
|
||||
if ($x > $y) {
|
||||
$b = \str_repeat('0', $x - $y) . $b;
|
||||
|
||||
return [$a, $b, $x];
|
||||
}
|
||||
|
||||
if ($x < $y) {
|
||||
$a = \str_repeat('0', $y - $x) . $a;
|
||||
|
||||
return [$a, $b, $y];
|
||||
}
|
||||
|
||||
return [$a, $b, $x];
|
||||
}
|
||||
}
|
107
vendor/brick/math/src/RoundingMode.php
vendored
Normal file
107
vendor/brick/math/src/RoundingMode.php
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Brick\Math;
|
||||
|
||||
/**
|
||||
* Specifies a rounding behavior for numerical operations capable of discarding precision.
|
||||
*
|
||||
* Each rounding mode indicates how the least significant returned digit of a rounded result
|
||||
* is to be calculated. If fewer digits are returned than the digits needed to represent the
|
||||
* exact numerical result, the discarded digits will be referred to as the discarded fraction
|
||||
* regardless the digits' contribution to the value of the number. In other words, considered
|
||||
* as a numerical value, the discarded fraction could have an absolute value greater than one.
|
||||
*/
|
||||
final class RoundingMode
|
||||
{
|
||||
/**
|
||||
* Private constructor. This class is not instantiable.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the requested operation has an exact result, hence no rounding is necessary.
|
||||
*
|
||||
* If this rounding mode is specified on an operation that yields a result that
|
||||
* cannot be represented at the requested scale, a RoundingNecessaryException is thrown.
|
||||
*/
|
||||
public const UNNECESSARY = 0;
|
||||
|
||||
/**
|
||||
* Rounds away from zero.
|
||||
*
|
||||
* Always increments the digit prior to a nonzero discarded fraction.
|
||||
* Note that this rounding mode never decreases the magnitude of the calculated value.
|
||||
*/
|
||||
public const UP = 1;
|
||||
|
||||
/**
|
||||
* Rounds towards zero.
|
||||
*
|
||||
* Never increments the digit prior to a discarded fraction (i.e., truncates).
|
||||
* Note that this rounding mode never increases the magnitude of the calculated value.
|
||||
*/
|
||||
public const DOWN = 2;
|
||||
|
||||
/**
|
||||
* Rounds towards positive infinity.
|
||||
*
|
||||
* If the result is positive, behaves as for UP; if negative, behaves as for DOWN.
|
||||
* Note that this rounding mode never decreases the calculated value.
|
||||
*/
|
||||
public const CEILING = 3;
|
||||
|
||||
/**
|
||||
* Rounds towards negative infinity.
|
||||
*
|
||||
* If the result is positive, behave as for DOWN; if negative, behave as for UP.
|
||||
* Note that this rounding mode never increases the calculated value.
|
||||
*/
|
||||
public const FLOOR = 4;
|
||||
|
||||
/**
|
||||
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.
|
||||
*
|
||||
* Behaves as for UP if the discarded fraction is >= 0.5; otherwise, behaves as for DOWN.
|
||||
* Note that this is the rounding mode commonly taught at school.
|
||||
*/
|
||||
public const HALF_UP = 5;
|
||||
|
||||
/**
|
||||
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round down.
|
||||
*
|
||||
* Behaves as for UP if the discarded fraction is > 0.5; otherwise, behaves as for DOWN.
|
||||
*/
|
||||
public const HALF_DOWN = 6;
|
||||
|
||||
/**
|
||||
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards positive infinity.
|
||||
*
|
||||
* If the result is positive, behaves as for HALF_UP; if negative, behaves as for HALF_DOWN.
|
||||
*/
|
||||
public const HALF_CEILING = 7;
|
||||
|
||||
/**
|
||||
* Rounds towards "nearest neighbor" unless both neighbors are equidistant, in which case round towards negative infinity.
|
||||
*
|
||||
* If the result is positive, behaves as for HALF_DOWN; if negative, behaves as for HALF_UP.
|
||||
*/
|
||||
public const HALF_FLOOR = 8;
|
||||
|
||||
/**
|
||||
* Rounds towards the "nearest neighbor" unless both neighbors are equidistant, in which case rounds towards the even neighbor.
|
||||
*
|
||||
* Behaves as for HALF_UP if the digit to the left of the discarded fraction is odd;
|
||||
* behaves as for HALF_DOWN if it's even.
|
||||
*
|
||||
* Note that this is the rounding mode that statistically minimizes
|
||||
* cumulative error when applied repeatedly over a sequence of calculations.
|
||||
* It is sometimes known as "Banker's rounding", and is chiefly used in the USA.
|
||||
*/
|
||||
public const HALF_EVEN = 9;
|
||||
}
|
269
vendor/cakephp/core/App.php
vendored
Normal file
269
vendor/cakephp/core/App.php
vendored
Normal file
@ -0,0 +1,269 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 1.2.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
/**
|
||||
* App is responsible for resource location, and path management.
|
||||
*
|
||||
* ### Adding paths
|
||||
*
|
||||
* Additional paths for Templates and Plugins are configured with Configure now. See config/app.php for an
|
||||
* example. The `App.paths.plugins` and `App.paths.templates` variables are used to configure paths for plugins
|
||||
* and templates respectively. All class based resources should be mapped using your application's autoloader.
|
||||
*
|
||||
* ### Inspecting loaded paths
|
||||
*
|
||||
* You can inspect the currently loaded paths using `App::classPath('Controller')` for example to see loaded
|
||||
* controller paths.
|
||||
*
|
||||
* It is also possible to inspect paths for plugin classes, for instance, to get
|
||||
* the path to a plugin's helpers you would call `App::classPath('View/Helper', 'MyPlugin')`
|
||||
*
|
||||
* ### Locating plugins
|
||||
*
|
||||
* Plugins can be located with App as well. Using Plugin::path('DebugKit') for example, will
|
||||
* give you the full path to the DebugKit plugin.
|
||||
*
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/app.html
|
||||
*/
|
||||
class App
|
||||
{
|
||||
/**
|
||||
* Return the class name namespaced. This method checks if the class is defined on the
|
||||
* application/plugin, otherwise try to load from the CakePHP core
|
||||
*
|
||||
* @param string $class Class name
|
||||
* @param string $type Type of class
|
||||
* @param string $suffix Class name suffix
|
||||
* @return string|null Namespaced class name, null if the class is not found.
|
||||
* @psalm-return class-string|null
|
||||
*/
|
||||
public static function className(string $class, string $type = '', string $suffix = ''): ?string
|
||||
{
|
||||
if (strpos($class, '\\') !== false) {
|
||||
return class_exists($class) ? $class : null;
|
||||
}
|
||||
|
||||
[$plugin, $name] = pluginSplit($class);
|
||||
$fullname = '\\' . str_replace('/', '\\', $type . '\\' . $name) . $suffix;
|
||||
|
||||
$base = $plugin ?: Configure::read('App.namespace');
|
||||
if ($base !== null) {
|
||||
$base = str_replace('/', '\\', rtrim($base, '\\'));
|
||||
|
||||
if (static::_classExistsInBase($fullname, $base)) {
|
||||
/** @var class-string */
|
||||
return $base . $fullname;
|
||||
}
|
||||
}
|
||||
|
||||
if ($plugin || !static::_classExistsInBase($fullname, 'Cake')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var class-string */
|
||||
return 'Cake' . $fullname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the plugin split name of a class
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* ```
|
||||
* App::shortName(
|
||||
* 'SomeVendor\SomePlugin\Controller\Component\TestComponent',
|
||||
* 'Controller/Component',
|
||||
* 'Component'
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* Returns: SomeVendor/SomePlugin.Test
|
||||
*
|
||||
* ```
|
||||
* App::shortName(
|
||||
* 'SomeVendor\SomePlugin\Controller\Component\Subfolder\TestComponent',
|
||||
* 'Controller/Component',
|
||||
* 'Component'
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* Returns: SomeVendor/SomePlugin.Subfolder/Test
|
||||
*
|
||||
* ```
|
||||
* App::shortName(
|
||||
* 'Cake\Controller\Component\AuthComponent',
|
||||
* 'Controller/Component',
|
||||
* 'Component'
|
||||
* )
|
||||
* ```
|
||||
*
|
||||
* Returns: Auth
|
||||
*
|
||||
* @param string $class Class name
|
||||
* @param string $type Type of class
|
||||
* @param string $suffix Class name suffix
|
||||
* @return string Plugin split name of class
|
||||
*/
|
||||
public static function shortName(string $class, string $type, string $suffix = ''): string
|
||||
{
|
||||
$class = str_replace('\\', '/', $class);
|
||||
$type = '/' . $type . '/';
|
||||
|
||||
$pos = strrpos($class, $type);
|
||||
if ($pos === false) {
|
||||
return $class;
|
||||
}
|
||||
|
||||
$pluginName = (string)substr($class, 0, $pos);
|
||||
$name = (string)substr($class, $pos + strlen($type));
|
||||
|
||||
if ($suffix) {
|
||||
$name = (string)substr($name, 0, -strlen($suffix));
|
||||
}
|
||||
|
||||
$nonPluginNamespaces = [
|
||||
'Cake',
|
||||
str_replace('\\', '/', (string)Configure::read('App.namespace')),
|
||||
];
|
||||
if (in_array($pluginName, $nonPluginNamespaces, true)) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
return $pluginName . '.' . $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* _classExistsInBase
|
||||
*
|
||||
* Test isolation wrapper
|
||||
*
|
||||
* @param string $name Class name.
|
||||
* @param string $namespace Namespace.
|
||||
* @return bool
|
||||
*/
|
||||
protected static function _classExistsInBase(string $name, string $namespace): bool
|
||||
{
|
||||
return class_exists($namespace . $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to read information stored path.
|
||||
*
|
||||
* The 1st character of $type argument should be lower cased and will return the
|
||||
* value of `App.paths.$type` config.
|
||||
*
|
||||
* Default types:
|
||||
* - plugins
|
||||
* - templates
|
||||
* - locales
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* App::path('plugins');
|
||||
* ```
|
||||
*
|
||||
* Will return the value of `App.paths.plugins` config.
|
||||
*
|
||||
* Deprecated: 4.0 App::path() is deprecated for class path (inside src/ directory).
|
||||
* Use \Cake\Core\App::classPath() instead or directly the method on \Cake\Core\Plugin class.
|
||||
*
|
||||
* @param string $type Type of path
|
||||
* @param string|null $plugin Plugin name
|
||||
* @return array<string>
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/app.html#finding-paths-to-namespaces
|
||||
*/
|
||||
public static function path(string $type, ?string $plugin = null): array
|
||||
{
|
||||
if ($plugin === null && $type[0] === strtolower($type[0])) {
|
||||
return (array)Configure::read('App.paths.' . $type);
|
||||
}
|
||||
|
||||
if ($type === 'templates') {
|
||||
/** @psalm-suppress PossiblyNullArgument */
|
||||
return [Plugin::templatePath($plugin)];
|
||||
}
|
||||
|
||||
if ($type === 'locales') {
|
||||
/** @psalm-suppress PossiblyNullArgument */
|
||||
return [Plugin::path($plugin) . 'resources' . DIRECTORY_SEPARATOR . 'locales' . DIRECTORY_SEPARATOR];
|
||||
}
|
||||
|
||||
deprecationWarning(
|
||||
'App::path() is deprecated for class path.'
|
||||
. ' Use \Cake\Core\App::classPath() or \Cake\Core\Plugin::classPath() instead.'
|
||||
);
|
||||
|
||||
return static::classPath($type, $plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the path to a class type in the application or a plugin.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* ```
|
||||
* App::classPath('Model/Table');
|
||||
* ```
|
||||
*
|
||||
* Will return the path for tables - e.g. `src/Model/Table/`.
|
||||
*
|
||||
* ```
|
||||
* App::classPath('Model/Table', 'My/Plugin');
|
||||
* ```
|
||||
*
|
||||
* Will return the plugin based path for those.
|
||||
*
|
||||
* @param string $type Package type.
|
||||
* @param string|null $plugin Plugin name.
|
||||
* @return array<string>
|
||||
*/
|
||||
public static function classPath(string $type, ?string $plugin = null): array
|
||||
{
|
||||
if ($plugin !== null) {
|
||||
return [
|
||||
Plugin::classPath($plugin) . $type . DIRECTORY_SEPARATOR,
|
||||
];
|
||||
}
|
||||
|
||||
return [APP . $type . DIRECTORY_SEPARATOR];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full path to a package inside the CakePHP core
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* ```
|
||||
* App::core('Cache/Engine');
|
||||
* ```
|
||||
*
|
||||
* Will return the full path to the cache engines package.
|
||||
*
|
||||
* @param string $type Package type.
|
||||
* @return array<string> Full path to package
|
||||
*/
|
||||
public static function core(string $type): array
|
||||
{
|
||||
if ($type === 'templates') {
|
||||
return [CORE_PATH . 'templates' . DIRECTORY_SEPARATOR];
|
||||
}
|
||||
|
||||
return [CAKE . str_replace('/', DIRECTORY_SEPARATOR, $type) . DIRECTORY_SEPARATOR];
|
||||
}
|
||||
}
|
305
vendor/cakephp/core/BasePlugin.php
vendored
Normal file
305
vendor/cakephp/core/BasePlugin.php
vendored
Normal file
@ -0,0 +1,305 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright 2005-2011, Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.6.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use Cake\Console\CommandCollection;
|
||||
use Cake\Http\MiddlewareQueue;
|
||||
use Cake\Routing\RouteBuilder;
|
||||
use Closure;
|
||||
use InvalidArgumentException;
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* Base Plugin Class
|
||||
*
|
||||
* Every plugin should extend from this class or implement the interfaces and
|
||||
* include a plugin class in its src root folder.
|
||||
*/
|
||||
class BasePlugin implements PluginInterface
|
||||
{
|
||||
/**
|
||||
* Do bootstrapping or not
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $bootstrapEnabled = true;
|
||||
|
||||
/**
|
||||
* Console middleware
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $consoleEnabled = true;
|
||||
|
||||
/**
|
||||
* Enable middleware
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $middlewareEnabled = true;
|
||||
|
||||
/**
|
||||
* Register container services
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $servicesEnabled = true;
|
||||
|
||||
/**
|
||||
* Load routes or not
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $routesEnabled = true;
|
||||
|
||||
/**
|
||||
* The path to this plugin.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* The class path for this plugin.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $classPath;
|
||||
|
||||
/**
|
||||
* The config path for this plugin.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $configPath;
|
||||
|
||||
/**
|
||||
* The templates path for this plugin.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $templatePath;
|
||||
|
||||
/**
|
||||
* The name of this plugin
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array<string, mixed> $options Options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
foreach (static::VALID_HOOKS as $key) {
|
||||
if (isset($options[$key])) {
|
||||
$this->{"{$key}Enabled"} = (bool)$options[$key];
|
||||
}
|
||||
}
|
||||
foreach (['name', 'path', 'classPath', 'configPath', 'templatePath'] as $path) {
|
||||
if (isset($options[$path])) {
|
||||
$this->{$path} = $options[$path];
|
||||
}
|
||||
}
|
||||
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialization hook called from constructor.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function initialize(): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
if ($this->name) {
|
||||
return $this->name;
|
||||
}
|
||||
$parts = explode('\\', static::class);
|
||||
array_pop($parts);
|
||||
$this->name = implode('/', $parts);
|
||||
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getPath(): string
|
||||
{
|
||||
if ($this->path) {
|
||||
return $this->path;
|
||||
}
|
||||
$reflection = new ReflectionClass($this);
|
||||
$path = dirname($reflection->getFileName());
|
||||
|
||||
// Trim off src
|
||||
if (substr($path, -3) === 'src') {
|
||||
$path = substr($path, 0, -3);
|
||||
}
|
||||
$this->path = rtrim($path, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
|
||||
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getConfigPath(): string
|
||||
{
|
||||
if ($this->configPath) {
|
||||
return $this->configPath;
|
||||
}
|
||||
$path = $this->getPath();
|
||||
|
||||
return $path . 'config' . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getClassPath(): string
|
||||
{
|
||||
if ($this->classPath) {
|
||||
return $this->classPath;
|
||||
}
|
||||
$path = $this->getPath();
|
||||
|
||||
return $path . 'src' . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getTemplatePath(): string
|
||||
{
|
||||
if ($this->templatePath) {
|
||||
return $this->templatePath;
|
||||
}
|
||||
$path = $this->getPath();
|
||||
|
||||
return $this->templatePath = $path . 'templates' . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function enable(string $hook)
|
||||
{
|
||||
$this->checkHook($hook);
|
||||
$this->{"{$hook}Enabled"} = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function disable(string $hook)
|
||||
{
|
||||
$this->checkHook($hook);
|
||||
$this->{"{$hook}Enabled"} = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function isEnabled(string $hook): bool
|
||||
{
|
||||
$this->checkHook($hook);
|
||||
|
||||
return $this->{"{$hook}Enabled"} === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a hook name is valid
|
||||
*
|
||||
* @param string $hook The hook name to check
|
||||
* @throws \InvalidArgumentException on invalid hooks
|
||||
* @return void
|
||||
*/
|
||||
protected function checkHook(string $hook): void
|
||||
{
|
||||
if (!in_array($hook, static::VALID_HOOKS, true)) {
|
||||
throw new InvalidArgumentException(
|
||||
"`$hook` is not a valid hook name. Must be one of " . implode(', ', static::VALID_HOOKS)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function routes(RouteBuilder $routes): void
|
||||
{
|
||||
$path = $this->getConfigPath() . 'routes.php';
|
||||
if (is_file($path)) {
|
||||
$return = require $path;
|
||||
if ($return instanceof Closure) {
|
||||
$return($routes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function bootstrap(PluginApplicationInterface $app): void
|
||||
{
|
||||
$bootstrap = $this->getConfigPath() . 'bootstrap.php';
|
||||
if (is_file($bootstrap)) {
|
||||
require $bootstrap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function console(CommandCollection $commands): CommandCollection
|
||||
{
|
||||
return $commands->addMany($commands->discoverPlugin($this->getName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
|
||||
{
|
||||
return $middlewareQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register container services for this plugin.
|
||||
*
|
||||
* @param \Cake\Core\ContainerInterface $container The container to add services to.
|
||||
* @return void
|
||||
*/
|
||||
public function services(ContainerInterface $container): void
|
||||
{
|
||||
}
|
||||
}
|
139
vendor/cakephp/core/ClassLoader.php
vendored
Normal file
139
vendor/cakephp/core/ClassLoader.php
vendored
Normal file
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright 2005-2011, Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
/**
|
||||
* ClassLoader
|
||||
*
|
||||
* @deprecated 4.0.3 Use composer to generate autoload files instead.
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
/**
|
||||
* An associative array where the key is a namespace prefix and the value
|
||||
* is an array of base directories for classes in that namespace.
|
||||
*
|
||||
* @var array<string, array>
|
||||
*/
|
||||
protected $_prefixes = [];
|
||||
|
||||
/**
|
||||
* Register loader with SPL autoloader stack.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
/** @var callable $callable */
|
||||
$callable = [$this, 'loadClass'];
|
||||
spl_autoload_register($callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a base directory for a namespace prefix.
|
||||
*
|
||||
* @param string $prefix The namespace prefix.
|
||||
* @param string $baseDir A base directory for class files in the
|
||||
* namespace.
|
||||
* @param bool $prepend If true, prepend the base directory to the stack
|
||||
* instead of appending it; this causes it to be searched first rather
|
||||
* than last.
|
||||
* @return void
|
||||
*/
|
||||
public function addNamespace(string $prefix, string $baseDir, bool $prepend = false): void
|
||||
{
|
||||
$prefix = trim($prefix, '\\') . '\\';
|
||||
|
||||
$baseDir = rtrim($baseDir, '/') . DIRECTORY_SEPARATOR;
|
||||
$baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR) . '/';
|
||||
|
||||
$this->_prefixes[$prefix] = $this->_prefixes[$prefix] ?? [];
|
||||
|
||||
if ($prepend) {
|
||||
array_unshift($this->_prefixes[$prefix], $baseDir);
|
||||
} else {
|
||||
$this->_prefixes[$prefix][] = $baseDir;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the class file for a given class name.
|
||||
*
|
||||
* @param string $class The fully-qualified class name.
|
||||
* @return string|false The mapped file name on success, or boolean false on
|
||||
* failure.
|
||||
*/
|
||||
public function loadClass(string $class)
|
||||
{
|
||||
$prefix = $class;
|
||||
|
||||
while (($pos = strrpos($prefix, '\\')) !== false) {
|
||||
$prefix = substr($class, 0, $pos + 1);
|
||||
$relativeClass = substr($class, $pos + 1);
|
||||
|
||||
$mappedFile = $this->_loadMappedFile($prefix, $relativeClass);
|
||||
if ($mappedFile) {
|
||||
return $mappedFile;
|
||||
}
|
||||
|
||||
$prefix = rtrim($prefix, '\\');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the mapped file for a namespace prefix and relative class.
|
||||
*
|
||||
* @param string $prefix The namespace prefix.
|
||||
* @param string $relativeClass The relative class name.
|
||||
* @return string|false Boolean false if no mapped file can be loaded, or the
|
||||
* name of the mapped file that was loaded.
|
||||
*/
|
||||
protected function _loadMappedFile(string $prefix, string $relativeClass)
|
||||
{
|
||||
if (!isset($this->_prefixes[$prefix])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($this->_prefixes[$prefix] as $baseDir) {
|
||||
$file = $baseDir . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php';
|
||||
|
||||
if ($this->_requireFile($file)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If a file exists, require it from the file system.
|
||||
*
|
||||
* @param string $file The file to require.
|
||||
* @return bool True if the file exists, false if not.
|
||||
*/
|
||||
protected function _requireFile(string $file): bool
|
||||
{
|
||||
if (file_exists($file)) {
|
||||
require $file;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
498
vendor/cakephp/core/Configure.php
vendored
Normal file
498
vendor/cakephp/core/Configure.php
vendored
Normal file
@ -0,0 +1,498 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 1.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use Cake\Cache\Cache;
|
||||
use Cake\Core\Configure\ConfigEngineInterface;
|
||||
use Cake\Core\Configure\Engine\PhpConfig;
|
||||
use Cake\Core\Exception\CakeException;
|
||||
use Cake\Utility\Hash;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Configuration class. Used for managing runtime configuration information.
|
||||
*
|
||||
* Provides features for reading and writing to the runtime configuration, as well
|
||||
* as methods for loading additional configuration files or storing runtime configuration
|
||||
* for future use.
|
||||
*
|
||||
* @link https://book.cakephp.org/4/en/development/configuration.html
|
||||
*/
|
||||
class Configure
|
||||
{
|
||||
/**
|
||||
* Array of values currently stored in Configure.
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected static $_values = [
|
||||
'debug' => false,
|
||||
];
|
||||
|
||||
/**
|
||||
* Configured engine classes, used to load config files from resources
|
||||
*
|
||||
* @see \Cake\Core\Configure::load()
|
||||
* @var array<\Cake\Core\Configure\ConfigEngineInterface>
|
||||
*/
|
||||
protected static $_engines = [];
|
||||
|
||||
/**
|
||||
* Flag to track whether ini_set exists.
|
||||
*
|
||||
* @var bool|null
|
||||
*/
|
||||
protected static $_hasIniSet;
|
||||
|
||||
/**
|
||||
* Used to store a dynamic variable in Configure.
|
||||
*
|
||||
* Usage:
|
||||
* ```
|
||||
* Configure::write('One.key1', 'value of the Configure::One[key1]');
|
||||
* Configure::write(['One.key1' => 'value of the Configure::One[key1]']);
|
||||
* Configure::write('One', [
|
||||
* 'key1' => 'value of the Configure::One[key1]',
|
||||
* 'key2' => 'value of the Configure::One[key2]'
|
||||
* ]);
|
||||
*
|
||||
* Configure::write([
|
||||
* 'One.key1' => 'value of the Configure::One[key1]',
|
||||
* 'One.key2' => 'value of the Configure::One[key2]'
|
||||
* ]);
|
||||
* ```
|
||||
*
|
||||
* @param array<string, mixed>|string $config The key to write, can be a dot notation value.
|
||||
* Alternatively can be an array containing key(s) and value(s).
|
||||
* @param mixed $value Value to set for the given key.
|
||||
* @return void
|
||||
* @link https://book.cakephp.org/4/en/development/configuration.html#writing-configuration-data
|
||||
*/
|
||||
public static function write($config, $value = null): void
|
||||
{
|
||||
if (!is_array($config)) {
|
||||
$config = [$config => $value];
|
||||
}
|
||||
|
||||
foreach ($config as $name => $valueToInsert) {
|
||||
static::$_values = Hash::insert(static::$_values, $name, $valueToInsert);
|
||||
}
|
||||
|
||||
if (isset($config['debug'])) {
|
||||
if (static::$_hasIniSet === null) {
|
||||
static::$_hasIniSet = function_exists('ini_set');
|
||||
}
|
||||
if (static::$_hasIniSet) {
|
||||
ini_set('display_errors', $config['debug'] ? '1' : '0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to read information stored in Configure. It's not
|
||||
* possible to store `null` values in Configure.
|
||||
*
|
||||
* Usage:
|
||||
* ```
|
||||
* Configure::read('Name'); will return all values for Name
|
||||
* Configure::read('Name.key'); will return only the value of Configure::Name[key]
|
||||
* ```
|
||||
*
|
||||
* @param string|null $var Variable to obtain. Use '.' to access array elements.
|
||||
* @param mixed $default The return value when the configure does not exist
|
||||
* @return mixed Value stored in configure, or null.
|
||||
* @link https://book.cakephp.org/4/en/development/configuration.html#reading-configuration-data
|
||||
*/
|
||||
public static function read(?string $var = null, $default = null)
|
||||
{
|
||||
if ($var === null) {
|
||||
return static::$_values;
|
||||
}
|
||||
|
||||
return Hash::get(static::$_values, $var, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if given variable is set in Configure.
|
||||
*
|
||||
* @param string $var Variable name to check for
|
||||
* @return bool True if variable is there
|
||||
*/
|
||||
public static function check(string $var): bool
|
||||
{
|
||||
if (empty($var)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return static::read($var) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to get information stored in Configure. It's not
|
||||
* possible to store `null` values in Configure.
|
||||
*
|
||||
* Acts as a wrapper around Configure::read() and Configure::check().
|
||||
* The configure key/value pair fetched via this method is expected to exist.
|
||||
* In case it does not an exception will be thrown.
|
||||
*
|
||||
* Usage:
|
||||
* ```
|
||||
* Configure::readOrFail('Name'); will return all values for Name
|
||||
* Configure::readOrFail('Name.key'); will return only the value of Configure::Name[key]
|
||||
* ```
|
||||
*
|
||||
* @param string $var Variable to obtain. Use '.' to access array elements.
|
||||
* @return mixed Value stored in configure.
|
||||
* @throws \RuntimeException if the requested configuration is not set.
|
||||
* @link https://book.cakephp.org/4/en/development/configuration.html#reading-configuration-data
|
||||
*/
|
||||
public static function readOrFail(string $var)
|
||||
{
|
||||
if (!static::check($var)) {
|
||||
throw new RuntimeException(sprintf('Expected configuration key "%s" not found.', $var));
|
||||
}
|
||||
|
||||
return static::read($var);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to delete a variable from Configure.
|
||||
*
|
||||
* Usage:
|
||||
* ```
|
||||
* Configure::delete('Name'); will delete the entire Configure::Name
|
||||
* Configure::delete('Name.key'); will delete only the Configure::Name[key]
|
||||
* ```
|
||||
*
|
||||
* @param string $var the var to be deleted
|
||||
* @return void
|
||||
* @link https://book.cakephp.org/4/en/development/configuration.html#deleting-configuration-data
|
||||
*/
|
||||
public static function delete(string $var): void
|
||||
{
|
||||
static::$_values = Hash::remove(static::$_values, $var);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to consume information stored in Configure. It's not
|
||||
* possible to store `null` values in Configure.
|
||||
*
|
||||
* Acts as a wrapper around Configure::consume() and Configure::check().
|
||||
* The configure key/value pair consumed via this method is expected to exist.
|
||||
* In case it does not an exception will be thrown.
|
||||
*
|
||||
* @param string $var Variable to consume. Use '.' to access array elements.
|
||||
* @return mixed Value stored in configure.
|
||||
* @throws \RuntimeException if the requested configuration is not set.
|
||||
* @since 3.6.0
|
||||
*/
|
||||
public static function consumeOrFail(string $var)
|
||||
{
|
||||
if (!static::check($var)) {
|
||||
throw new RuntimeException(sprintf('Expected configuration key "%s" not found.', $var));
|
||||
}
|
||||
|
||||
return static::consume($var);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to read and delete a variable from Configure.
|
||||
*
|
||||
* This is primarily used during bootstrapping to move configuration data
|
||||
* out of configure into the various other classes in CakePHP.
|
||||
*
|
||||
* @param string $var The key to read and remove.
|
||||
* @return array|string|null
|
||||
*/
|
||||
public static function consume(string $var)
|
||||
{
|
||||
if (strpos($var, '.') === false) {
|
||||
if (!isset(static::$_values[$var])) {
|
||||
return null;
|
||||
}
|
||||
$value = static::$_values[$var];
|
||||
unset(static::$_values[$var]);
|
||||
|
||||
return $value;
|
||||
}
|
||||
$value = Hash::get(static::$_values, $var);
|
||||
static::delete($var);
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new engine to Configure. Engines allow you to read configuration
|
||||
* files in various formats/storage locations. CakePHP comes with two built-in engines
|
||||
* PhpConfig and IniConfig. You can also implement your own engine classes in your application.
|
||||
*
|
||||
* To add a new engine to Configure:
|
||||
*
|
||||
* ```
|
||||
* Configure::config('ini', new IniConfig());
|
||||
* ```
|
||||
*
|
||||
* @param string $name The name of the engine being configured. This alias is used later to
|
||||
* read values from a specific engine.
|
||||
* @param \Cake\Core\Configure\ConfigEngineInterface $engine The engine to append.
|
||||
* @return void
|
||||
*/
|
||||
public static function config(string $name, ConfigEngineInterface $engine): void
|
||||
{
|
||||
static::$_engines[$name] = $engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the Engine objects is configured.
|
||||
*
|
||||
* @param string $name Engine name.
|
||||
* @return bool
|
||||
*/
|
||||
public static function isConfigured(string $name): bool
|
||||
{
|
||||
return isset(static::$_engines[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the names of the configured Engine objects.
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public static function configured(): array
|
||||
{
|
||||
$engines = array_keys(static::$_engines);
|
||||
|
||||
return array_map(function ($key) {
|
||||
return (string)$key;
|
||||
}, $engines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a configured engine. This will unset the engine
|
||||
* and make any future attempts to use it cause an Exception.
|
||||
*
|
||||
* @param string $name Name of the engine to drop.
|
||||
* @return bool Success
|
||||
*/
|
||||
public static function drop(string $name): bool
|
||||
{
|
||||
if (!isset(static::$_engines[$name])) {
|
||||
return false;
|
||||
}
|
||||
unset(static::$_engines[$name]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads stored configuration information from a resource. You can add
|
||||
* config file resource engines with `Configure::config()`.
|
||||
*
|
||||
* Loaded configuration information will be merged with the current
|
||||
* runtime configuration. You can load configuration files from plugins
|
||||
* by preceding the filename with the plugin name.
|
||||
*
|
||||
* `Configure::load('Users.user', 'default')`
|
||||
*
|
||||
* Would load the 'user' config file using the default config engine. You can load
|
||||
* app config files by giving the name of the resource you want loaded.
|
||||
*
|
||||
* ```
|
||||
* Configure::load('setup', 'default');
|
||||
* ```
|
||||
*
|
||||
* If using `default` config and no engine has been configured for it yet,
|
||||
* one will be automatically created using PhpConfig
|
||||
*
|
||||
* @param string $key name of configuration resource to load.
|
||||
* @param string $config Name of the configured engine to use to read the resource identified by $key.
|
||||
* @param bool $merge if config files should be merged instead of simply overridden
|
||||
* @return bool True if load successful.
|
||||
* @throws \Cake\Core\Exception\CakeException if the $config engine is not found
|
||||
* @link https://book.cakephp.org/4/en/development/configuration.html#reading-and-writing-configuration-files
|
||||
*/
|
||||
public static function load(string $key, string $config = 'default', bool $merge = true): bool
|
||||
{
|
||||
$engine = static::_getEngine($config);
|
||||
if (!$engine) {
|
||||
throw new CakeException(
|
||||
sprintf(
|
||||
'Config %s engine not found when attempting to load %s.',
|
||||
$config,
|
||||
$key
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$values = $engine->read($key);
|
||||
|
||||
if ($merge) {
|
||||
$values = Hash::merge(static::$_values, $values);
|
||||
}
|
||||
|
||||
static::write($values);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump data currently in Configure into $key. The serialization format
|
||||
* is decided by the config engine attached as $config. For example, if the
|
||||
* 'default' adapter is a PhpConfig, the generated file will be a PHP
|
||||
* configuration file loadable by the PhpConfig.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* Given that the 'default' engine is an instance of PhpConfig.
|
||||
* Save all data in Configure to the file `my_config.php`:
|
||||
*
|
||||
* ```
|
||||
* Configure::dump('my_config', 'default');
|
||||
* ```
|
||||
*
|
||||
* Save only the error handling configuration:
|
||||
*
|
||||
* ```
|
||||
* Configure::dump('error', 'default', ['Error', 'Exception'];
|
||||
* ```
|
||||
*
|
||||
* @param string $key The identifier to create in the config adapter.
|
||||
* This could be a filename or a cache key depending on the adapter being used.
|
||||
* @param string $config The name of the configured adapter to dump data with.
|
||||
* @param array<string> $keys The name of the top-level keys you want to dump.
|
||||
* This allows you save only some data stored in Configure.
|
||||
* @return bool Success
|
||||
* @throws \Cake\Core\Exception\CakeException if the adapter does not implement a `dump` method.
|
||||
*/
|
||||
public static function dump(string $key, string $config = 'default', array $keys = []): bool
|
||||
{
|
||||
$engine = static::_getEngine($config);
|
||||
if (!$engine) {
|
||||
throw new CakeException(sprintf('There is no "%s" config engine.', $config));
|
||||
}
|
||||
$values = static::$_values;
|
||||
if (!empty($keys)) {
|
||||
$values = array_intersect_key($values, array_flip($keys));
|
||||
}
|
||||
|
||||
return $engine->dump($key, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured engine. Internally used by `Configure::load()` and `Configure::dump()`
|
||||
* Will create new PhpConfig for default if not configured yet.
|
||||
*
|
||||
* @param string $config The name of the configured adapter
|
||||
* @return \Cake\Core\Configure\ConfigEngineInterface|null Engine instance or null
|
||||
*/
|
||||
protected static function _getEngine(string $config): ?ConfigEngineInterface
|
||||
{
|
||||
if (!isset(static::$_engines[$config])) {
|
||||
if ($config !== 'default') {
|
||||
return null;
|
||||
}
|
||||
static::config($config, new PhpConfig());
|
||||
}
|
||||
|
||||
return static::$_engines[$config];
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to determine the current version of CakePHP.
|
||||
*
|
||||
* Usage
|
||||
* ```
|
||||
* Configure::version();
|
||||
* ```
|
||||
*
|
||||
* @return string Current version of CakePHP
|
||||
*/
|
||||
public static function version(): string
|
||||
{
|
||||
$version = static::read('Cake.version');
|
||||
if ($version !== null) {
|
||||
return $version;
|
||||
}
|
||||
|
||||
$path = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'config/config.php';
|
||||
if (is_file($path)) {
|
||||
$config = require $path;
|
||||
static::write($config);
|
||||
|
||||
return static::read('Cake.version');
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to write runtime configuration into Cache. Stored runtime configuration can be
|
||||
* restored using `Configure::restore()`. These methods can be used to enable configuration managers
|
||||
* frontends, or other GUI type interfaces for configuration.
|
||||
*
|
||||
* @param string $name The storage name for the saved configuration.
|
||||
* @param string $cacheConfig The cache configuration to save into. Defaults to 'default'
|
||||
* @param array|null $data Either an array of data to store, or leave empty to store all values.
|
||||
* @return bool Success
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function store(string $name, string $cacheConfig = 'default', ?array $data = null): bool
|
||||
{
|
||||
if ($data === null) {
|
||||
$data = static::$_values;
|
||||
}
|
||||
if (!class_exists(Cache::class)) {
|
||||
throw new RuntimeException('You must install cakephp/cache to use Configure::store()');
|
||||
}
|
||||
|
||||
return Cache::write($name, $data, $cacheConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores configuration data stored in the Cache into configure. Restored
|
||||
* values will overwrite existing ones.
|
||||
*
|
||||
* @param string $name Name of the stored config file to load.
|
||||
* @param string $cacheConfig Name of the Cache configuration to read from.
|
||||
* @return bool Success.
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function restore(string $name, string $cacheConfig = 'default'): bool
|
||||
{
|
||||
if (!class_exists(Cache::class)) {
|
||||
throw new RuntimeException('You must install cakephp/cache to use Configure::restore()');
|
||||
}
|
||||
$values = Cache::read($name, $cacheConfig);
|
||||
if ($values) {
|
||||
static::write($values);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all values stored in Configure.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function clear(): void
|
||||
{
|
||||
static::$_values = [];
|
||||
}
|
||||
}
|
44
vendor/cakephp/core/Configure/ConfigEngineInterface.php
vendored
Normal file
44
vendor/cakephp/core/Configure/ConfigEngineInterface.php
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 1.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\Configure;
|
||||
|
||||
/**
|
||||
* An interface for creating objects compatible with Configure::load()
|
||||
*/
|
||||
interface ConfigEngineInterface
|
||||
{
|
||||
/**
|
||||
* Read a configuration file/storage key
|
||||
*
|
||||
* This method is used for reading configuration information from sources.
|
||||
* These sources can either be static resources like files, or dynamic ones like
|
||||
* a database, or other datasource.
|
||||
*
|
||||
* @param string $key Key to read.
|
||||
* @return array An array of data to merge into the runtime configuration
|
||||
*/
|
||||
public function read(string $key): array;
|
||||
|
||||
/**
|
||||
* Dumps the configure data into the storage key/file of the given `$key`.
|
||||
*
|
||||
* @param string $key The identifier to write to.
|
||||
* @param array $data The data to dump.
|
||||
* @return bool True on success or false on failure.
|
||||
*/
|
||||
public function dump(string $key, array $data): bool;
|
||||
}
|
203
vendor/cakephp/core/Configure/Engine/IniConfig.php
vendored
Normal file
203
vendor/cakephp/core/Configure/Engine/IniConfig.php
vendored
Normal file
@ -0,0 +1,203 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 2.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\Configure\Engine;
|
||||
|
||||
use Cake\Core\Configure\ConfigEngineInterface;
|
||||
use Cake\Core\Configure\FileConfigTrait;
|
||||
use Cake\Utility\Hash;
|
||||
|
||||
/**
|
||||
* Ini file configuration engine.
|
||||
*
|
||||
* Since IniConfig uses parse_ini_file underneath, you should be aware that this
|
||||
* class shares the same behavior, especially with regards to boolean and null values.
|
||||
*
|
||||
* In addition to the native `parse_ini_file` features, IniConfig also allows you
|
||||
* to create nested array structures through usage of `.` delimited names. This allows
|
||||
* you to create nested arrays structures in an ini config file. For example:
|
||||
*
|
||||
* `db.password = secret` would turn into `['db' => ['password' => 'secret']]`
|
||||
*
|
||||
* You can nest properties as deeply as needed using `.`'s. In addition to using `.` you
|
||||
* can use standard ini section notation to create nested structures:
|
||||
*
|
||||
* ```
|
||||
* [section]
|
||||
* key = value
|
||||
* ```
|
||||
*
|
||||
* Once loaded into Configure, the above would be accessed using:
|
||||
*
|
||||
* `Configure::read('section.key');
|
||||
*
|
||||
* You can also use `.` separated values in section names to create more deeply
|
||||
* nested structures.
|
||||
*
|
||||
* IniConfig also manipulates how the special ini values of
|
||||
* 'yes', 'no', 'on', 'off', 'null' are handled. These values will be
|
||||
* converted to their boolean equivalents.
|
||||
*
|
||||
* @see https://secure.php.net/parse_ini_file
|
||||
*/
|
||||
class IniConfig implements ConfigEngineInterface
|
||||
{
|
||||
use FileConfigTrait;
|
||||
|
||||
/**
|
||||
* File extension.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_extension = '.ini';
|
||||
|
||||
/**
|
||||
* The section to read, if null all sections will be read.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $_section;
|
||||
|
||||
/**
|
||||
* Build and construct a new ini file parser. The parser can be used to read
|
||||
* ini files that are on the filesystem.
|
||||
*
|
||||
* @param string|null $path Path to load ini config files from. Defaults to CONFIG.
|
||||
* @param string|null $section Only get one section, leave null to parse and fetch
|
||||
* all sections in the ini file.
|
||||
*/
|
||||
public function __construct(?string $path = null, ?string $section = null)
|
||||
{
|
||||
if ($path === null) {
|
||||
$path = CONFIG;
|
||||
}
|
||||
$this->_path = $path;
|
||||
$this->_section = $section;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an ini file and return the results as an array.
|
||||
*
|
||||
* @param string $key The identifier to read from. If the key has a . it will be treated
|
||||
* as a plugin prefix. The chosen file must be on the engine's path.
|
||||
* @return array Parsed configuration values.
|
||||
* @throws \Cake\Core\Exception\CakeException when files don't exist.
|
||||
* Or when files contain '..' as this could lead to abusive reads.
|
||||
*/
|
||||
public function read(string $key): array
|
||||
{
|
||||
$file = $this->_getFilePath($key, true);
|
||||
|
||||
$contents = parse_ini_file($file, true);
|
||||
if ($this->_section && isset($contents[$this->_section])) {
|
||||
$values = $this->_parseNestedValues($contents[$this->_section]);
|
||||
} else {
|
||||
$values = [];
|
||||
foreach ($contents as $section => $attribs) {
|
||||
if (is_array($attribs)) {
|
||||
$values[$section] = $this->_parseNestedValues($attribs);
|
||||
} else {
|
||||
$parse = $this->_parseNestedValues([$attribs]);
|
||||
$values[$section] = array_shift($parse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* parses nested values out of keys.
|
||||
*
|
||||
* @param array $values Values to be exploded.
|
||||
* @return array Array of values exploded
|
||||
*/
|
||||
protected function _parseNestedValues(array $values): array
|
||||
{
|
||||
foreach ($values as $key => $value) {
|
||||
if ($value === '1') {
|
||||
$value = true;
|
||||
}
|
||||
if ($value === '') {
|
||||
$value = false;
|
||||
}
|
||||
unset($values[$key]);
|
||||
if (strpos((string)$key, '.') !== false) {
|
||||
$values = Hash::insert($values, $key, $value);
|
||||
} else {
|
||||
$values[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps the state of Configure data into an ini formatted string.
|
||||
*
|
||||
* @param string $key The identifier to write to. If the key has a . it will be treated
|
||||
* as a plugin prefix.
|
||||
* @param array $data The data to convert to ini file.
|
||||
* @return bool Success.
|
||||
*/
|
||||
public function dump(string $key, array $data): bool
|
||||
{
|
||||
$result = [];
|
||||
foreach ($data as $k => $value) {
|
||||
$isSection = false;
|
||||
/** @psalm-suppress InvalidArrayAccess */
|
||||
if ($k[0] !== '[') {
|
||||
$result[] = "[$k]";
|
||||
$isSection = true;
|
||||
}
|
||||
if (is_array($value)) {
|
||||
$kValues = Hash::flatten($value, '.');
|
||||
foreach ($kValues as $k2 => $v) {
|
||||
$result[] = "$k2 = " . $this->_value($v);
|
||||
}
|
||||
}
|
||||
if ($isSection) {
|
||||
$result[] = '';
|
||||
}
|
||||
}
|
||||
$contents = trim(implode("\n", $result));
|
||||
|
||||
$filename = $this->_getFilePath($key);
|
||||
|
||||
return file_put_contents($filename, $contents) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a value into the ini equivalent
|
||||
*
|
||||
* @param mixed $value Value to export.
|
||||
* @return string String value for ini file.
|
||||
*/
|
||||
protected function _value($value): string
|
||||
{
|
||||
if ($value === null) {
|
||||
return 'null';
|
||||
}
|
||||
if ($value === true) {
|
||||
return 'true';
|
||||
}
|
||||
if ($value === false) {
|
||||
return 'false';
|
||||
}
|
||||
|
||||
return (string)$value;
|
||||
}
|
||||
}
|
115
vendor/cakephp/core/Configure/Engine/JsonConfig.php
vendored
Normal file
115
vendor/cakephp/core/Configure/Engine/JsonConfig.php
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\Configure\Engine;
|
||||
|
||||
use Cake\Core\Configure\ConfigEngineInterface;
|
||||
use Cake\Core\Configure\FileConfigTrait;
|
||||
use Cake\Core\Exception\CakeException;
|
||||
|
||||
/**
|
||||
* JSON engine allows Configure to load configuration values from
|
||||
* files containing JSON strings.
|
||||
*
|
||||
* An example JSON file would look like::
|
||||
*
|
||||
* ```
|
||||
* {
|
||||
* "debug": false,
|
||||
* "App": {
|
||||
* "namespace": "MyApp"
|
||||
* },
|
||||
* "Security": {
|
||||
* "salt": "its-secret"
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class JsonConfig implements ConfigEngineInterface
|
||||
{
|
||||
use FileConfigTrait;
|
||||
|
||||
/**
|
||||
* File extension.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_extension = '.json';
|
||||
|
||||
/**
|
||||
* Constructor for JSON Config file reading.
|
||||
*
|
||||
* @param string|null $path The path to read config files from. Defaults to CONFIG.
|
||||
*/
|
||||
public function __construct(?string $path = null)
|
||||
{
|
||||
if ($path === null) {
|
||||
$path = CONFIG;
|
||||
}
|
||||
$this->_path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a config file and return its contents.
|
||||
*
|
||||
* Files with `.` in the name will be treated as values in plugins. Instead of
|
||||
* reading from the initialized path, plugin keys will be located using Plugin::path().
|
||||
*
|
||||
* @param string $key The identifier to read from. If the key has a . it will be treated
|
||||
* as a plugin prefix.
|
||||
* @return array Parsed configuration values.
|
||||
* @throws \Cake\Core\Exception\CakeException When files don't exist or when
|
||||
* files contain '..' (as this could lead to abusive reads) or when there
|
||||
* is an error parsing the JSON string.
|
||||
*/
|
||||
public function read(string $key): array
|
||||
{
|
||||
$file = $this->_getFilePath($key, true);
|
||||
|
||||
$values = json_decode(file_get_contents($file), true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
throw new CakeException(sprintf(
|
||||
'Error parsing JSON string fetched from config file "%s.json": %s',
|
||||
$key,
|
||||
json_last_error_msg()
|
||||
));
|
||||
}
|
||||
if (!is_array($values)) {
|
||||
throw new CakeException(sprintf(
|
||||
'Decoding JSON config file "%s.json" did not return an array',
|
||||
$key
|
||||
));
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the provided $data into a JSON string that can be used saved
|
||||
* into a file and loaded later.
|
||||
*
|
||||
* @param string $key The identifier to write to. If the key has a . it will
|
||||
* be treated as a plugin prefix.
|
||||
* @param array $data Data to dump.
|
||||
* @return bool Success
|
||||
*/
|
||||
public function dump(string $key, array $data): bool
|
||||
{
|
||||
$filename = $this->_getFilePath($key);
|
||||
|
||||
return file_put_contents($filename, json_encode($data, JSON_PRETTY_PRINT)) > 0;
|
||||
}
|
||||
}
|
114
vendor/cakephp/core/Configure/Engine/PhpConfig.php
vendored
Normal file
114
vendor/cakephp/core/Configure/Engine/PhpConfig.php
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 2.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\Configure\Engine;
|
||||
|
||||
use Cake\Core\Configure\ConfigEngineInterface;
|
||||
use Cake\Core\Configure\FileConfigTrait;
|
||||
use Cake\Core\Exception\CakeException;
|
||||
|
||||
/**
|
||||
* PHP engine allows Configure to load configuration values from
|
||||
* files containing simple PHP arrays.
|
||||
*
|
||||
* Files compatible with PhpConfig should return an array that
|
||||
* contains all the configuration data contained in the file.
|
||||
*
|
||||
* An example configuration file would look like::
|
||||
*
|
||||
* ```
|
||||
* <?php
|
||||
* return [
|
||||
* 'debug' => false,
|
||||
* 'Security' => [
|
||||
* 'salt' => 'its-secret'
|
||||
* ],
|
||||
* 'App' => [
|
||||
* 'namespace' => 'App'
|
||||
* ]
|
||||
* ];
|
||||
* ```
|
||||
*
|
||||
* @see \Cake\Core\Configure::load() for how to load custom configuration files.
|
||||
*/
|
||||
class PhpConfig implements ConfigEngineInterface
|
||||
{
|
||||
use FileConfigTrait;
|
||||
|
||||
/**
|
||||
* File extension.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_extension = '.php';
|
||||
|
||||
/**
|
||||
* Constructor for PHP Config file reading.
|
||||
*
|
||||
* @param string|null $path The path to read config files from. Defaults to CONFIG.
|
||||
*/
|
||||
public function __construct(?string $path = null)
|
||||
{
|
||||
if ($path === null) {
|
||||
$path = CONFIG;
|
||||
}
|
||||
$this->_path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a config file and return its contents.
|
||||
*
|
||||
* Files with `.` in the name will be treated as values in plugins. Instead of
|
||||
* reading from the initialized path, plugin keys will be located using Plugin::path().
|
||||
*
|
||||
* @param string $key The identifier to read from. If the key has a . it will be treated
|
||||
* as a plugin prefix.
|
||||
* @return array Parsed configuration values.
|
||||
* @throws \Cake\Core\Exception\CakeException when files don't exist or they don't contain `$config`.
|
||||
* Or when files contain '..' as this could lead to abusive reads.
|
||||
*/
|
||||
public function read(string $key): array
|
||||
{
|
||||
$file = $this->_getFilePath($key, true);
|
||||
|
||||
$config = null;
|
||||
|
||||
$return = include $file;
|
||||
if (is_array($return)) {
|
||||
return $return;
|
||||
}
|
||||
|
||||
throw new CakeException(sprintf('Config file "%s" did not return an array', $key . '.php'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the provided $data into a string of PHP code that can
|
||||
* be used saved into a file and loaded later.
|
||||
*
|
||||
* @param string $key The identifier to write to. If the key has a . it will be treated
|
||||
* as a plugin prefix.
|
||||
* @param array $data Data to dump.
|
||||
* @return bool Success
|
||||
*/
|
||||
public function dump(string $key, array $data): bool
|
||||
{
|
||||
$contents = '<?php' . "\n" . 'return ' . var_export($data, true) . ';';
|
||||
|
||||
$filename = $this->_getFilePath($key);
|
||||
|
||||
return file_put_contents($filename, $contents) > 0;
|
||||
}
|
||||
}
|
72
vendor/cakephp/core/Configure/FileConfigTrait.php
vendored
Normal file
72
vendor/cakephp/core/Configure/FileConfigTrait.php
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\Configure;
|
||||
|
||||
use Cake\Core\Exception\CakeException;
|
||||
use Cake\Core\Plugin;
|
||||
use function Cake\Core\pluginSplit;
|
||||
|
||||
/**
|
||||
* Trait providing utility methods for file based config engines.
|
||||
*/
|
||||
trait FileConfigTrait
|
||||
{
|
||||
/**
|
||||
* The path this engine finds files on.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_path = '';
|
||||
|
||||
/**
|
||||
* Get file path
|
||||
*
|
||||
* @param string $key The identifier to write to. If the key has a . it will be treated
|
||||
* as a plugin prefix.
|
||||
* @param bool $checkExists Whether to check if file exists. Defaults to false.
|
||||
* @return string Full file path
|
||||
* @throws \Cake\Core\Exception\CakeException When files don't exist or when
|
||||
* files contain '..' as this could lead to abusive reads.
|
||||
*/
|
||||
protected function _getFilePath(string $key, bool $checkExists = false): string
|
||||
{
|
||||
if (strpos($key, '..') !== false) {
|
||||
throw new CakeException('Cannot load/dump configuration files with ../ in them.');
|
||||
}
|
||||
|
||||
[$plugin, $key] = pluginSplit($key);
|
||||
|
||||
if ($plugin) {
|
||||
$file = Plugin::configPath($plugin) . $key;
|
||||
} else {
|
||||
$file = $this->_path . $key;
|
||||
}
|
||||
|
||||
$file .= $this->_extension;
|
||||
|
||||
if (!$checkExists || is_file($file)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
$realPath = realpath($file);
|
||||
if ($realPath !== false && is_file($realPath)) {
|
||||
return $realPath;
|
||||
}
|
||||
|
||||
throw new CakeException(sprintf('Could not load configuration file: %s', $file));
|
||||
}
|
||||
}
|
42
vendor/cakephp/core/ConsoleApplicationInterface.php
vendored
Normal file
42
vendor/cakephp/core/ConsoleApplicationInterface.php
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright 2005-2011, Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.5.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use Cake\Console\CommandCollection;
|
||||
|
||||
/**
|
||||
* An interface defining the methods that the
|
||||
* console runner depend on.
|
||||
*/
|
||||
interface ConsoleApplicationInterface
|
||||
{
|
||||
/**
|
||||
* Load all the application configuration and bootstrap logic.
|
||||
*
|
||||
* Override this method to add additional bootstrap logic for your application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrap(): void;
|
||||
|
||||
/**
|
||||
* Define the console commands for an application.
|
||||
*
|
||||
* @param \Cake\Console\CommandCollection $commands The CommandCollection to add commands into.
|
||||
* @return \Cake\Console\CommandCollection The updated collection.
|
||||
*/
|
||||
public function console(CommandCollection $commands): CommandCollection;
|
||||
}
|
28
vendor/cakephp/core/Container.php
vendored
Normal file
28
vendor/cakephp/core/Container.php
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 4.2.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use League\Container\Container as LeagueContainer;
|
||||
|
||||
/**
|
||||
* Dependency Injection container
|
||||
*
|
||||
* Based on the container out of League\Container
|
||||
*/
|
||||
class Container extends LeagueContainer implements ContainerInterface
|
||||
{
|
||||
}
|
45
vendor/cakephp/core/ContainerApplicationInterface.php
vendored
Normal file
45
vendor/cakephp/core/ContainerApplicationInterface.php
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 4.2.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
/**
|
||||
* Interface for applications that configure and use a dependency injection container.
|
||||
*/
|
||||
interface ContainerApplicationInterface
|
||||
{
|
||||
/**
|
||||
* Register services to the container
|
||||
*
|
||||
* Registered services can have instances fetched out of the container
|
||||
* using `get()`. Dependencies and parameters will be resolved based
|
||||
* on service definitions.
|
||||
*
|
||||
* @param \Cake\Core\ContainerInterface $container The container to add services to
|
||||
* @return void
|
||||
*/
|
||||
public function services(ContainerInterface $container): void;
|
||||
|
||||
/**
|
||||
* Create a new container and register services.
|
||||
*
|
||||
* This will `register()` services provided by both the application
|
||||
* and any plugins if the application has plugin support.
|
||||
*
|
||||
* @return \Cake\Core\ContainerInterface A populated container
|
||||
*/
|
||||
public function getContainer(): ContainerInterface;
|
||||
}
|
32
vendor/cakephp/core/ContainerInterface.php
vendored
Normal file
32
vendor/cakephp/core/ContainerInterface.php
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 4.2.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use League\Container\DefinitionContainerInterface;
|
||||
|
||||
/**
|
||||
* Interface for the Dependency Injection Container in CakePHP applications
|
||||
*
|
||||
* This interface extends the PSR-11 container interface and adds
|
||||
* methods to add services and service providers to the container.
|
||||
*
|
||||
* The methods defined in this interface use the conventions provided
|
||||
* by league/container as that is the library that CakePHP uses.
|
||||
*/
|
||||
interface ContainerInterface extends DefinitionContainerInterface
|
||||
{
|
||||
}
|
156
vendor/cakephp/core/ConventionsTrait.php
vendored
Normal file
156
vendor/cakephp/core/ConventionsTrait.php
vendored
Normal file
@ -0,0 +1,156 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use Cake\Utility\Inflector;
|
||||
|
||||
/**
|
||||
* Provides methods that allow other classes access to conventions based inflections.
|
||||
*/
|
||||
trait ConventionsTrait
|
||||
{
|
||||
/**
|
||||
* Creates a fixture name
|
||||
*
|
||||
* @param string $name Model class name
|
||||
* @return string Singular model key
|
||||
*/
|
||||
protected function _fixtureName(string $name): string
|
||||
{
|
||||
return Inflector::camelize($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the proper entity name (singular) for the specified name
|
||||
*
|
||||
* @param string $name Name
|
||||
* @return string Camelized and plural model name
|
||||
*/
|
||||
protected function _entityName(string $name): string
|
||||
{
|
||||
return Inflector::singularize(Inflector::camelize($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the proper underscored model key for associations
|
||||
*
|
||||
* If the input contains a dot, assume that the right side is the real table name.
|
||||
*
|
||||
* @param string $name Model class name
|
||||
* @return string Singular model key
|
||||
*/
|
||||
protected function _modelKey(string $name): string
|
||||
{
|
||||
[, $name] = pluginSplit($name);
|
||||
|
||||
return Inflector::underscore(Inflector::singularize($name)) . '_id';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the proper model name from a foreign key
|
||||
*
|
||||
* @param string $key Foreign key
|
||||
* @return string Model name
|
||||
*/
|
||||
protected function _modelNameFromKey(string $key): string
|
||||
{
|
||||
$key = str_replace('_id', '', $key);
|
||||
|
||||
return Inflector::camelize(Inflector::pluralize($key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the singular name for use in views.
|
||||
*
|
||||
* @param string $name Name to use
|
||||
* @return string Variable name
|
||||
*/
|
||||
protected function _singularName(string $name): string
|
||||
{
|
||||
return Inflector::variable(Inflector::singularize($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the plural variable name for views
|
||||
*
|
||||
* @param string $name Name to use
|
||||
* @return string Plural name for views
|
||||
*/
|
||||
protected function _variableName(string $name): string
|
||||
{
|
||||
return Inflector::variable($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the singular human name used in views
|
||||
*
|
||||
* @param string $name Controller name
|
||||
* @return string Singular human name
|
||||
*/
|
||||
protected function _singularHumanName(string $name): string
|
||||
{
|
||||
return Inflector::humanize(Inflector::underscore(Inflector::singularize($name)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a camelized version of $name
|
||||
*
|
||||
* @param string $name name
|
||||
* @return string Camelized name
|
||||
*/
|
||||
protected function _camelize(string $name): string
|
||||
{
|
||||
return Inflector::camelize($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the plural human name used in views
|
||||
*
|
||||
* @param string $name Controller name
|
||||
* @return string Plural human name
|
||||
*/
|
||||
protected function _pluralHumanName(string $name): string
|
||||
{
|
||||
return Inflector::humanize(Inflector::underscore($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the correct path for a plugin. Scans $pluginPaths for the plugin you want.
|
||||
*
|
||||
* @param string $pluginName Name of the plugin you want ie. DebugKit
|
||||
* @return string path path to the correct plugin.
|
||||
*/
|
||||
protected function _pluginPath(string $pluginName): string
|
||||
{
|
||||
if (Plugin::isLoaded($pluginName)) {
|
||||
return Plugin::path($pluginName);
|
||||
}
|
||||
|
||||
return current(App::path('plugins')) . $pluginName . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return plugin's namespace
|
||||
*
|
||||
* @param string $pluginName Plugin name
|
||||
* @return string Plugin's namespace
|
||||
*/
|
||||
protected function _pluginNamespace(string $pluginName): string
|
||||
{
|
||||
return str_replace('/', '\\', $pluginName);
|
||||
}
|
||||
}
|
123
vendor/cakephp/core/Exception/CakeException.php
vendored
Normal file
123
vendor/cakephp/core/Exception/CakeException.php
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
use Throwable;
|
||||
use function Cake\Core\deprecationWarning;
|
||||
|
||||
/**
|
||||
* Base class that all CakePHP Exceptions extend.
|
||||
*
|
||||
* @method int getCode() Gets the Exception code.
|
||||
*/
|
||||
class CakeException extends RuntimeException
|
||||
{
|
||||
/**
|
||||
* Array of attributes that are passed in from the constructor, and
|
||||
* made available in the view when a development error is displayed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $_attributes = [];
|
||||
|
||||
/**
|
||||
* Template string that has attributes sprintf()'ed into it.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $_messageTemplate = '';
|
||||
|
||||
/**
|
||||
* Array of headers to be passed to {@link \Cake\Http\Response::withHeader()}
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
protected $_responseHeaders;
|
||||
|
||||
/**
|
||||
* Default exception code
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $_defaultCode = 0;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Allows you to create exceptions that are treated as framework errors and disabled
|
||||
* when debug mode is off.
|
||||
*
|
||||
* @param array|string $message Either the string of the error message, or an array of attributes
|
||||
* that are made available in the view, and sprintf()'d into Exception::$_messageTemplate
|
||||
* @param int|null $code The error code
|
||||
* @param \Throwable|null $previous the previous exception.
|
||||
*/
|
||||
public function __construct($message = '', ?int $code = null, ?Throwable $previous = null)
|
||||
{
|
||||
if (is_array($message)) {
|
||||
$this->_attributes = $message;
|
||||
$message = vsprintf($this->_messageTemplate, $message);
|
||||
}
|
||||
parent::__construct($message, $code ?? $this->_defaultCode, $previous);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the passed in attributes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAttributes(): array
|
||||
{
|
||||
return $this->_attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set the response header to be used
|
||||
*
|
||||
* See also {@link \Cake\Http\Response::withHeader()}
|
||||
*
|
||||
* @param array|string|null $header A single header string or an associative
|
||||
* array of "header name" => "header value"
|
||||
* @param string|null $value The header value.
|
||||
* @return array|null
|
||||
* @deprecated 4.2.0 Use `HttpException::setHeaders()` instead. Response headers
|
||||
* should be set for HttpException only.
|
||||
*/
|
||||
public function responseHeader($header = null, $value = null): ?array
|
||||
{
|
||||
if ($header === null) {
|
||||
return $this->_responseHeaders;
|
||||
}
|
||||
|
||||
deprecationWarning(
|
||||
'Setting HTTP response headers from Exception directly is deprecated. ' .
|
||||
'If your exceptions extend Exception, they must now extend HttpException. ' .
|
||||
'You should only set HTTP headers on HttpException instances via the `setHeaders()` method.'
|
||||
);
|
||||
if (is_array($header)) {
|
||||
return $this->_responseHeaders = $header;
|
||||
}
|
||||
|
||||
return $this->_responseHeaders = [$header => $value];
|
||||
}
|
||||
}
|
||||
|
||||
// phpcs:disable
|
||||
class_alias(
|
||||
'Cake\Core\Exception\CakeException',
|
||||
'Cake\Core\Exception\Exception'
|
||||
);
|
||||
// phpcs:enable
|
21
vendor/cakephp/core/Exception/Exception.php
vendored
Normal file
21
vendor/cakephp/core/Exception/Exception.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use function Cake\Core\deprecationWarning;
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @since 4.2.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
deprecationWarning(
|
||||
'Since 4.2.0: Cake\Core\Exception\Exception is deprecated.' .
|
||||
'Use Cake\Core\Exception\CakeException instead.'
|
||||
);
|
||||
class_exists('Cake\Core\Exception\CakeException');
|
26
vendor/cakephp/core/Exception/MissingPluginException.php
vendored
Normal file
26
vendor/cakephp/core/Exception/MissingPluginException.php
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\Exception;
|
||||
|
||||
/**
|
||||
* Exception raised when a plugin could not be found
|
||||
*/
|
||||
class MissingPluginException extends CakeException
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected $_messageTemplate = 'Plugin %s could not be found.';
|
||||
}
|
43
vendor/cakephp/core/HttpApplicationInterface.php
vendored
Normal file
43
vendor/cakephp/core/HttpApplicationInterface.php
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright 2005-2011, Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.5.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use Cake\Http\MiddlewareQueue;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
/**
|
||||
* An interface defining the methods that the
|
||||
* http server depend on.
|
||||
*/
|
||||
interface HttpApplicationInterface extends RequestHandlerInterface
|
||||
{
|
||||
/**
|
||||
* Load all the application configuration and bootstrap logic.
|
||||
*
|
||||
* Override this method to add additional bootstrap logic for your application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrap(): void;
|
||||
|
||||
/**
|
||||
* Define the HTTP middleware layers for an application.
|
||||
*
|
||||
* @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to set in your App Class
|
||||
* @return \Cake\Http\MiddlewareQueue
|
||||
*/
|
||||
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue;
|
||||
}
|
312
vendor/cakephp/core/InstanceConfigTrait.php
vendored
Normal file
312
vendor/cakephp/core/InstanceConfigTrait.php
vendored
Normal file
@ -0,0 +1,312 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use Cake\Core\Exception\CakeException;
|
||||
use Cake\Utility\Hash;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* A trait for reading and writing instance config
|
||||
*
|
||||
* Implementing objects are expected to declare a `$_defaultConfig` property.
|
||||
*/
|
||||
trait InstanceConfigTrait
|
||||
{
|
||||
/**
|
||||
* Runtime config
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected $_config = [];
|
||||
|
||||
/**
|
||||
* Whether the config property has already been configured with defaults
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $_configInitialized = false;
|
||||
|
||||
/**
|
||||
* Sets the config.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* Setting a specific value:
|
||||
*
|
||||
* ```
|
||||
* $this->setConfig('key', $value);
|
||||
* ```
|
||||
*
|
||||
* Setting a nested value:
|
||||
*
|
||||
* ```
|
||||
* $this->setConfig('some.nested.key', $value);
|
||||
* ```
|
||||
*
|
||||
* Updating multiple config settings at the same time:
|
||||
*
|
||||
* ```
|
||||
* $this->setConfig(['one' => 'value', 'another' => 'value']);
|
||||
* ```
|
||||
*
|
||||
* @param array<string, mixed>|string $key The key to set, or a complete array of configs.
|
||||
* @param mixed|null $value The value to set.
|
||||
* @param bool $merge Whether to recursively merge or overwrite existing config, defaults to true.
|
||||
* @return $this
|
||||
* @throws \Cake\Core\Exception\CakeException When trying to set a key that is invalid.
|
||||
*/
|
||||
public function setConfig($key, $value = null, $merge = true)
|
||||
{
|
||||
if (!$this->_configInitialized) {
|
||||
$this->_config = $this->_defaultConfig;
|
||||
$this->_configInitialized = true;
|
||||
}
|
||||
|
||||
$this->_configWrite($key, $value, $merge);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the config.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* Reading the whole config:
|
||||
*
|
||||
* ```
|
||||
* $this->getConfig();
|
||||
* ```
|
||||
*
|
||||
* Reading a specific value:
|
||||
*
|
||||
* ```
|
||||
* $this->getConfig('key');
|
||||
* ```
|
||||
*
|
||||
* Reading a nested value:
|
||||
*
|
||||
* ```
|
||||
* $this->getConfig('some.nested.key');
|
||||
* ```
|
||||
*
|
||||
* Reading with default value:
|
||||
*
|
||||
* ```
|
||||
* $this->getConfig('some-key', 'default-value');
|
||||
* ```
|
||||
*
|
||||
* @param string|null $key The key to get or null for the whole config.
|
||||
* @param mixed $default The return value when the key does not exist.
|
||||
* @return mixed Configuration data at the named key or null if the key does not exist.
|
||||
*/
|
||||
public function getConfig(?string $key = null, $default = null)
|
||||
{
|
||||
if (!$this->_configInitialized) {
|
||||
$this->_config = $this->_defaultConfig;
|
||||
$this->_configInitialized = true;
|
||||
}
|
||||
|
||||
$return = $this->_configRead($key);
|
||||
|
||||
return $return ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the config for this specific key.
|
||||
*
|
||||
* The config value for this key must exist, it can never be null.
|
||||
*
|
||||
* @param string $key The key to get.
|
||||
* @return mixed Configuration data at the named key
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getConfigOrFail(string $key)
|
||||
{
|
||||
$config = $this->getConfig($key);
|
||||
if ($config === null) {
|
||||
throw new InvalidArgumentException(sprintf('Expected configuration `%s` not found.', $key));
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge provided config with existing config. Unlike `config()` which does
|
||||
* a recursive merge for nested keys, this method does a simple merge.
|
||||
*
|
||||
* Setting a specific value:
|
||||
*
|
||||
* ```
|
||||
* $this->configShallow('key', $value);
|
||||
* ```
|
||||
*
|
||||
* Setting a nested value:
|
||||
*
|
||||
* ```
|
||||
* $this->configShallow('some.nested.key', $value);
|
||||
* ```
|
||||
*
|
||||
* Updating multiple config settings at the same time:
|
||||
*
|
||||
* ```
|
||||
* $this->configShallow(['one' => 'value', 'another' => 'value']);
|
||||
* ```
|
||||
*
|
||||
* @param array<string, mixed>|string $key The key to set, or a complete array of configs.
|
||||
* @param mixed|null $value The value to set.
|
||||
* @return $this
|
||||
*/
|
||||
public function configShallow($key, $value = null)
|
||||
{
|
||||
if (!$this->_configInitialized) {
|
||||
$this->_config = $this->_defaultConfig;
|
||||
$this->_configInitialized = true;
|
||||
}
|
||||
|
||||
$this->_configWrite($key, $value, 'shallow');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a config key.
|
||||
*
|
||||
* @param string|null $key Key to read.
|
||||
* @return mixed
|
||||
*/
|
||||
protected function _configRead(?string $key)
|
||||
{
|
||||
if ($key === null) {
|
||||
return $this->_config;
|
||||
}
|
||||
|
||||
if (strpos($key, '.') === false) {
|
||||
return $this->_config[$key] ?? null;
|
||||
}
|
||||
|
||||
$return = $this->_config;
|
||||
|
||||
foreach (explode('.', $key) as $k) {
|
||||
if (!is_array($return) || !isset($return[$k])) {
|
||||
$return = null;
|
||||
break;
|
||||
}
|
||||
|
||||
$return = $return[$k];
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a config key.
|
||||
*
|
||||
* @param array<string, mixed>|string $key Key to write to.
|
||||
* @param mixed $value Value to write.
|
||||
* @param string|bool $merge True to merge recursively, 'shallow' for simple merge,
|
||||
* false to overwrite, defaults to false.
|
||||
* @return void
|
||||
* @throws \Cake\Core\Exception\CakeException if attempting to clobber existing config
|
||||
*/
|
||||
protected function _configWrite($key, $value, $merge = false): void
|
||||
{
|
||||
if (is_string($key) && $value === null) {
|
||||
$this->_configDelete($key);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ($merge) {
|
||||
$update = is_array($key) ? $key : [$key => $value];
|
||||
if ($merge === 'shallow') {
|
||||
$this->_config = array_merge($this->_config, Hash::expand($update));
|
||||
} else {
|
||||
$this->_config = Hash::merge($this->_config, Hash::expand($update));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_array($key)) {
|
||||
foreach ($key as $k => $val) {
|
||||
$this->_configWrite($k, $val);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strpos($key, '.') === false) {
|
||||
$this->_config[$key] = $value;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$update = &$this->_config;
|
||||
$stack = explode('.', $key);
|
||||
|
||||
foreach ($stack as $k) {
|
||||
if (!is_array($update)) {
|
||||
throw new CakeException(sprintf('Cannot set %s value', $key));
|
||||
}
|
||||
|
||||
$update[$k] = $update[$k] ?? [];
|
||||
|
||||
$update = &$update[$k];
|
||||
}
|
||||
|
||||
$update = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a single config key.
|
||||
*
|
||||
* @param string $key Key to delete.
|
||||
* @return void
|
||||
* @throws \Cake\Core\Exception\CakeException if attempting to clobber existing config
|
||||
*/
|
||||
protected function _configDelete(string $key): void
|
||||
{
|
||||
if (strpos($key, '.') === false) {
|
||||
unset($this->_config[$key]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$update = &$this->_config;
|
||||
$stack = explode('.', $key);
|
||||
$length = count($stack);
|
||||
|
||||
foreach ($stack as $i => $k) {
|
||||
if (!is_array($update)) {
|
||||
throw new CakeException(sprintf('Cannot unset %s value', $key));
|
||||
}
|
||||
|
||||
if (!isset($update[$k])) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($i === $length - 1) {
|
||||
unset($update[$k]);
|
||||
break;
|
||||
}
|
||||
|
||||
$update = &$update[$k];
|
||||
}
|
||||
}
|
||||
}
|
22
vendor/cakephp/core/LICENSE.txt
vendored
Normal file
22
vendor/cakephp/core/LICENSE.txt
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org)
|
||||
Copyright (c) 2005-2020, Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
416
vendor/cakephp/core/ObjectRegistry.php
vendored
Normal file
416
vendor/cakephp/core/ObjectRegistry.php
vendored
Normal file
@ -0,0 +1,416 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use ArrayIterator;
|
||||
use Cake\Event\EventDispatcherInterface;
|
||||
use Cake\Event\EventListenerInterface;
|
||||
use Countable;
|
||||
use IteratorAggregate;
|
||||
use RuntimeException;
|
||||
use Traversable;
|
||||
|
||||
/**
|
||||
* Acts as a registry/factory for objects.
|
||||
*
|
||||
* Provides registry & factory functionality for object types. Used
|
||||
* as a super class for various composition based re-use features in CakePHP.
|
||||
*
|
||||
* Each subclass needs to implement the various abstract methods to complete
|
||||
* the template method load().
|
||||
*
|
||||
* The ObjectRegistry is EventManager aware, but each extending class will need to use
|
||||
* \Cake\Event\EventDispatcherTrait to attach and detach on set and bind
|
||||
*
|
||||
* @see \Cake\Controller\ComponentRegistry
|
||||
* @see \Cake\View\HelperRegistry
|
||||
* @see \Cake\Console\TaskRegistry
|
||||
* @template TObject of object
|
||||
* @template-implements \IteratorAggregate<string, TObject>
|
||||
*/
|
||||
abstract class ObjectRegistry implements Countable, IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Map of loaded objects.
|
||||
*
|
||||
* @var array<object>
|
||||
* @psalm-var array<array-key, TObject>
|
||||
*/
|
||||
protected $_loaded = [];
|
||||
|
||||
/**
|
||||
* Loads/constructs an object instance.
|
||||
*
|
||||
* Will return the instance in the registry if it already exists.
|
||||
* If a subclass provides event support, you can use `$config['enabled'] = false`
|
||||
* to exclude constructed objects from being registered for events.
|
||||
*
|
||||
* Using {@link \Cake\Controller\Component::$components} as an example. You can alias
|
||||
* an object by setting the 'className' key, i.e.,
|
||||
*
|
||||
* ```
|
||||
* protected $components = [
|
||||
* 'Email' => [
|
||||
* 'className' => 'App\Controller\Component\AliasedEmailComponent'
|
||||
* ];
|
||||
* ];
|
||||
* ```
|
||||
*
|
||||
* All calls to the `Email` component would use `AliasedEmail` instead.
|
||||
*
|
||||
* @param string $name The name/class of the object to load.
|
||||
* @param array<string, mixed> $config Additional settings to use when loading the object.
|
||||
* @return mixed
|
||||
* @psalm-return TObject
|
||||
* @throws \Exception If the class cannot be found.
|
||||
*/
|
||||
public function load(string $name, array $config = [])
|
||||
{
|
||||
if (isset($config['className'])) {
|
||||
$objName = $name;
|
||||
$name = $config['className'];
|
||||
} else {
|
||||
[, $objName] = pluginSplit($name);
|
||||
}
|
||||
|
||||
$loaded = isset($this->_loaded[$objName]);
|
||||
if ($loaded && !empty($config)) {
|
||||
$this->_checkDuplicate($objName, $config);
|
||||
}
|
||||
if ($loaded) {
|
||||
return $this->_loaded[$objName];
|
||||
}
|
||||
|
||||
$className = $name;
|
||||
if (is_string($name)) {
|
||||
$className = $this->_resolveClassName($name);
|
||||
if ($className === null) {
|
||||
[$plugin, $name] = pluginSplit($name);
|
||||
$this->_throwMissingClassError($name, $plugin);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-var TObject $instance
|
||||
* @psalm-suppress PossiblyNullArgument
|
||||
**/
|
||||
$instance = $this->_create($className, $objName, $config);
|
||||
$this->_loaded[$objName] = $instance;
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for duplicate object loading.
|
||||
*
|
||||
* If a duplicate is being loaded and has different configuration, that is
|
||||
* bad and an exception will be raised.
|
||||
*
|
||||
* An exception is raised, as replacing the object will not update any
|
||||
* references other objects may have. Additionally, simply updating the runtime
|
||||
* configuration is not a good option as we may be missing important constructor
|
||||
* logic dependent on the configuration.
|
||||
*
|
||||
* @param string $name The name of the alias in the registry.
|
||||
* @param array<string, mixed> $config The config data for the new instance.
|
||||
* @return void
|
||||
* @throws \RuntimeException When a duplicate is found.
|
||||
*/
|
||||
protected function _checkDuplicate(string $name, array $config): void
|
||||
{
|
||||
$existing = $this->_loaded[$name];
|
||||
$msg = sprintf('The "%s" alias has already been loaded.', $name);
|
||||
$hasConfig = method_exists($existing, 'getConfig');
|
||||
if (!$hasConfig) {
|
||||
throw new RuntimeException($msg);
|
||||
}
|
||||
if (empty($config)) {
|
||||
return;
|
||||
}
|
||||
$existingConfig = $existing->getConfig();
|
||||
unset($config['enabled'], $existingConfig['enabled']);
|
||||
|
||||
$failure = null;
|
||||
foreach ($config as $key => $value) {
|
||||
if (!array_key_exists($key, $existingConfig)) {
|
||||
$failure = " The `{$key}` was not defined in the previous configuration data.";
|
||||
break;
|
||||
}
|
||||
if (isset($existingConfig[$key]) && $existingConfig[$key] !== $value) {
|
||||
$failure = sprintf(
|
||||
' The `%s` key has a value of `%s` but previously had a value of `%s`',
|
||||
$key,
|
||||
json_encode($value),
|
||||
json_encode($existingConfig[$key])
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($failure) {
|
||||
throw new RuntimeException($msg . $failure);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Should resolve the classname for a given object type.
|
||||
*
|
||||
* @param string $class The class to resolve.
|
||||
* @return string|null The resolved name or null for failure.
|
||||
* @psalm-return class-string|null
|
||||
*/
|
||||
abstract protected function _resolveClassName(string $class): ?string;
|
||||
|
||||
/**
|
||||
* Throw an exception when the requested object name is missing.
|
||||
*
|
||||
* @param string $class The class that is missing.
|
||||
* @param string|null $plugin The plugin $class is missing from.
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
abstract protected function _throwMissingClassError(string $class, ?string $plugin): void;
|
||||
|
||||
/**
|
||||
* Create an instance of a given classname.
|
||||
*
|
||||
* This method should construct and do any other initialization logic
|
||||
* required.
|
||||
*
|
||||
* @param object|string $class The class to build.
|
||||
* @param string $alias The alias of the object.
|
||||
* @param array<string, mixed> $config The Configuration settings for construction
|
||||
* @return object
|
||||
* @psalm-param TObject|string $class
|
||||
* @psalm-return TObject
|
||||
*/
|
||||
abstract protected function _create($class, string $alias, array $config);
|
||||
|
||||
/**
|
||||
* Get the list of loaded objects.
|
||||
*
|
||||
* @return array<string> List of object names.
|
||||
*/
|
||||
public function loaded(): array
|
||||
{
|
||||
return array_keys($this->_loaded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given object is loaded.
|
||||
*
|
||||
* @param string $name The object name to check for.
|
||||
* @return bool True is object is loaded else false.
|
||||
*/
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return isset($this->_loaded[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get loaded object instance.
|
||||
*
|
||||
* @param string $name Name of object.
|
||||
* @return object Object instance.
|
||||
* @throws \RuntimeException If not loaded or found.
|
||||
* @psalm-return TObject
|
||||
*/
|
||||
public function get(string $name)
|
||||
{
|
||||
if (!isset($this->_loaded[$name])) {
|
||||
throw new RuntimeException(sprintf('Unknown object "%s"', $name));
|
||||
}
|
||||
|
||||
return $this->_loaded[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide public read access to the loaded objects
|
||||
*
|
||||
* @param string $name Name of property to read
|
||||
* @return object|null
|
||||
* @psalm-return TObject|null
|
||||
*/
|
||||
public function __get(string $name)
|
||||
{
|
||||
return $this->_loaded[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide isset access to _loaded
|
||||
*
|
||||
* @param string $name Name of object being checked.
|
||||
* @return bool
|
||||
*/
|
||||
public function __isset(string $name): bool
|
||||
{
|
||||
return $this->has($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an object.
|
||||
*
|
||||
* @param string $name Name of a property to set.
|
||||
* @param object $object Object to set.
|
||||
* @psalm-param TObject $object
|
||||
* @return void
|
||||
*/
|
||||
public function __set(string $name, $object): void
|
||||
{
|
||||
$this->set($name, $object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets an object.
|
||||
*
|
||||
* @param string $name Name of a property to unset.
|
||||
* @return void
|
||||
*/
|
||||
public function __unset(string $name): void
|
||||
{
|
||||
$this->unload($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes an object array, creates an array that makes lazy loading
|
||||
* easier
|
||||
*
|
||||
* @param array $objects Array of child objects to normalize.
|
||||
* @return array<string, array> Array of normalized objects.
|
||||
*/
|
||||
public function normalizeArray(array $objects): array
|
||||
{
|
||||
$normal = [];
|
||||
foreach ($objects as $i => $objectName) {
|
||||
$config = [];
|
||||
if (!is_int($i)) {
|
||||
$config = (array)$objectName;
|
||||
$objectName = $i;
|
||||
}
|
||||
[, $name] = pluginSplit($objectName);
|
||||
if (isset($config['class'])) {
|
||||
$normal[$name] = $config + ['config' => []];
|
||||
} else {
|
||||
$normal[$name] = ['class' => $objectName, 'config' => $config];
|
||||
}
|
||||
}
|
||||
|
||||
return $normal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear loaded instances in the registry.
|
||||
*
|
||||
* If the registry subclass has an event manager, the objects will be detached from events as well.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
foreach (array_keys($this->_loaded) as $name) {
|
||||
$this->unload((string)$name);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an object directly into the registry by name.
|
||||
*
|
||||
* If this collection implements events, the passed object will
|
||||
* be attached into the event manager
|
||||
*
|
||||
* @param string $name The name of the object to set in the registry.
|
||||
* @param object $object instance to store in the registry
|
||||
* @return $this
|
||||
* @psalm-param TObject $object
|
||||
*/
|
||||
public function set(string $name, object $object)
|
||||
{
|
||||
[, $objName] = pluginSplit($name);
|
||||
|
||||
// Just call unload if the object was loaded before
|
||||
if (array_key_exists($name, $this->_loaded)) {
|
||||
$this->unload($name);
|
||||
}
|
||||
if ($this instanceof EventDispatcherInterface && $object instanceof EventListenerInterface) {
|
||||
$this->getEventManager()->on($object);
|
||||
}
|
||||
$this->_loaded[$objName] = $object;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an object from the registry.
|
||||
*
|
||||
* If this registry has an event manager, the object will be detached from any events as well.
|
||||
*
|
||||
* @param string $name The name of the object to remove from the registry.
|
||||
* @return $this
|
||||
*/
|
||||
public function unload(string $name)
|
||||
{
|
||||
if (empty($this->_loaded[$name])) {
|
||||
[$plugin, $name] = pluginSplit($name);
|
||||
$this->_throwMissingClassError($name, $plugin);
|
||||
}
|
||||
|
||||
$object = $this->_loaded[$name];
|
||||
if ($this instanceof EventDispatcherInterface && $object instanceof EventListenerInterface) {
|
||||
$this->getEventManager()->off($object);
|
||||
}
|
||||
unset($this->_loaded[$name]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array iterator.
|
||||
*
|
||||
* @return \Traversable
|
||||
* @psalm-return \Traversable<string, TObject>
|
||||
*/
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
return new ArrayIterator($this->_loaded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of loaded objects.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->_loaded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug friendly object properties.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function __debugInfo(): array
|
||||
{
|
||||
$properties = get_object_vars($this);
|
||||
if (isset($properties['_loaded'])) {
|
||||
$properties['_loaded'] = array_keys($properties['_loaded']);
|
||||
}
|
||||
|
||||
return $properties;
|
||||
}
|
||||
}
|
136
vendor/cakephp/core/Plugin.php
vendored
Normal file
136
vendor/cakephp/core/Plugin.php
vendored
Normal file
@ -0,0 +1,136 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 2.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
/**
|
||||
* Plugin is used to load and locate plugins.
|
||||
*
|
||||
* It also can retrieve plugin paths and load their bootstrap and routes files.
|
||||
*
|
||||
* @link https://book.cakephp.org/4/en/plugins.html
|
||||
*/
|
||||
class Plugin
|
||||
{
|
||||
/**
|
||||
* Holds a list of all loaded plugins and their configuration
|
||||
*
|
||||
* @var \Cake\Core\PluginCollection|null
|
||||
*/
|
||||
protected static $plugins;
|
||||
|
||||
/**
|
||||
* Returns the filesystem path for a plugin
|
||||
*
|
||||
* @param string $name name of the plugin in CamelCase format
|
||||
* @return string path to the plugin folder
|
||||
* @throws \Cake\Core\Exception\MissingPluginException If the folder for plugin was not found
|
||||
* or plugin has not been loaded.
|
||||
*/
|
||||
public static function path(string $name): string
|
||||
{
|
||||
$plugin = static::getCollection()->get($name);
|
||||
|
||||
return $plugin->getPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filesystem path for plugin's folder containing class files.
|
||||
*
|
||||
* @param string $name name of the plugin in CamelCase format.
|
||||
* @return string Path to the plugin folder containing class files.
|
||||
* @throws \Cake\Core\Exception\MissingPluginException If plugin has not been loaded.
|
||||
*/
|
||||
public static function classPath(string $name): string
|
||||
{
|
||||
$plugin = static::getCollection()->get($name);
|
||||
|
||||
return $plugin->getClassPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filesystem path for plugin's folder containing config files.
|
||||
*
|
||||
* @param string $name name of the plugin in CamelCase format.
|
||||
* @return string Path to the plugin folder containing config files.
|
||||
* @throws \Cake\Core\Exception\MissingPluginException If plugin has not been loaded.
|
||||
*/
|
||||
public static function configPath(string $name): string
|
||||
{
|
||||
$plugin = static::getCollection()->get($name);
|
||||
|
||||
return $plugin->getConfigPath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filesystem path for plugin's folder containing template files.
|
||||
*
|
||||
* @param string $name name of the plugin in CamelCase format.
|
||||
* @return string Path to the plugin folder containing template files.
|
||||
* @throws \Cake\Core\Exception\MissingPluginException If plugin has not been loaded.
|
||||
*/
|
||||
public static function templatePath(string $name): string
|
||||
{
|
||||
$plugin = static::getCollection()->get($name);
|
||||
|
||||
return $plugin->getTemplatePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the plugin $plugin is already loaded.
|
||||
*
|
||||
* @param string $plugin Plugin name.
|
||||
* @return bool
|
||||
* @since 3.7.0
|
||||
*/
|
||||
public static function isLoaded(string $plugin): bool
|
||||
{
|
||||
return static::getCollection()->has($plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of loaded plugins.
|
||||
*
|
||||
* @return array<string> A list of plugins that have been loaded
|
||||
*/
|
||||
public static function loaded(): array
|
||||
{
|
||||
$names = [];
|
||||
foreach (static::getCollection() as $plugin) {
|
||||
$names[] = $plugin->getName();
|
||||
}
|
||||
sort($names);
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the shared plugin collection.
|
||||
*
|
||||
* This method should generally not be used during application
|
||||
* runtime as plugins should be set during Application startup.
|
||||
*
|
||||
* @return \Cake\Core\PluginCollection
|
||||
*/
|
||||
public static function getCollection(): PluginCollection
|
||||
{
|
||||
if (!isset(static::$plugins)) {
|
||||
static::$plugins = new PluginCollection();
|
||||
}
|
||||
|
||||
return static::$plugins;
|
||||
}
|
||||
}
|
75
vendor/cakephp/core/PluginApplicationInterface.php
vendored
Normal file
75
vendor/cakephp/core/PluginApplicationInterface.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.6.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use Cake\Console\CommandCollection;
|
||||
use Cake\Event\EventDispatcherInterface;
|
||||
use Cake\Http\MiddlewareQueue;
|
||||
use Cake\Routing\RouteBuilder;
|
||||
|
||||
/**
|
||||
* Interface for Applications that leverage plugins & events.
|
||||
*
|
||||
* Events can be bound to the application event manager during
|
||||
* the application's bootstrap and plugin bootstrap.
|
||||
*/
|
||||
interface PluginApplicationInterface extends EventDispatcherInterface
|
||||
{
|
||||
/**
|
||||
* Add a plugin to the loaded plugin set.
|
||||
*
|
||||
* If the named plugin does not exist, or does not define a Plugin class, an
|
||||
* instance of `Cake\Core\BasePlugin` will be used. This generated class will have
|
||||
* all plugin hooks enabled.
|
||||
*
|
||||
* @param \Cake\Core\PluginInterface|string $name The plugin name or plugin object.
|
||||
* @param array<string, mixed> $config The configuration data for the plugin if using a string for $name
|
||||
* @return $this
|
||||
*/
|
||||
public function addPlugin($name, array $config = []);
|
||||
|
||||
/**
|
||||
* Run bootstrap logic for loaded plugins.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function pluginBootstrap(): void;
|
||||
|
||||
/**
|
||||
* Run routes hooks for loaded plugins
|
||||
*
|
||||
* @param \Cake\Routing\RouteBuilder $routes The route builder to use.
|
||||
* @return \Cake\Routing\RouteBuilder
|
||||
*/
|
||||
public function pluginRoutes(RouteBuilder $routes): RouteBuilder;
|
||||
|
||||
/**
|
||||
* Run middleware hooks for plugins
|
||||
*
|
||||
* @param \Cake\Http\MiddlewareQueue $middleware The MiddlewareQueue to use.
|
||||
* @return \Cake\Http\MiddlewareQueue
|
||||
*/
|
||||
public function pluginMiddleware(MiddlewareQueue $middleware): MiddlewareQueue;
|
||||
|
||||
/**
|
||||
* Run console hooks for plugins
|
||||
*
|
||||
* @param \Cake\Console\CommandCollection $commands The CommandCollection to use.
|
||||
* @return \Cake\Console\CommandCollection
|
||||
*/
|
||||
public function pluginConsole(CommandCollection $commands): CommandCollection;
|
||||
}
|
363
vendor/cakephp/core/PluginCollection.php
vendored
Normal file
363
vendor/cakephp/core/PluginCollection.php
vendored
Normal file
@ -0,0 +1,363 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright 2005-2011, Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.6.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use Cake\Core\Exception\CakeException;
|
||||
use Cake\Core\Exception\MissingPluginException;
|
||||
use Countable;
|
||||
use Generator;
|
||||
use InvalidArgumentException;
|
||||
use Iterator;
|
||||
|
||||
/**
|
||||
* Plugin Collection
|
||||
*
|
||||
* Holds onto plugin objects loaded into an application, and
|
||||
* provides methods for iterating, and finding plugins based
|
||||
* on criteria.
|
||||
*
|
||||
* This class implements the Iterator interface to allow plugins
|
||||
* to be iterated, handling the situation where a plugin's hook
|
||||
* method (usually bootstrap) loads another plugin during iteration.
|
||||
*
|
||||
* While its implementation supported nested iteration it does not
|
||||
* support using `continue` or `break` inside loops.
|
||||
*
|
||||
* @template-implements \Iterator<string, \Cake\Core\PluginInterface>
|
||||
*/
|
||||
class PluginCollection implements Iterator, Countable
|
||||
{
|
||||
/**
|
||||
* Plugin list
|
||||
*
|
||||
* @var array<\Cake\Core\PluginInterface>
|
||||
*/
|
||||
protected $plugins = [];
|
||||
|
||||
/**
|
||||
* Names of plugins
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $names = [];
|
||||
|
||||
/**
|
||||
* Iterator position stack.
|
||||
*
|
||||
* @var array<int>
|
||||
*/
|
||||
protected $positions = [];
|
||||
|
||||
/**
|
||||
* Loop depth
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $loopDepth = -1;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array<\Cake\Core\PluginInterface> $plugins The map of plugins to add to the collection.
|
||||
*/
|
||||
public function __construct(array $plugins = [])
|
||||
{
|
||||
foreach ($plugins as $plugin) {
|
||||
$this->add($plugin);
|
||||
}
|
||||
$this->loadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the path information stored in vendor/cakephp-plugins.php
|
||||
*
|
||||
* This file is generated by the cakephp/plugin-installer package and used
|
||||
* to locate plugins on the filesystem as applications can use `extra.plugin-paths`
|
||||
* in their composer.json file to move plugin outside of vendor/
|
||||
*
|
||||
* @internal
|
||||
* @return void
|
||||
*/
|
||||
protected function loadConfig(): void
|
||||
{
|
||||
if (Configure::check('plugins')) {
|
||||
return;
|
||||
}
|
||||
$vendorFile = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'cakephp-plugins.php';
|
||||
if (!is_file($vendorFile)) {
|
||||
$vendorFile = dirname(dirname(dirname(dirname(__DIR__)))) . DIRECTORY_SEPARATOR . 'cakephp-plugins.php';
|
||||
if (!is_file($vendorFile)) {
|
||||
Configure::write(['plugins' => []]);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$config = require $vendorFile;
|
||||
Configure::write($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate a plugin path by looking at configuration data.
|
||||
*
|
||||
* This will use the `plugins` Configure key, and fallback to enumerating `App::path('plugins')`
|
||||
*
|
||||
* This method is not part of the official public API as plugins with
|
||||
* no plugin class are being phased out.
|
||||
*
|
||||
* @param string $name The plugin name to locate a path for.
|
||||
* @return string
|
||||
* @throws \Cake\Core\Exception\MissingPluginException when a plugin path cannot be resolved.
|
||||
* @internal
|
||||
*/
|
||||
public function findPath(string $name): string
|
||||
{
|
||||
// Ensure plugin config is loaded each time. This is necessary primarily
|
||||
// for testing because the Configure::clear() call in TestCase::tearDown()
|
||||
// wipes out all configuration including plugin paths config.
|
||||
$this->loadConfig();
|
||||
|
||||
$path = Configure::read('plugins.' . $name);
|
||||
if ($path) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
$pluginPath = str_replace('/', DIRECTORY_SEPARATOR, $name);
|
||||
$paths = App::path('plugins');
|
||||
foreach ($paths as $path) {
|
||||
if (is_dir($path . $pluginPath)) {
|
||||
return $path . $pluginPath . DIRECTORY_SEPARATOR;
|
||||
}
|
||||
}
|
||||
|
||||
throw new MissingPluginException(['plugin' => $name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a plugin to the collection
|
||||
*
|
||||
* Plugins will be keyed by their names.
|
||||
*
|
||||
* @param \Cake\Core\PluginInterface $plugin The plugin to load.
|
||||
* @return $this
|
||||
*/
|
||||
public function add(PluginInterface $plugin)
|
||||
{
|
||||
$name = $plugin->getName();
|
||||
$this->plugins[$name] = $plugin;
|
||||
$this->names = array_keys($this->plugins);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a plugin from the collection if it exists.
|
||||
*
|
||||
* @param string $name The named plugin.
|
||||
* @return $this
|
||||
*/
|
||||
public function remove(string $name)
|
||||
{
|
||||
unset($this->plugins[$name]);
|
||||
$this->names = array_keys($this->plugins);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all plugins from the collection
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function clear()
|
||||
{
|
||||
$this->plugins = [];
|
||||
$this->names = [];
|
||||
$this->positions = [];
|
||||
$this->loopDepth = -1;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the named plugin exists in the collection.
|
||||
*
|
||||
* @param string $name The named plugin.
|
||||
* @return bool
|
||||
*/
|
||||
public function has(string $name): bool
|
||||
{
|
||||
return isset($this->plugins[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the a plugin by name.
|
||||
*
|
||||
* If a plugin isn't already loaded it will be autoloaded on first access
|
||||
* and that plugins loaded this way may miss some hook methods.
|
||||
*
|
||||
* @param string $name The plugin to get.
|
||||
* @return \Cake\Core\PluginInterface The plugin.
|
||||
* @throws \Cake\Core\Exception\MissingPluginException when unknown plugins are fetched.
|
||||
*/
|
||||
public function get(string $name): PluginInterface
|
||||
{
|
||||
if ($this->has($name)) {
|
||||
return $this->plugins[$name];
|
||||
}
|
||||
|
||||
$plugin = $this->create($name);
|
||||
$this->add($plugin);
|
||||
|
||||
return $plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a plugin instance from a name/classname and configuration.
|
||||
*
|
||||
* @param string $name The plugin name or classname
|
||||
* @param array<string, mixed> $config Configuration options for the plugin.
|
||||
* @return \Cake\Core\PluginInterface
|
||||
* @throws \Cake\Core\Exception\MissingPluginException When plugin instance could not be created.
|
||||
*/
|
||||
public function create(string $name, array $config = []): PluginInterface
|
||||
{
|
||||
if ($name === '') {
|
||||
throw new CakeException('Cannot create a plugin with empty name');
|
||||
}
|
||||
|
||||
if (strpos($name, '\\') !== false) {
|
||||
/** @var \Cake\Core\PluginInterface */
|
||||
return new $name($config);
|
||||
}
|
||||
|
||||
$config += ['name' => $name];
|
||||
$namespace = str_replace('/', '\\', $name);
|
||||
|
||||
$className = $namespace . '\\' . 'Plugin';
|
||||
// Check for [Vendor/]Foo/Plugin class
|
||||
if (!class_exists($className)) {
|
||||
$pos = strpos($name, '/');
|
||||
if ($pos === false) {
|
||||
$className = $namespace . '\\' . $name . 'Plugin';
|
||||
} else {
|
||||
$className = $namespace . '\\' . substr($name, $pos + 1) . 'Plugin';
|
||||
}
|
||||
|
||||
// Check for [Vendor/]Foo/FooPlugin
|
||||
if (!class_exists($className)) {
|
||||
$className = BasePlugin::class;
|
||||
if (empty($config['path'])) {
|
||||
$config['path'] = $this->findPath($name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @var class-string<\Cake\Core\PluginInterface> $className */
|
||||
return new $className($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of Countable.
|
||||
*
|
||||
* Get the number of plugins in the collection.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int
|
||||
{
|
||||
return count($this->plugins);
|
||||
}
|
||||
|
||||
/**
|
||||
* Part of Iterator Interface
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function next(): void
|
||||
{
|
||||
$this->positions[$this->loopDepth]++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Part of Iterator Interface
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function key(): string
|
||||
{
|
||||
return $this->names[$this->positions[$this->loopDepth]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Part of Iterator Interface
|
||||
*
|
||||
* @return \Cake\Core\PluginInterface
|
||||
*/
|
||||
public function current(): PluginInterface
|
||||
{
|
||||
$position = $this->positions[$this->loopDepth];
|
||||
$name = $this->names[$position];
|
||||
|
||||
return $this->plugins[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Part of Iterator Interface
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function rewind(): void
|
||||
{
|
||||
$this->positions[] = 0;
|
||||
$this->loopDepth += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Part of Iterator Interface
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function valid(): bool
|
||||
{
|
||||
$valid = isset($this->names[$this->positions[$this->loopDepth]]);
|
||||
if (!$valid) {
|
||||
array_pop($this->positions);
|
||||
$this->loopDepth -= 1;
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the plugins to those with the named hook enabled.
|
||||
*
|
||||
* @param string $hook The hook to filter plugins by
|
||||
* @return \Generator<\Cake\Core\PluginInterface> A generator containing matching plugins.
|
||||
* @throws \InvalidArgumentException on invalid hooks
|
||||
*/
|
||||
public function with(string $hook): Generator
|
||||
{
|
||||
if (!in_array($hook, PluginInterface::VALID_HOOKS, true)) {
|
||||
throw new InvalidArgumentException("The `{$hook}` hook is not a known plugin hook.");
|
||||
}
|
||||
foreach ($this as $plugin) {
|
||||
if ($plugin->isEnabled($hook)) {
|
||||
yield $plugin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
135
vendor/cakephp/core/PluginInterface.php
vendored
Normal file
135
vendor/cakephp/core/PluginInterface.php
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright 2005-2011, Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* Licensed under The MIT License
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.6.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use Cake\Console\CommandCollection;
|
||||
use Cake\Http\MiddlewareQueue;
|
||||
use Cake\Routing\RouteBuilder;
|
||||
|
||||
/**
|
||||
* Plugin Interface
|
||||
*
|
||||
* @method void services(\Cake\Core\ContainerInterface $container) Register plugin services to
|
||||
* the application's container
|
||||
*/
|
||||
interface PluginInterface
|
||||
{
|
||||
/**
|
||||
* List of valid hooks.
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
public const VALID_HOOKS = ['bootstrap', 'console', 'middleware', 'routes', 'services'];
|
||||
|
||||
/**
|
||||
* Get the name of this plugin.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string;
|
||||
|
||||
/**
|
||||
* Get the filesystem path to this plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPath(): string;
|
||||
|
||||
/**
|
||||
* Get the filesystem path to configuration for this plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getConfigPath(): string;
|
||||
|
||||
/**
|
||||
* Get the filesystem path to configuration for this plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClassPath(): string;
|
||||
|
||||
/**
|
||||
* Get the filesystem path to templates for this plugin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTemplatePath(): string;
|
||||
|
||||
/**
|
||||
* Load all the application configuration and bootstrap logic.
|
||||
*
|
||||
* The default implementation of this method will include the `config/bootstrap.php` in the plugin if it exist. You
|
||||
* can override this method to replace that behavior.
|
||||
*
|
||||
* The host application is provided as an argument. This allows you to load additional
|
||||
* plugin dependencies, or attach events.
|
||||
*
|
||||
* @param \Cake\Core\PluginApplicationInterface $app The host application
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrap(PluginApplicationInterface $app): void;
|
||||
|
||||
/**
|
||||
* Add console commands for the plugin.
|
||||
*
|
||||
* @param \Cake\Console\CommandCollection $commands The command collection to update
|
||||
* @return \Cake\Console\CommandCollection
|
||||
*/
|
||||
public function console(CommandCollection $commands): CommandCollection;
|
||||
|
||||
/**
|
||||
* Add middleware for the plugin.
|
||||
*
|
||||
* @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to update.
|
||||
* @return \Cake\Http\MiddlewareQueue
|
||||
*/
|
||||
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue;
|
||||
|
||||
/**
|
||||
* Add routes for the plugin.
|
||||
*
|
||||
* The default implementation of this method will include the `config/routes.php` in the plugin if it exists. You
|
||||
* can override this method to replace that behavior.
|
||||
*
|
||||
* @param \Cake\Routing\RouteBuilder $routes The route builder to update.
|
||||
* @return void
|
||||
*/
|
||||
public function routes(RouteBuilder $routes): void;
|
||||
|
||||
/**
|
||||
* Disables the named hook
|
||||
*
|
||||
* @param string $hook The hook to disable
|
||||
* @return $this
|
||||
*/
|
||||
public function disable(string $hook);
|
||||
|
||||
/**
|
||||
* Enables the named hook
|
||||
*
|
||||
* @param string $hook The hook to disable
|
||||
* @return $this
|
||||
*/
|
||||
public function enable(string $hook);
|
||||
|
||||
/**
|
||||
* Check if the named hook is enabled
|
||||
*
|
||||
* @param string $hook The hook to check
|
||||
* @return bool
|
||||
*/
|
||||
public function isEnabled(string $hook): bool;
|
||||
}
|
37
vendor/cakephp/core/README.md
vendored
Normal file
37
vendor/cakephp/core/README.md
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
[![Total Downloads](https://img.shields.io/packagist/dt/cakephp/core.svg?style=flat-square)](https://packagist.org/packages/cakephp/core)
|
||||
[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE.txt)
|
||||
|
||||
# CakePHP Core Classes
|
||||
|
||||
A set of classes used for configuration files reading and storing.
|
||||
This repository contains the classes that are used as glue for creating the CakePHP framework.
|
||||
|
||||
## Usage
|
||||
|
||||
You can use the `Configure` class to store arbitrary configuration data:
|
||||
|
||||
```php
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Core\Configure\Engine\PhpConfig;
|
||||
|
||||
Configure::write('Company.name','Pizza, Inc.');
|
||||
Configure::read('Company.name'); // Returns: 'Pizza, Inc.'
|
||||
```
|
||||
|
||||
It also possible to load configuration from external files:
|
||||
|
||||
```php
|
||||
Configure::config('default', new PhpConfig('/path/to/config/folder'));
|
||||
Configure::load('app', 'default', false);
|
||||
Configure::load('other_config', 'default');
|
||||
```
|
||||
|
||||
And write the configuration back into files:
|
||||
|
||||
```php
|
||||
Configure::dump('my_config', 'default');
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Please make sure you check the [official documentation](https://book.cakephp.org/4/en/development/configuration.html)
|
94
vendor/cakephp/core/Retry/CommandRetry.php
vendored
Normal file
94
vendor/cakephp/core/Retry/CommandRetry.php
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.6.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\Retry;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Allows any action to be retried in case of an exception.
|
||||
*
|
||||
* This class can be parametrized with a strategy, which will be followed
|
||||
* to determine whether the action should be retried.
|
||||
*/
|
||||
class CommandRetry
|
||||
{
|
||||
/**
|
||||
* The strategy to follow should the executed action fail.
|
||||
*
|
||||
* @var \Cake\Core\Retry\RetryStrategyInterface
|
||||
*/
|
||||
protected $strategy;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $maxRetries;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $numRetries;
|
||||
|
||||
/**
|
||||
* Creates the CommandRetry object with the given strategy and retry count
|
||||
*
|
||||
* @param \Cake\Core\Retry\RetryStrategyInterface $strategy The strategy to follow should the action fail
|
||||
* @param int $maxRetries The maximum number of retry attempts allowed
|
||||
*/
|
||||
public function __construct(RetryStrategyInterface $strategy, int $maxRetries = 1)
|
||||
{
|
||||
$this->strategy = $strategy;
|
||||
$this->maxRetries = $maxRetries;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of retries to perform in case of failure
|
||||
*
|
||||
* @param callable $action The callable action to execute with a retry strategy
|
||||
* @return mixed The return value of the passed action callable
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function run(callable $action)
|
||||
{
|
||||
$this->numRetries = 0;
|
||||
while (true) {
|
||||
try {
|
||||
return $action();
|
||||
} catch (Exception $e) {
|
||||
if (
|
||||
$this->numRetries < $this->maxRetries &&
|
||||
$this->strategy->shouldRetry($e, $this->numRetries)
|
||||
) {
|
||||
$this->numRetries++;
|
||||
continue;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last number of retry attemps.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getRetries(): int
|
||||
{
|
||||
return $this->numRetries;
|
||||
}
|
||||
}
|
35
vendor/cakephp/core/Retry/RetryStrategyInterface.php
vendored
Normal file
35
vendor/cakephp/core/Retry/RetryStrategyInterface.php
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.6.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\Retry;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Used to instruct a CommandRetry object on whether a retry
|
||||
* for an action should be performed
|
||||
*/
|
||||
interface RetryStrategyInterface
|
||||
{
|
||||
/**
|
||||
* Returns true if the action can be retried, false otherwise.
|
||||
*
|
||||
* @param \Exception $exception The exception that caused the action to fail
|
||||
* @param int $retryCount The number of times action has been retried
|
||||
* @return bool Whether it is OK to retry the action
|
||||
*/
|
||||
public function shouldRetry(Exception $exception, int $retryCount): bool;
|
||||
}
|
50
vendor/cakephp/core/ServiceConfig.php
vendored
Normal file
50
vendor/cakephp/core/ServiceConfig.php
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 4.2.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
/**
|
||||
* Read-only wrapper for configuration data
|
||||
*
|
||||
* Intended for use with {@link \Cake\Core\Container} as
|
||||
* a typehintable way for services to have application
|
||||
* configuration injected as arrays cannot be typehinted.
|
||||
*/
|
||||
class ServiceConfig
|
||||
{
|
||||
/**
|
||||
* Read a configuration key
|
||||
*
|
||||
* @param string $path The path to read.
|
||||
* @param mixed $default The default value to use if $path does not exist.
|
||||
* @return mixed The configuration data or $default value.
|
||||
*/
|
||||
public function get(string $path, $default = null)
|
||||
{
|
||||
return Configure::read($path, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if $path exists and has a non-null value.
|
||||
*
|
||||
* @param string $path The path to check.
|
||||
* @return bool True if the configuration data exists.
|
||||
*/
|
||||
public function has(string $path): bool
|
||||
{
|
||||
return Configure::check($path);
|
||||
}
|
||||
}
|
132
vendor/cakephp/core/ServiceProvider.php
vendored
Normal file
132
vendor/cakephp/core/ServiceProvider.php
vendored
Normal file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 4.2.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use League\Container\DefinitionContainerInterface;
|
||||
use League\Container\ServiceProvider\AbstractServiceProvider;
|
||||
use League\Container\ServiceProvider\BootableServiceProviderInterface;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Container ServiceProvider
|
||||
*
|
||||
* Service provider bundle related services together helping
|
||||
* to organize your application's dependencies. They also help
|
||||
* improve performance of applications with many services by
|
||||
* allowing service registration to be deferred until services are needed.
|
||||
*/
|
||||
abstract class ServiceProvider extends AbstractServiceProvider implements BootableServiceProviderInterface
|
||||
{
|
||||
/**
|
||||
* List of ids of services this provider provides.
|
||||
*
|
||||
* @var array<string>
|
||||
* @see ServiceProvider::provides()
|
||||
*/
|
||||
protected $provides = [];
|
||||
|
||||
/**
|
||||
* Get the container.
|
||||
*
|
||||
* This method's actual return type and documented return type differ
|
||||
* because PHP 7.2 doesn't support return type narrowing.
|
||||
*
|
||||
* @return \Cake\Core\ContainerInterface
|
||||
*/
|
||||
public function getContainer(): DefinitionContainerInterface
|
||||
{
|
||||
$container = parent::getContainer();
|
||||
|
||||
if (!($container instanceof ContainerInterface)) {
|
||||
$message = sprintf(
|
||||
'Unexpected container type. Expected `%s` got `%s` instead.',
|
||||
ContainerInterface::class,
|
||||
getTypeName($container)
|
||||
);
|
||||
throw new RuntimeException($message);
|
||||
}
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delegate to the bootstrap() method
|
||||
*
|
||||
* This method wraps the league/container function so users
|
||||
* only need to use the CakePHP bootstrap() interface.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
$this->bootstrap($this->getContainer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap hook for ServiceProviders
|
||||
*
|
||||
* This hook should be implemented if your service provider
|
||||
* needs to register additional service providers, load configuration
|
||||
* files or do any other work when the service provider is added to the
|
||||
* container.
|
||||
*
|
||||
* @param \Cake\Core\ContainerInterface $container The container to add services to.
|
||||
* @return void
|
||||
*/
|
||||
public function bootstrap(ContainerInterface $container): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the abstract services() method.
|
||||
*
|
||||
* This method primarily exists as a shim between the interface
|
||||
* that league/container has and the one we want to offer in CakePHP.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
$this->services($this->getContainer());
|
||||
}
|
||||
|
||||
/**
|
||||
* The provides method is a way to let the container know that a service
|
||||
* is provided by this service provider.
|
||||
*
|
||||
* Every service that is registered via this service provider must have an
|
||||
* alias added to this array or it will be ignored.
|
||||
*
|
||||
* @param string $id Identifier.
|
||||
* @return bool
|
||||
*/
|
||||
public function provides(string $id): bool
|
||||
{
|
||||
return in_array($id, $this->provides, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the services in a provider.
|
||||
*
|
||||
* All services registered in this method should also be included in the $provides
|
||||
* property so that services can be located.
|
||||
*
|
||||
* @param \Cake\Core\ContainerInterface $container The container to add services to.
|
||||
* @return void
|
||||
*/
|
||||
abstract public function services(ContainerInterface $container): void;
|
||||
}
|
325
vendor/cakephp/core/StaticConfigTrait.php
vendored
Normal file
325
vendor/cakephp/core/StaticConfigTrait.php
vendored
Normal file
@ -0,0 +1,325 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core;
|
||||
|
||||
use BadMethodCallException;
|
||||
use InvalidArgumentException;
|
||||
use LogicException;
|
||||
|
||||
/**
|
||||
* A trait that provides a set of static methods to manage configuration
|
||||
* for classes that provide an adapter facade or need to have sets of
|
||||
* configuration data registered and manipulated.
|
||||
*
|
||||
* Implementing objects are expected to declare a static `$_dsnClassMap` property.
|
||||
*/
|
||||
trait StaticConfigTrait
|
||||
{
|
||||
/**
|
||||
* Configuration sets.
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
protected static $_config = [];
|
||||
|
||||
/**
|
||||
* This method can be used to define configuration adapters for an application.
|
||||
*
|
||||
* To change an adapter's configuration at runtime, first drop the adapter and then
|
||||
* reconfigure it.
|
||||
*
|
||||
* Adapters will not be constructed until the first operation is done.
|
||||
*
|
||||
* ### Usage
|
||||
*
|
||||
* Assuming that the class' name is `Cache` the following scenarios
|
||||
* are supported:
|
||||
*
|
||||
* Setting a cache engine up.
|
||||
*
|
||||
* ```
|
||||
* Cache::setConfig('default', $settings);
|
||||
* ```
|
||||
*
|
||||
* Injecting a constructed adapter in:
|
||||
*
|
||||
* ```
|
||||
* Cache::setConfig('default', $instance);
|
||||
* ```
|
||||
*
|
||||
* Configure multiple adapters at once:
|
||||
*
|
||||
* ```
|
||||
* Cache::setConfig($arrayOfConfig);
|
||||
* ```
|
||||
*
|
||||
* @param array<string, mixed>|string $key The name of the configuration, or an array of multiple configs.
|
||||
* @param mixed $config Configuration value. Generally an array of name => configuration data for adapter.
|
||||
* @throws \BadMethodCallException When trying to modify an existing config.
|
||||
* @throws \LogicException When trying to store an invalid structured config array.
|
||||
* @return void
|
||||
*/
|
||||
public static function setConfig($key, $config = null): void
|
||||
{
|
||||
if ($config === null) {
|
||||
if (!is_array($key)) {
|
||||
throw new LogicException('If config is null, key must be an array.');
|
||||
}
|
||||
foreach ($key as $name => $settings) {
|
||||
static::setConfig($name, $settings);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isset(static::$_config[$key])) {
|
||||
/** @psalm-suppress PossiblyInvalidArgument */
|
||||
throw new BadMethodCallException(sprintf('Cannot reconfigure existing key "%s"', $key));
|
||||
}
|
||||
|
||||
if (is_object($config)) {
|
||||
$config = ['className' => $config];
|
||||
}
|
||||
|
||||
if (is_array($config) && isset($config['url'])) {
|
||||
$parsed = static::parseDsn($config['url']);
|
||||
unset($config['url']);
|
||||
$config = $parsed + $config;
|
||||
}
|
||||
|
||||
if (isset($config['engine']) && empty($config['className'])) {
|
||||
$config['className'] = $config['engine'];
|
||||
unset($config['engine']);
|
||||
}
|
||||
/** @psalm-suppress InvalidPropertyAssignmentValue */
|
||||
static::$_config[$key] = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads existing configuration.
|
||||
*
|
||||
* @param string $key The name of the configuration.
|
||||
* @return mixed|null Configuration data at the named key or null if the key does not exist.
|
||||
*/
|
||||
public static function getConfig(string $key)
|
||||
{
|
||||
return static::$_config[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads existing configuration for a specific key.
|
||||
*
|
||||
* The config value for this key must exist, it can never be null.
|
||||
*
|
||||
* @param string $key The name of the configuration.
|
||||
* @return mixed Configuration data at the named key.
|
||||
* @throws \InvalidArgumentException If value does not exist.
|
||||
*/
|
||||
public static function getConfigOrFail(string $key)
|
||||
{
|
||||
if (!isset(static::$_config[$key])) {
|
||||
throw new InvalidArgumentException(sprintf('Expected configuration `%s` not found.', $key));
|
||||
}
|
||||
|
||||
return static::$_config[$key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a constructed adapter.
|
||||
*
|
||||
* If you wish to modify an existing configuration, you should drop it,
|
||||
* change configuration and then re-add it.
|
||||
*
|
||||
* If the implementing objects supports a `$_registry` object the named configuration
|
||||
* will also be unloaded from the registry.
|
||||
*
|
||||
* @param string $config An existing configuration you wish to remove.
|
||||
* @return bool Success of the removal, returns false when the config does not exist.
|
||||
*/
|
||||
public static function drop(string $config): bool
|
||||
{
|
||||
if (!isset(static::$_config[$config])) {
|
||||
return false;
|
||||
}
|
||||
/** @psalm-suppress RedundantPropertyInitializationCheck */
|
||||
if (isset(static::$_registry)) {
|
||||
static::$_registry->unload($config);
|
||||
}
|
||||
unset(static::$_config[$config]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing the named configurations
|
||||
*
|
||||
* @return array<string> Array of configurations.
|
||||
*/
|
||||
public static function configured(): array
|
||||
{
|
||||
$configurations = array_keys(static::$_config);
|
||||
|
||||
return array_map(function ($key) {
|
||||
return (string)$key;
|
||||
}, $configurations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a DSN into a valid connection configuration
|
||||
*
|
||||
* This method allows setting a DSN using formatting similar to that used by PEAR::DB.
|
||||
* The following is an example of its usage:
|
||||
*
|
||||
* ```
|
||||
* $dsn = 'mysql://user:pass@localhost/database?';
|
||||
* $config = ConnectionManager::parseDsn($dsn);
|
||||
*
|
||||
* $dsn = 'Cake\Log\Engine\FileLog://?types=notice,info,debug&file=debug&path=LOGS';
|
||||
* $config = Log::parseDsn($dsn);
|
||||
*
|
||||
* $dsn = 'smtp://user:secret@localhost:25?timeout=30&client=null&tls=null';
|
||||
* $config = Email::parseDsn($dsn);
|
||||
*
|
||||
* $dsn = 'file:///?className=\My\Cache\Engine\FileEngine';
|
||||
* $config = Cache::parseDsn($dsn);
|
||||
*
|
||||
* $dsn = 'File://?prefix=myapp_cake_core_&serialize=true&duration=+2 minutes&path=/tmp/persistent/';
|
||||
* $config = Cache::parseDsn($dsn);
|
||||
* ```
|
||||
*
|
||||
* For all classes, the value of `scheme` is set as the value of both the `className`
|
||||
* unless they have been otherwise specified.
|
||||
*
|
||||
* Note that querystring arguments are also parsed and set as values in the returned configuration.
|
||||
*
|
||||
* @param string $dsn The DSN string to convert to a configuration array
|
||||
* @return array<string, mixed> The configuration array to be stored after parsing the DSN
|
||||
* @throws \InvalidArgumentException If not passed a string, or passed an invalid string
|
||||
*/
|
||||
public static function parseDsn(string $dsn): array
|
||||
{
|
||||
if (empty($dsn)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$pattern = <<<'REGEXP'
|
||||
{
|
||||
^
|
||||
(?P<_scheme>
|
||||
(?P<scheme>[\w\\\\]+)://
|
||||
)
|
||||
(?P<_username>
|
||||
(?P<username>.*?)
|
||||
(?P<_password>
|
||||
:(?P<password>.*?)
|
||||
)?
|
||||
@
|
||||
)?
|
||||
(?P<_host>
|
||||
(?P<host>[^?#/:@]+)
|
||||
(?P<_port>
|
||||
:(?P<port>\d+)
|
||||
)?
|
||||
)?
|
||||
(?P<_path>
|
||||
(?P<path>/[^?#]*)
|
||||
)?
|
||||
(?P<_query>
|
||||
\?(?P<query>[^#]*)
|
||||
)?
|
||||
(?P<_fragment>
|
||||
\#(?P<fragment>.*)
|
||||
)?
|
||||
$
|
||||
}x
|
||||
REGEXP;
|
||||
|
||||
preg_match($pattern, $dsn, $parsed);
|
||||
|
||||
if (!$parsed) {
|
||||
throw new InvalidArgumentException("The DSN string '{$dsn}' could not be parsed.");
|
||||
}
|
||||
|
||||
$exists = [];
|
||||
foreach ($parsed as $k => $v) {
|
||||
if (is_int($k)) {
|
||||
unset($parsed[$k]);
|
||||
} elseif (strpos($k, '_') === 0) {
|
||||
$exists[substr($k, 1)] = ($v !== '');
|
||||
unset($parsed[$k]);
|
||||
} elseif ($v === '' && !$exists[$k]) {
|
||||
unset($parsed[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
$query = '';
|
||||
|
||||
if (isset($parsed['query'])) {
|
||||
$query = $parsed['query'];
|
||||
unset($parsed['query']);
|
||||
}
|
||||
|
||||
parse_str($query, $queryArgs);
|
||||
|
||||
foreach ($queryArgs as $key => $value) {
|
||||
if ($value === 'true') {
|
||||
$queryArgs[$key] = true;
|
||||
} elseif ($value === 'false') {
|
||||
$queryArgs[$key] = false;
|
||||
} elseif ($value === 'null') {
|
||||
$queryArgs[$key] = null;
|
||||
}
|
||||
}
|
||||
|
||||
$parsed = $queryArgs + $parsed;
|
||||
|
||||
if (empty($parsed['className'])) {
|
||||
$classMap = static::getDsnClassMap();
|
||||
|
||||
$parsed['className'] = $parsed['scheme'];
|
||||
if (isset($classMap[$parsed['scheme']])) {
|
||||
/** @psalm-suppress PossiblyNullArrayOffset */
|
||||
$parsed['className'] = $classMap[$parsed['scheme']];
|
||||
}
|
||||
}
|
||||
|
||||
return $parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the DSN class map for this class.
|
||||
*
|
||||
* @param array<string, string> $map Additions/edits to the class map to apply.
|
||||
* @return void
|
||||
* @psalm-param array<string, class-string> $map
|
||||
*/
|
||||
public static function setDsnClassMap(array $map): void
|
||||
{
|
||||
static::$_dsnClassMap = $map + static::$_dsnClassMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the DSN class map for this class.
|
||||
*
|
||||
* @return array<string, string>
|
||||
* @psalm-return array<string, class-string>
|
||||
*/
|
||||
public static function getDsnClassMap(): array
|
||||
{
|
||||
return static::$_dsnClassMap;
|
||||
}
|
||||
}
|
181
vendor/cakephp/core/TestSuite/ContainerStubTrait.php
vendored
Normal file
181
vendor/cakephp/core/TestSuite/ContainerStubTrait.php
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @since 4.2.0
|
||||
* @license https://www.opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
namespace Cake\Core\TestSuite;
|
||||
|
||||
use Cake\Core\Configure;
|
||||
use Cake\Core\ContainerInterface;
|
||||
use Cake\Event\EventInterface;
|
||||
use Closure;
|
||||
use League\Container\Exception\NotFoundException;
|
||||
use LogicException;
|
||||
|
||||
/**
|
||||
* A set of methods used for defining container services
|
||||
* in test cases.
|
||||
*
|
||||
* This trait leverages the `Application.buildContainer` event
|
||||
* to inject the mocked services into the container that the
|
||||
* application uses.
|
||||
*/
|
||||
trait ContainerStubTrait
|
||||
{
|
||||
/**
|
||||
* The customized application class name.
|
||||
*
|
||||
* @psalm-var class-string<\Cake\Core\HttpApplicationInterface>|class-string<\Cake\Core\ConsoleApplicationInterface>|null
|
||||
* @var string|null
|
||||
*/
|
||||
protected $_appClass;
|
||||
|
||||
/**
|
||||
* The customized application constructor arguments.
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
protected $_appArgs;
|
||||
|
||||
/**
|
||||
* The collection of container services.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $containerServices = [];
|
||||
|
||||
/**
|
||||
* Configure the application class to use in integration tests.
|
||||
*
|
||||
* @param string $class The application class name.
|
||||
* @param array|null $constructorArgs The constructor arguments for your application class.
|
||||
* @return void
|
||||
* @psalm-param class-string<\Cake\Core\HttpApplicationInterface>|class-string<\Cake\Core\ConsoleApplicationInterface> $class
|
||||
*/
|
||||
public function configApplication(string $class, ?array $constructorArgs): void
|
||||
{
|
||||
$this->_appClass = $class;
|
||||
$this->_appArgs = $constructorArgs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an application instance.
|
||||
*
|
||||
* Uses the configuration set in `configApplication()`.
|
||||
*
|
||||
* @return \Cake\Core\HttpApplicationInterface|\Cake\Core\ConsoleApplicationInterface
|
||||
*/
|
||||
protected function createApp()
|
||||
{
|
||||
if ($this->_appClass) {
|
||||
$appClass = $this->_appClass;
|
||||
} else {
|
||||
/** @psalm-var class-string<\Cake\Http\BaseApplication> */
|
||||
$appClass = Configure::read('App.namespace') . '\Application';
|
||||
}
|
||||
if (!class_exists($appClass)) {
|
||||
throw new LogicException("Cannot load `{$appClass}` for use in integration testing.");
|
||||
}
|
||||
$appArgs = $this->_appArgs ?: [CONFIG];
|
||||
|
||||
$app = new $appClass(...$appArgs);
|
||||
if (!empty($this->containerServices) && method_exists($app, 'getEventManager')) {
|
||||
$app->getEventManager()->on('Application.buildContainer', [$this, 'modifyContainer']);
|
||||
}
|
||||
|
||||
return $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a mocked service to the container.
|
||||
*
|
||||
* When the container is created the provided classname
|
||||
* will be mapped to the factory function. The factory
|
||||
* function will be used to create mocked services.
|
||||
*
|
||||
* @param string $class The class or interface you want to define.
|
||||
* @param \Closure $factory The factory function for mocked services.
|
||||
* @return $this
|
||||
*/
|
||||
public function mockService(string $class, Closure $factory)
|
||||
{
|
||||
$this->containerServices[$class] = $factory;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a mocked service to the container.
|
||||
*
|
||||
* @param string $class The class or interface you want to remove.
|
||||
* @return $this
|
||||
*/
|
||||
public function removeMockService(string $class)
|
||||
{
|
||||
unset($this->containerServices[$class]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the application's container with one containing mocks.
|
||||
*
|
||||
* If any mocked services are defined, the application's container
|
||||
* will be replaced with one containing mocks. The original
|
||||
* container will be set as a delegate to the mock container.
|
||||
*
|
||||
* @param \Cake\Event\EventInterface $event The event
|
||||
* @param \Cake\Core\ContainerInterface $container The container to wrap.
|
||||
* @return \Cake\Core\ContainerInterface|null
|
||||
*/
|
||||
public function modifyContainer(EventInterface $event, ContainerInterface $container): ?ContainerInterface
|
||||
{
|
||||
if (empty($this->containerServices)) {
|
||||
return null;
|
||||
}
|
||||
foreach ($this->containerServices as $key => $factory) {
|
||||
if ($container->has($key)) {
|
||||
try {
|
||||
$container->extend($key)->setConcrete($factory);
|
||||
} catch (NotFoundException $e) {
|
||||
$container->add($key, $factory);
|
||||
}
|
||||
} else {
|
||||
$container->add($key, $factory);
|
||||
}
|
||||
}
|
||||
|
||||
return $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears any mocks that were defined and cleans
|
||||
* up application class configuration.
|
||||
*
|
||||
* @after
|
||||
* @return void
|
||||
*/
|
||||
public function cleanupContainer(): void
|
||||
{
|
||||
$this->_appArgs = null;
|
||||
$this->_appClass = null;
|
||||
$this->containerServices = [];
|
||||
}
|
||||
}
|
||||
|
||||
// phpcs:disable
|
||||
class_alias(
|
||||
'Cake\Core\TestSuite\ContainerStubTrait',
|
||||
'Cake\TestSuite\ContainerStubTrait'
|
||||
);
|
||||
// phpcs:enable
|
44
vendor/cakephp/core/composer.json
vendored
Normal file
44
vendor/cakephp/core/composer.json
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "cakephp/core",
|
||||
"description": "CakePHP Framework Core classes",
|
||||
"type": "library",
|
||||
"keywords": [
|
||||
"cakephp",
|
||||
"framework",
|
||||
"core"
|
||||
],
|
||||
"homepage": "https://cakephp.org",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "CakePHP Community",
|
||||
"homepage": "https://github.com/cakephp/core/graphs/contributors"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/cakephp/cakephp/issues",
|
||||
"forum": "https://stackoverflow.com/tags/cakephp",
|
||||
"irc": "irc://irc.freenode.org/cakephp",
|
||||
"source": "https://github.com/cakephp/core"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.4.0",
|
||||
"cakephp/utility": "^4.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/container-implementation": "^1.0 || ^2.0"
|
||||
},
|
||||
"suggest": {
|
||||
"cakephp/event": "To use PluginApplicationInterface or plugin applications.",
|
||||
"cakephp/cache": "To use Configure::store() and restore().",
|
||||
"league/container": "To use Container and ServiceProvider classes"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Cake\\Core\\": "."
|
||||
},
|
||||
"files": [
|
||||
"functions.php"
|
||||
]
|
||||
}
|
||||
}
|
340
vendor/cakephp/core/functions.php
vendored
Normal file
340
vendor/cakephp/core/functions.php
vendored
Normal file
@ -0,0 +1,340 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 4.5.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
// phpcs:disable PSR1.Files.SideEffects
|
||||
namespace Cake\Core;
|
||||
|
||||
if (!function_exists('Cake\Core\h')) {
|
||||
/**
|
||||
* Convenience method for htmlspecialchars.
|
||||
*
|
||||
* @param mixed $text Text to wrap through htmlspecialchars. Also works with arrays, and objects.
|
||||
* Arrays will be mapped and have all their elements escaped. Objects will be string cast if they
|
||||
* implement a `__toString` method. Otherwise, the class name will be used.
|
||||
* Other scalar types will be returned unchanged.
|
||||
* @param bool $double Encode existing html entities.
|
||||
* @param string|null $charset Character set to use when escaping.
|
||||
* Defaults to config value in `mb_internal_encoding()` or 'UTF-8'.
|
||||
* @return mixed Wrapped text.
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#h
|
||||
*/
|
||||
function h($text, bool $double = true, ?string $charset = null)
|
||||
{
|
||||
if (is_string($text)) {
|
||||
//optimize for strings
|
||||
} elseif (is_array($text)) {
|
||||
$texts = [];
|
||||
foreach ($text as $k => $t) {
|
||||
$texts[$k] = h($t, $double, $charset);
|
||||
}
|
||||
|
||||
return $texts;
|
||||
} elseif (is_object($text)) {
|
||||
if (method_exists($text, '__toString')) {
|
||||
$text = $text->__toString();
|
||||
} else {
|
||||
$text = '(object)' . get_class($text);
|
||||
}
|
||||
} elseif ($text === null || is_scalar($text)) {
|
||||
return $text;
|
||||
}
|
||||
|
||||
static $defaultCharset = false;
|
||||
if ($defaultCharset === false) {
|
||||
$defaultCharset = mb_internal_encoding() ?: 'UTF-8';
|
||||
}
|
||||
|
||||
return htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, $charset ?: $defaultCharset, $double);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('Cake\Core\pluginSplit')) {
|
||||
/**
|
||||
* Splits a dot syntax plugin name into its plugin and class name.
|
||||
* If $name does not have a dot, then index 0 will be null.
|
||||
*
|
||||
* Commonly used like
|
||||
* ```
|
||||
* list($plugin, $name) = pluginSplit($name);
|
||||
* ```
|
||||
*
|
||||
* @param string $name The name you want to plugin split.
|
||||
* @param bool $dotAppend Set to true if you want the plugin to have a '.' appended to it.
|
||||
* @param string|null $plugin Optional default plugin to use if no plugin is found. Defaults to null.
|
||||
* @return array Array with 2 indexes. 0 => plugin name, 1 => class name.
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pluginSplit
|
||||
* @psalm-return array{string|null, string}
|
||||
*/
|
||||
function pluginSplit(string $name, bool $dotAppend = false, ?string $plugin = null): array
|
||||
{
|
||||
if (strpos($name, '.') !== false) {
|
||||
$parts = explode('.', $name, 2);
|
||||
if ($dotAppend) {
|
||||
$parts[0] .= '.';
|
||||
}
|
||||
|
||||
/** @psalm-var array{string, string} */
|
||||
return $parts;
|
||||
}
|
||||
|
||||
return [$plugin, $name];
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('Cake\Core\namespaceSplit')) {
|
||||
/**
|
||||
* Split the namespace from the classname.
|
||||
*
|
||||
* Commonly used like `list($namespace, $className) = namespaceSplit($class);`.
|
||||
*
|
||||
* @param string $class The full class name, ie `Cake\Core\App`.
|
||||
* @return array<string> Array with 2 indexes. 0 => namespace, 1 => classname.
|
||||
*/
|
||||
function namespaceSplit(string $class): array
|
||||
{
|
||||
$pos = strrpos($class, '\\');
|
||||
if ($pos === false) {
|
||||
return ['', $class];
|
||||
}
|
||||
|
||||
return [substr($class, 0, $pos), substr($class, $pos + 1)];
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('Cake\Core\pr')) {
|
||||
/**
|
||||
* print_r() convenience function.
|
||||
*
|
||||
* In terminals this will act similar to using print_r() directly, when not run on CLI
|
||||
* print_r() will also wrap `<pre>` tags around the output of given variable. Similar to debug().
|
||||
*
|
||||
* This function returns the same variable that was passed.
|
||||
*
|
||||
* @param mixed $var Variable to print out.
|
||||
* @return mixed the same $var that was passed to this function
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pr
|
||||
* @see debug()
|
||||
*/
|
||||
function pr($var)
|
||||
{
|
||||
if (!Configure::read('debug')) {
|
||||
return $var;
|
||||
}
|
||||
|
||||
$template = PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' ? '<pre class="pr">%s</pre>' : "\n%s\n\n";
|
||||
printf($template, trim(print_r($var, true)));
|
||||
|
||||
return $var;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('Cake\Core\pj')) {
|
||||
/**
|
||||
* JSON pretty print convenience function.
|
||||
*
|
||||
* In terminals this will act similar to using json_encode() with JSON_PRETTY_PRINT directly, when not run on CLI
|
||||
* will also wrap `<pre>` tags around the output of given variable. Similar to pr().
|
||||
*
|
||||
* This function returns the same variable that was passed.
|
||||
*
|
||||
* @param mixed $var Variable to print out.
|
||||
* @return mixed the same $var that was passed to this function
|
||||
* @see pr()
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pj
|
||||
*/
|
||||
function pj($var)
|
||||
{
|
||||
if (!Configure::read('debug')) {
|
||||
return $var;
|
||||
}
|
||||
|
||||
$template = PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg' ? '<pre class="pj">%s</pre>' : "\n%s\n\n";
|
||||
printf($template, trim(json_encode($var, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)));
|
||||
|
||||
return $var;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('Cake\Core\env')) {
|
||||
/**
|
||||
* Gets an environment variable from available sources, and provides emulation
|
||||
* for unsupported or inconsistent environment variables (i.e. DOCUMENT_ROOT on
|
||||
* IIS, or SCRIPT_NAME in CGI mode). Also exposes some additional custom
|
||||
* environment information.
|
||||
*
|
||||
* @param string $key Environment variable name.
|
||||
* @param string|bool|null $default Specify a default value in case the environment variable is not defined.
|
||||
* @return string|bool|null Environment variable setting.
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#env
|
||||
*/
|
||||
function env(string $key, $default = null)
|
||||
{
|
||||
if ($key === 'HTTPS') {
|
||||
if (isset($_SERVER['HTTPS'])) {
|
||||
return !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
|
||||
}
|
||||
|
||||
return strpos((string)env('SCRIPT_URI'), 'https://') === 0;
|
||||
}
|
||||
|
||||
if ($key === 'SCRIPT_NAME' && env('CGI_MODE') && isset($_ENV['SCRIPT_URL'])) {
|
||||
$key = 'SCRIPT_URL';
|
||||
}
|
||||
|
||||
/** @var string|null $val */
|
||||
$val = $_SERVER[$key] ?? $_ENV[$key] ?? null;
|
||||
if ($val == null && getenv($key) !== false) {
|
||||
/** @var string|false $val */
|
||||
$val = getenv($key);
|
||||
}
|
||||
|
||||
if ($key === 'REMOTE_ADDR' && $val === env('SERVER_ADDR')) {
|
||||
$addr = env('HTTP_PC_REMOTE_ADDR');
|
||||
if ($addr !== null) {
|
||||
$val = $addr;
|
||||
}
|
||||
}
|
||||
|
||||
if ($val !== null) {
|
||||
return $val;
|
||||
}
|
||||
|
||||
switch ($key) {
|
||||
case 'DOCUMENT_ROOT':
|
||||
$name = (string)env('SCRIPT_NAME');
|
||||
$filename = (string)env('SCRIPT_FILENAME');
|
||||
$offset = 0;
|
||||
if (!strpos($name, '.php')) {
|
||||
$offset = 4;
|
||||
}
|
||||
|
||||
return substr($filename, 0, -(strlen($name) + $offset));
|
||||
case 'PHP_SELF':
|
||||
return str_replace((string)env('DOCUMENT_ROOT'), '', (string)env('SCRIPT_FILENAME'));
|
||||
case 'CGI_MODE':
|
||||
return PHP_SAPI === 'cgi';
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('Cake\Core\triggerWarning')) {
|
||||
/**
|
||||
* Triggers an E_USER_WARNING.
|
||||
*
|
||||
* @param string $message The warning message.
|
||||
* @return void
|
||||
*/
|
||||
function triggerWarning(string $message): void
|
||||
{
|
||||
$trace = debug_backtrace();
|
||||
if (isset($trace[1])) {
|
||||
$frame = $trace[1];
|
||||
$frame += ['file' => '[internal]', 'line' => '??'];
|
||||
$message = sprintf(
|
||||
'%s - %s, line: %s',
|
||||
$message,
|
||||
$frame['file'],
|
||||
$frame['line']
|
||||
);
|
||||
}
|
||||
trigger_error($message, E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('Cake\Core\deprecationWarning')) {
|
||||
/**
|
||||
* Helper method for outputting deprecation warnings
|
||||
*
|
||||
* @param string $message The message to output as a deprecation warning.
|
||||
* @param int $stackFrame The stack frame to include in the error. Defaults to 1
|
||||
* as that should point to application/plugin code.
|
||||
* @return void
|
||||
*/
|
||||
function deprecationWarning(string $message, int $stackFrame = 1): void
|
||||
{
|
||||
if (!(error_reporting() & E_USER_DEPRECATED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$trace = debug_backtrace();
|
||||
if (isset($trace[$stackFrame])) {
|
||||
$frame = $trace[$stackFrame];
|
||||
$frame += ['file' => '[internal]', 'line' => '??'];
|
||||
|
||||
// Assuming we're installed in vendor/cakephp/cakephp/src/Core/functions.php
|
||||
$root = dirname(__DIR__, 5);
|
||||
if (defined('ROOT')) {
|
||||
$root = ROOT;
|
||||
}
|
||||
$relative = str_replace(
|
||||
DIRECTORY_SEPARATOR,
|
||||
'/',
|
||||
substr($frame['file'], strlen($root) + 1)
|
||||
);
|
||||
$patterns = (array)Configure::read('Error.ignoredDeprecationPaths');
|
||||
foreach ($patterns as $pattern) {
|
||||
$pattern = str_replace(DIRECTORY_SEPARATOR, '/', $pattern);
|
||||
if (fnmatch($pattern, $relative)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$message = sprintf(
|
||||
"%s\n%s, line: %s\n" . 'You can disable all deprecation warnings by setting `Error.errorLevel` to ' .
|
||||
'`E_ALL & ~E_USER_DEPRECATED`. Adding `%s` to `Error.ignoredDeprecationPaths` ' .
|
||||
'in your `config/app.php` config will mute deprecations from that file only.',
|
||||
$message,
|
||||
$frame['file'],
|
||||
$frame['line'],
|
||||
$relative
|
||||
);
|
||||
}
|
||||
|
||||
static $errors = [];
|
||||
$checksum = md5($message);
|
||||
$duplicate = (bool)Configure::read('Error.allowDuplicateDeprecations', false);
|
||||
if (isset($errors[$checksum]) && !$duplicate) {
|
||||
return;
|
||||
}
|
||||
if (!$duplicate) {
|
||||
$errors[$checksum] = true;
|
||||
}
|
||||
|
||||
trigger_error($message, E_USER_DEPRECATED);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('Cake\Core\getTypeName')) {
|
||||
/**
|
||||
* Returns the objects class or var type of it's not an object
|
||||
*
|
||||
* @param mixed $var Variable to check
|
||||
* @return string Returns the class name or variable type
|
||||
*/
|
||||
function getTypeName($var): string
|
||||
{
|
||||
return is_object($var) ? get_class($var) : gettype($var);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Include global functions.
|
||||
*/
|
||||
if (!getenv('CAKE_DISABLE_GLOBAL_FUNCS')) {
|
||||
include 'functions_global.php';
|
||||
}
|
190
vendor/cakephp/core/functions_global.php
vendored
Normal file
190
vendor/cakephp/core/functions_global.php
vendored
Normal file
@ -0,0 +1,190 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||||
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
*
|
||||
* Licensed under The MIT License
|
||||
* For full copyright and license information, please see the LICENSE.txt
|
||||
* Redistributions of files must retain the above copyright notice.
|
||||
*
|
||||
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||||
* @link https://cakephp.org CakePHP(tm) Project
|
||||
* @since 3.0.0
|
||||
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||||
*/
|
||||
|
||||
use function Cake\Core\deprecationWarning as cakeDeprecationWarning;
|
||||
use function Cake\Core\env as cakeEnv;
|
||||
use function Cake\Core\getTypeName as cakeGetTypeName;
|
||||
use function Cake\Core\h as cakeH;
|
||||
use function Cake\Core\namespaceSplit as cakeNamespaceSplit;
|
||||
use function Cake\Core\pj as cakePj;
|
||||
use function Cake\Core\pluginSplit as cakePluginSplit;
|
||||
use function Cake\Core\pr as cakePr;
|
||||
use function Cake\Core\triggerWarning as cakeTriggerWarning;
|
||||
|
||||
if (!defined('DS')) {
|
||||
/**
|
||||
* Defines DS as short form of DIRECTORY_SEPARATOR.
|
||||
*/
|
||||
define('DS', DIRECTORY_SEPARATOR);
|
||||
}
|
||||
|
||||
if (!function_exists('h')) {
|
||||
/**
|
||||
* Convenience method for htmlspecialchars.
|
||||
*
|
||||
* @param mixed $text Text to wrap through htmlspecialchars. Also works with arrays, and objects.
|
||||
* Arrays will be mapped and have all their elements escaped. Objects will be string cast if they
|
||||
* implement a `__toString` method. Otherwise, the class name will be used.
|
||||
* Other scalar types will be returned unchanged.
|
||||
* @param bool $double Encode existing html entities.
|
||||
* @param string|null $charset Character set to use when escaping.
|
||||
* Defaults to config value in `mb_internal_encoding()` or 'UTF-8'.
|
||||
* @return mixed Wrapped text.
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#h
|
||||
*/
|
||||
function h($text, bool $double = true, ?string $charset = null)
|
||||
{
|
||||
return cakeH($text, $double, $charset);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('pluginSplit')) {
|
||||
/**
|
||||
* Splits a dot syntax plugin name into its plugin and class name.
|
||||
* If $name does not have a dot, then index 0 will be null.
|
||||
*
|
||||
* Commonly used like
|
||||
* ```
|
||||
* list($plugin, $name) = pluginSplit($name);
|
||||
* ```
|
||||
*
|
||||
* @param string $name The name you want to plugin split.
|
||||
* @param bool $dotAppend Set to true if you want the plugin to have a '.' appended to it.
|
||||
* @param string|null $plugin Optional default plugin to use if no plugin is found. Defaults to null.
|
||||
* @return array Array with 2 indexes. 0 => plugin name, 1 => class name.
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pluginSplit
|
||||
* @psalm-return array{string|null, string}
|
||||
*/
|
||||
function pluginSplit(string $name, bool $dotAppend = false, ?string $plugin = null): array
|
||||
{
|
||||
return cakePluginSplit($name, $dotAppend, $plugin);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('namespaceSplit')) {
|
||||
/**
|
||||
* Split the namespace from the classname.
|
||||
*
|
||||
* Commonly used like `list($namespace, $className) = namespaceSplit($class);`.
|
||||
*
|
||||
* @param string $class The full class name, ie `Cake\Core\App`.
|
||||
* @return array<string> Array with 2 indexes. 0 => namespace, 1 => classname.
|
||||
*/
|
||||
function namespaceSplit(string $class): array
|
||||
{
|
||||
return cakeNamespaceSplit($class);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('pr')) {
|
||||
/**
|
||||
* print_r() convenience function.
|
||||
*
|
||||
* In terminals this will act similar to using print_r() directly, when not run on CLI
|
||||
* print_r() will also wrap `<pre>` tags around the output of given variable. Similar to debug().
|
||||
*
|
||||
* This function returns the same variable that was passed.
|
||||
*
|
||||
* @param mixed $var Variable to print out.
|
||||
* @return mixed the same $var that was passed to this function
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pr
|
||||
* @see debug()
|
||||
*/
|
||||
function pr($var)
|
||||
{
|
||||
return cakePr($var);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('pj')) {
|
||||
/**
|
||||
* JSON pretty print convenience function.
|
||||
*
|
||||
* In terminals this will act similar to using json_encode() with JSON_PRETTY_PRINT directly, when not run on CLI
|
||||
* will also wrap `<pre>` tags around the output of given variable. Similar to pr().
|
||||
*
|
||||
* This function returns the same variable that was passed.
|
||||
*
|
||||
* @param mixed $var Variable to print out.
|
||||
* @return mixed the same $var that was passed to this function
|
||||
* @see pr()
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pj
|
||||
*/
|
||||
function pj($var)
|
||||
{
|
||||
return cakePj($var);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('env')) {
|
||||
/**
|
||||
* Gets an environment variable from available sources, and provides emulation
|
||||
* for unsupported or inconsistent environment variables (i.e. DOCUMENT_ROOT on
|
||||
* IIS, or SCRIPT_NAME in CGI mode). Also exposes some additional custom
|
||||
* environment information.
|
||||
*
|
||||
* @param string $key Environment variable name.
|
||||
* @param string|bool|null $default Specify a default value in case the environment variable is not defined.
|
||||
* @return string|bool|null Environment variable setting.
|
||||
* @link https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#env
|
||||
*/
|
||||
function env(string $key, $default = null)
|
||||
{
|
||||
return cakeEnv($key, $default);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('triggerWarning')) {
|
||||
/**
|
||||
* Triggers an E_USER_WARNING.
|
||||
*
|
||||
* @param string $message The warning message.
|
||||
* @return void
|
||||
*/
|
||||
function triggerWarning(string $message): void
|
||||
{
|
||||
cakeTriggerWarning($message);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('deprecationWarning')) {
|
||||
/**
|
||||
* Helper method for outputting deprecation warnings
|
||||
*
|
||||
* @param string $message The message to output as a deprecation warning.
|
||||
* @param int $stackFrame The stack frame to include in the error. Defaults to 1
|
||||
* as that should point to application/plugin code.
|
||||
* @return void
|
||||
*/
|
||||
function deprecationWarning(string $message, int $stackFrame = 1): void
|
||||
{
|
||||
cakeDeprecationWarning($message, $stackFrame + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('getTypeName')) {
|
||||
/**
|
||||
* Returns the objects class or var type of it's not an object
|
||||
*
|
||||
* @param mixed $var Variable to check
|
||||
* @return string Returns the class name or variable type
|
||||
*/
|
||||
function getTypeName($var): string
|
||||
{
|
||||
return cakeGetTypeName($var);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user