This commit is contained in:
Enoch 2024-11-05 12:10:06 +08:00
commit 6b687feee2
3995 changed files with 418583 additions and 0 deletions

21
LICENSE Normal file
View 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
View 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/* ./
```

View File

@ -0,0 +1,80 @@
<?php
namespace app\controller;
use support\Request;
use yzh52521\EasyHttp\Response;
use yzh52521\EasyHttp\RequestException;
use yzh52521\EasyHttp\Http;
use support\Db;
use Webman\Captcha\CaptchaBuilder;
class Account
{
public function login(Request $request)
{
$session = $request->session();
$user=$session->get('user',null);
if($user!=null){
return redirect('/my');
}
return view('login');
}
public function logout(Request $request)
{
$session = $request->session();
$session->delete('user');
$session->delete('realname');
return redirect('/');
}
public function my(Request $request)
{
$session = $request->session();
$user=$session->get('user',null);
$realname=$session->get('realname',null);
if($user==null||$realname==null){
return redirect('/');
}
$record=Db::table('links')->where('user', "$user")->get();
print_r($record);
return view('my', ['user' => $user,'realname' => $realname,'record' => $record]);
}
public function captcha(Request $request)
{
// 初始化验证码类
$builder = new CaptchaBuilder;
// 生成验证码
$builder->build();
// 将验证码的值存储到session中
$request->session()->set('captcha', strtolower($builder->getPhrase()));
// 获得验证码图片二进制数据
$img_content = $builder->get();
// 输出验证码二进制数据
return response($img_content, 200, ['Content-Type' => 'image/jpeg']);
}
public function loginapi(Request $request)
{
$session = $request->session();
$username = $request->input('username');
$password = $request->input('password');
$captcha = $request->input('captcha');
$captcha_session = $request->session()->get('captcha');
if(strtolower($captcha) != $captcha_session){
return json(['code' => 400, 'msg' => '验证码错误']);
}
if($username == null || $password == null){
return json(['code' => 400, 'msg' => '用户名或密码不能为空']);
}
if(strlen($username)>=36){
return json(['code' => 400, 'msg' => '用户名格式错误']);
}
$username=Db::table('users')->where('username', "$username")->where('password',"$password")->value('username');
if($username==null){
return json(['code' => 400, 'msg' => '用户名或密码错误']);
}else{
$session->set('user', $username);
$session->set('realname', Db::table('users')->where('username', "$username")->value('realname'));
return json(['code' => 200, 'msg' => '登陆成功']);
}
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace app\controller;
use support\Request;
class IndexController
{
public function index(Request $request)
{
$session = $request->session();
$user=$session->get('user',null);
$realname=$session->get('realname',null);
if($user==null||$realname==null){
$display='none';
}else{
$display='block';
}
return view('index', ['user' => $user,'realname' => $realname,'display' => $display]);
}
public function view(Request $request)
{
return view('index/view', ['name' => 'webman']);
}
public function json(Request $request)
{
return json(['code' => 0, 'msg' => 'ok']);
}
}

107
app/controller/Server.php Normal file
View File

@ -0,0 +1,107 @@
<?php
namespace app\controller;
use support\Request;
use yzh52521\EasyHttp\Response;
use yzh52521\EasyHttp\RequestException;
use yzh52521\EasyHttp\Http;
use support\Db;
class Server
{
public function short(Request $request)
{
$session = $request->session();
$url = $request->input('url',null);
$link = $request->input('link',null);
$type = $request->input('type','false');
if($link==null && Db::table('links')->where('source', "$url")->where('type',0)->exists() ){
$link=Db::table('links')->where('source', "$url")->where('type',0)->value('link');
return json(['code' => 200, 'msg' => 'success','link'=>'https://SPQR.top/'.$link]);
}
$user=$session->get('user',null);
if($user==null&&($type!='false'||$link!=null)){
return json(['code' => 505, 'msg' => '302直链和自定义链接后缀需要登陆后才可使用']);
}
$realname=$session->get('realname',null);
#$domain=parse_url($url)['host'];
$domain=$url;
$response = Http::post('https://api.uutool.cn/beian/icp/', ['site' => "$domain"])->json();
if(isset($response->data->is_icp)){
if($response->data->is_icp==1){
$icp=$response->data;
$owner=$icp->icp_org;
$webid=$icp->icp_no;
}else{
$icp=null;
if($user==null){
$owner='anonymous';
}else{
$owner=$realname;
}
$webid=null;
}
}else{
return json(['code' => 405, 'msg' => '备案查询接口响应异常,请稍后重试']);
}
if($icp==null&&($user==null||$type!='false')){
return json(['code' => 502, 'msg' => '该域名未备案,需要登陆后才可缩短,且不支持302直链']);
}
if($link==null){
for($i=4;$i>0;$i++){
$link=substr(md5($url).rand(0,pow(10,$i)),0,$i);
if(Db::table('links')->where('link', "$link")->exists()){
continue;
}else{
break;
}
}
}else{
if(Db::table('links')->where('link', "$link")->exists()){
return json(['code' => 408, 'msg' => '自定义链接已存在']);
}
}
if($user==null){
$user='anonymous';
}
if($type=='false'){
$type=0;
}elseif($type=='true'){
$type=1;
}
$data=[
'link'=>$link,
'source'=>$url,
'owner'=>$owner,
'webid'=>$webid,
'user'=>$user,
'type'=>$type,
'time'=>time()
];
Db::table('links')->insert($data);
return json(['code' => 200, 'msg' => 'success','link'=>'https://SPQR.top/'.$link]);
}
public function view(Request $request,$link)
{
$source=Db::table('links')->where('link', "$link")->first();
if($source==null){
return redirect('/');
}
if($source->type==0){
if($source->webid==null){
$ba='block';
}else{
$ba='none';
}
return view('jump', ['source' => $source,'ba'=>$ba]);
}elseif($source->type==1){
return redirect($source->source);
}
}
}

4
app/functions.php Normal file
View File

@ -0,0 +1,4 @@
<?php
/**
* Here is your custom functions.
*/

View 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
View 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;
}

232
app/view/index.html Normal file
View File

@ -0,0 +1,232 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href='normalize.css' rel='stylesheet' type='text/css'>
<link href="https://fonts.laysense.com/css/5000/zh-cn/harmonyossans/harmonyossans.css" rel="preload" as="style" onload="this.rel='stylesheet'">
<link id="theme_css" rel="stylesheet" href="sakura.css">
<style>
.pmnet { font-family: DOUYUFont;}
html {font-family: HarmonyOSSans;}
.animbox {
margin: 50px auto;
width: 200px;
text-align: center;
}
/*设置各竖条的共有样式*/
.animbox > div {
background-color: #279fcf;
width: 4px;
height: 35px;
border-radius: 2px;
margin: 2px;
animation-fill-mode: both;
display: inline-block;
animation: anim 0.9s 0s infinite cubic-bezier(.11, .49, .38, .78);
}
/*设置动画延迟*/
.animbox > :nth-child(2), .animbox > :nth-child(4) {
animation-delay: 0.25s !important;
}
.animbox > :nth-child(1), .animbox > :nth-child(5) {
animation-delay: 0.5s !important;
}
/*定义动画*/
@keyframes anim {
0% { transform: scaley(1); }
80% { transform: scaley(0.3); }
90% { transform: scaley(1); }
}
/* 开关 - 滑块周围的框 */
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
/* 隐藏默认 HTML 复选框 */
.switch input {
opacity: 0;
width: 0;
height: 0;
}
/* 滑块 */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #2fbc47;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #2196F3;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* 圆形滑块 */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
</style>
<title>SPQR短链接服务-罗马元老院与罗马人民SPQR.TOP</title>
<meta name="keywords" content="短链接,短网址,链接缩短,网址缩短,SPQR,罗马元老院,元老院">
<meta name="description" content="SPQR.top提供免费的短链接、网址缩短服务,SPQR是罗马元老院与罗马人民的缩写">
</head>
<body>
<script>
//切换深色模式
if(window.matchMedia('(prefers-color-scheme: dark)').matches){
document.getElementById('theme_css').href = 'sakura-dark.css';
}
</script>
<main>
<h1>SPQR.top 免费短链接服务 </h1><hr />
<p>可以将很长的网址链接缩短成以SPQR.top开头弘扬罗马元老院与人民精神的短链接。</p>
<a href="//laysense.cn" target="_blank_laysense">由[<span class="pmnet">来笙</span>]维护</a>
<blockquote>
SPQR.top 代表 SPQR(罗马元老院与人民) 宇宙第一
<br />
SPQR是拉丁语Senatus Populusque Romanus(罗马元老院与罗马人民)的缩写
<br />
<img src="https://wallpapercave.com/wp/wp2471066.jpg" alt="SPQR" style=" max-height: 200px;">
<br />
<span style="color: coral;">严禁将本服务用于违法目的。</span>
</blockquote>
<blockquote>
登陆后您可以使用自定义短链接后缀以及302直链功能。<br />
未备案的域名必须登陆后才能缩短且不支持302直链<br />
<?php if($user==null){
echo '<span><a href="/!/login" target="_blank_login">[登陆]</a></span>';
}else{
echo '<span>你好,'.$realname.'。<a href="/!/my" target="_blank_login">[查看我缩短的域名]</a><a href="/!/logout">[登出]</a></span>';
}
?>
</blockquote>
<section>
<h4>缩短一个网址</h4>
<hr />
<form action="new_apply.php" method="POST">
<p style="display: <?=htmlspecialchars($display)?>;">JS跳转 <label class="switch">
<input id="type" type="checkbox" name="type">
<span class="slider round"></span>
</label> 302直链[需登录]
</p>
<p>
<label for="name">需要缩短的网址链接</label>
<input required="required" name="url" id="url" placeholder="https://baidu.cn/" type="url" style="width:80%">
</p>
<p style="display: <?=htmlspecialchars($display)?>;">
<label for="password">自定义短链接后缀[需登录,留空则由系统生成]</label>
https://SPQR.top/<input name="link" id="link" type="text" style="width:30%">
</p>
<div id="load" class="animbox" style="display: none;">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<p style="color: blueviolet;" id="notice"></p>
<p style="color: skyblue;" id="notice2"></p>
</form>
<p>
<button onclick="short()" value="缩短网址" style="width:50%">缩短网址</button>
<br />
点击缩短代表您同意并接受<a href="https://www.pmnet.gq/PMNETTOS" target="_blank_tos">TOS</a>
</p>
</section>
</main>
<footer>
<hr/>
©<a href="https://laysense.cn">laysense.cn</a> 2024 Made with <a href="https://oxal.org/projects/sakura">sakura.css</a>
</footer>
<script src="jquery-3.6.3.min.js"></script>
<script>
function short(){
var url = $("#url").val();
var link = $("#link").val();
var type = $("#type").prop('checked');
$.ajax({
type: "POST",
url: '/!/short',
data: {'url':url,'link':link,'type':type},
async: true,
dataType: 'json',
cache: false,
beforeSend: function () {
$('#notice2').html('');
$('#notice').html('正在进行缩短');
$('#load').show();
},
success: function (data) {
if (data.code == 200) {
$('#notice').html(`短链接为<a href="javascript:copyToClipboard('`+data.link+`')">`+data.link+'</a>,点击复制');
} else {
$('#notice').html(data.msg);
}
$('#load').hide();
},
clearForm: true,
resetForm: false
});
}
async function copyToClipboard(text) {
try {
await navigator.clipboard.writeText(text);
$('#notice2').html('复制成功');
} catch (err) {
$('#notice2').html('复制失败', err);
}
}
</script>
</body>
</html>

93
app/view/jump.html Normal file
View File

@ -0,0 +1,93 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>正在跳转|SPQR.top 短网址跳转服务</title>
<!-- 引入Bootstrap CDN -->
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
<style>
/* 内联CSS样式 */
body {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
/**background: linear-gradient(270deg, #ff9a9e, #fad0c4);**/
background-image: url('https://cdn-dynmedia-1.microsoft.com/is/image/microsoftcorp/305616-skucards-backgroundfullbleed-1600x1132?resMode=sharp2&op_usm=1.5,0.65,15,0&wid=2000&hei=1415&qlt=100&fmt=png-alpha&fit=constrain');
background-size: 400% 400%;
animation: gradient 15s ease infinite;
}
@keyframes gradient {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
.card {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
border-radius: 10px;
}
.btn-primary {
background-color: #007bff;
border: none;
}
.btn-primary:hover {
background-color: #0056b3;
}
.btn-error {
background-color: #ff3700;
border: none;
}
.btn-error:hover {
background-color: #b70404;
}
.loader {
border: 4px solid #f3f3f3;
border-radius: 50%;
border-top: 4px solid #3498db;
width: 48px;
height: 48px;
animation: spin 2s linear infinite;
margin: 0 auto 15px auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="card text-center">
<div class="card-body">
<img src="https://wallpapercave.com/wp/wp2471066.jpg" style="max-width: 168px;"/>
<h2 class="card-title" id="title">正在前往目标站点</h2>
<p class="card-text"><a href="//spqr.top" target="_blank">SPQR.top</a> 提供网址链接缩短服务。</p>
<p class="card-text">目标网站由 <span><?php echo($source->owner); ?></span> 提供 <br />
我们不对其内容的真实性和合法性负责。
</p>
<p class="card-text" style="display: <?php echo($ba);?>;">⚠️该网站未备案,可能存在违规内容,请谨慎访问!⚠️</p>
<div id="loader" class="loader"></div>
<a id="msg" class="btn "></a>
</div>
</div>
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.7.1.js" ></script>
<script type="text/javascript">
let min = 2000;
let max = 6000;
let randomIntInRange = Math.floor(Math.random() * (max - min + 1)) + min;
setTimeout(function(){
var goto='<?php echo $source->source; ?>';
$("#msg").attr("href", goto);
$("#msg").html('若您的浏览器没有反应,请点击此处');
$("#msg").addClass("btn-primary");
$("#msg").show();
window.location.href=goto;
}, randomIntInRange);
</script>
</body>
</html>

71
app/view/login.html Normal file
View File

@ -0,0 +1,71 @@
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登录 SPQR.top</title>
<link href='normalize.css' rel='stylesheet' type='text/css'>
<link href="https://fonts.laysense.com/css/5000/zh-cn/harmonyossans/harmonyossans.css" rel="preload" as="style" onload="this.rel='stylesheet'">
<link id="theme_css" rel="stylesheet" href="/sakura.css"></head>
<body>
<script>
//切换深色模式
if(window.matchMedia('(prefers-color-scheme: dark)').matches){
document.getElementById('theme_css').href = '/sakura-dark.css';
}
</script>
<div class="login-container">
<h2>登录 SPQR.top</h2>
<form action="/login" method="post">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required>
<label for="password">密码:</label>
<input type="password" id="password" name="password" required>
<label for="captcha">验证码:</label>
<img src="/!/captcha" alt="Captcha Image" id="captcha-image" onclick="change()"><br />
<input type="text" id="captcha" name="captcha" required>
<br />
<button type="submit">登入</button>
</form>
<hr />
<a href="/">[返回首页]</a>
</div>
</body>
</html>
<script src="/jquery-3.6.3.min.js"></script>
<script>
function change() {
document.getElementById('captcha-image').src = '/!/captcha?' + Math.random();
}
$(document).ready(function() {
$('form').on('submit', function(event) {
event.preventDefault();
var formData = {
username: $('#username').val(),
password: $('#password').val(),
captcha: $('#captcha').val()
};
$.ajax({
type: 'POST',
url: '/!/loginapi',
data: JSON.stringify(formData),
contentType: 'application/json',
success: function(response) {
if(response.code == 200) {
window.location.href = '/my';
} else {
alert('登陆失败:'+response.msg);
change();
}
},
error: function(error) {
alert('登陆失败:接口错误');
change();
}
});
});
});
</script>

66
app/view/my.html Normal file
View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Short Links</title>
<link id="theme_css" rel="stylesheet" href="/sakura.css"></head>
<link href="https://cdn.bootcdn.net/ajax/libs/datatables/1.10.21/css/jquery.dataTables.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/datatables/1.10.21/js/jquery.dataTables.min.js"></script>
</head>
<body>
<script>
//切换深色模式
if(window.matchMedia('(prefers-color-scheme: dark)').matches){
document.getElementById('theme_css').href = '/sakura-dark.css';
}
</script>
<h1>当前的短链接</h1>
<p>用户名:<?=htmlspecialchars($user)?> <?=htmlspecialchars($realname)?><a href="/logout">[登出]</a> <a href="/">[回到首页]</a></p>
<hr />
<table id="shortLinksTable" class="display" style="width: 100%; table-layout: fixed; word-break: break-all;color:black">
<thead>
<tr>
<th>短链接</th>
<th>原始域名</th>
<th>备案号/负责人</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<style>
.sourcelink {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 200px;
}
</style>
<?php
foreach($record as $rec){
echo '<tr>';
echo '<td>SPQR.top/'.$rec->link.'</td>';
echo '<td class="sourcelink">'.$rec->source .'</td>';
if($rec->webid == null) {
echo '<td>未备案</td>';
} else {
echo '<td>' . $rec->webid . '<br />'.$rec->owner.'</td>';
}
echo '<td><a style="color: red;" href="/!/delete/'. $rec->link . '">[删除]</a></td>';
echo '</tr>';
}
?>
</tbody>
</table>
<script>
$(document).ready(function() {
$('#shortLinksTable').DataTable();
});
</script>
</body>
</html>

71
composer.json Normal file
View 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

File diff suppressed because it is too large Load Diff

26
config/app.php Normal file
View 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
View 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
View 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
View 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;

36
config/database.php Normal file
View File

@ -0,0 +1,36 @@
<?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' => 'laysense.mysql.database.azure.com',
'port' => 3306,
'database' => 'SPQR',
'username' => 'laysense',
'password' => 'qi2005112',
'unix_socket' => '',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => null
],
],
];

15
config/dependence.php Normal file
View 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
View File

@ -0,0 +1,5 @@
<?php
return [
];

17
config/exception.php Normal file
View 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
View 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
View 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 [];

View File

@ -0,0 +1,4 @@
<?php
return [
'enable' => true,
];

View 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,
];

View File

@ -0,0 +1,7 @@
<?php
use Webman\Event\EventListCommand;
return [
EventListCommand::class
];

42
config/process.php Normal file
View 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
View 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,
],
];

31
config/route.php Normal file
View 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
*/
use Webman\Route;
Route::any('/', [app\controller\IndexController::class, 'index']);
Route::any('/!/short', [app\controller\Server::class, 'short']);
Route::any('/!/login', [app\controller\Account::class, 'login']);
Route::any('/!/loginapi', [app\controller\Account::class, 'loginapi']);
Route::any('/!/my', [app\controller\Account::class, 'my']);
Route::any('/!/logout', [app\controller\Account::class, 'logout']);
Route::any('/!/captcha', [app\controller\Account::class, 'captcha']);
Route::any('/{link}', [app\controller\Server::class, 'view']);
Route::fallback(function(){
return redirect('/');
});

31
config/server.php Normal file
View 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:8780',
'transport' => 'tcp',
'context' => [],
'name' => 'webman',
'count' => cpu_count() * 4,
'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
View 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
View 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
View 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
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

2
public/jquery-3.6.3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

349
public/normalize.css vendored Normal file
View File

@ -0,0 +1,349 @@
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
/* Document
========================================================================== */
/**
* 1. Correct the line height in all browsers.
* 2. Prevent adjustments of font size after orientation changes in iOS.
*/
html {
line-height: 1.15; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/* Sections
========================================================================== */
/**
* Remove the margin in all browsers.
*/
body {
margin: 0;
}
/**
* Render the `main` element consistently in IE.
*/
main {
display: block;
}
/**
* Correct the font size and margin on `h1` elements within `section` and
* `article` contexts in Chrome, Firefox, and Safari.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/* Grouping content
========================================================================== */
/**
* 1. Add the correct box sizing in Firefox.
* 2. Show the overflow in Edge and IE.
*/
hr {
box-sizing: content-box; /* 1 */
height: 0; /* 1 */
overflow: visible; /* 2 */
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
pre {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/* Text-level semantics
========================================================================== */
/**
* Remove the gray background on active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* 1. Remove the bottom border in Chrome 57-
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
*/
abbr[title] {
border-bottom: none; /* 1 */
text-decoration: underline; /* 2 */
text-decoration: underline dotted; /* 2 */
}
/**
* Add the correct font weight in Chrome, Edge, and Safari.
*/
b,
strong {
font-weight: bolder;
}
/**
* 1. Correct the inheritance and scaling of font size in all browsers.
* 2. Correct the odd `em` font sizing in all browsers.
*/
code,
kbd,
samp {
font-family: monospace, monospace; /* 1 */
font-size: 1em; /* 2 */
}
/**
* Add the correct font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` elements from affecting the line height in
* all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
/* Embedded content
========================================================================== */
/**
* Remove the border on images inside links in IE 10.
*/
img {
border-style: none;
}
/* Forms
========================================================================== */
/**
* 1. Change the font styles in all browsers.
* 2. Remove the margin in Firefox and Safari.
*/
button,
input,
optgroup,
select,
textarea {
font-family: inherit; /* 1 */
font-size: 100%; /* 1 */
line-height: 1.15; /* 1 */
margin: 0; /* 2 */
}
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
button,
input { /* 1 */
overflow: visible;
}
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
button,
select { /* 1 */
text-transform: none;
}
/**
* Correct the inability to style clickable types in iOS and Safari.
*/
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
/**
* Remove the inner border and padding in Firefox.
*/
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
/**
* Restore the focus styles unset by the previous rule.
*/
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
/**
* Correct the padding in Firefox.
*/
fieldset {
padding: 0.35em 0.75em 0.625em;
}
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
legend {
box-sizing: border-box; /* 1 */
color: inherit; /* 2 */
display: table; /* 1 */
max-width: 100%; /* 1 */
padding: 0; /* 3 */
white-space: normal; /* 1 */
}
/**
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
*/
progress {
vertical-align: baseline;
}
/**
* Remove the default vertical scrollbar in IE 10+.
*/
textarea {
overflow: auto;
}
/**
* 1. Add the correct box sizing in IE 10.
* 2. Remove the padding in IE 10.
*/
[type="checkbox"],
[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
[type="search"] {
-webkit-appearance: textfield; /* 1 */
outline-offset: -2px; /* 2 */
}
/**
* Remove the inner padding in Chrome and Safari on macOS.
*/
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
::-webkit-file-upload-button {
-webkit-appearance: button; /* 1 */
font: inherit; /* 2 */
}
/* Interactive
========================================================================== */
/*
* Add the correct display in Edge, IE 10+, and Firefox.
*/
details {
display: block;
}
/*
* Add the correct display in all browsers.
*/
summary {
display: list-item;
}
/* Misc
========================================================================== */
/**
* Add the correct display in IE 10+.
*/
template {
display: none;
}
/**
* Add the correct display in IE 10.
*/
[hidden] {
display: none;
}

223
public/sakura-dark.css Normal file
View File

@ -0,0 +1,223 @@
/* $color-text: #dedce5; */
/* Sakura.css v1.4.1
* ================
* Minimal css theme.
* Project: https://github.com/oxalorg/sakura/
*/
/* Body */
html {
font-size: 62.5%;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
}
body {
font-size: 1.8rem;
line-height: 1.618;
max-width: 38em;
margin: auto;
color: #c9c9c9;
background-color: #222222;
padding: 13px;
}
@media (max-width: 684px) {
body {
font-size: 1.53rem;
}
}
@media (max-width: 382px) {
body {
font-size: 1.35rem;
}
}
h1, h2, h3, h4, h5, h6 {
line-height: 1.1;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
font-weight: 700;
margin-top: 3rem;
margin-bottom: 1.5rem;
overflow-wrap: break-word;
word-wrap: break-word;
-ms-word-break: break-all;
word-break: break-word;
}
h1 {
font-size: 2.35em;
}
h2 {
font-size: 2em;
}
h3 {
font-size: 1.75em;
}
h4 {
font-size: 1.5em;
}
h5 {
font-size: 1.25em;
}
h6 {
font-size: 1em;
}
p {
margin-top: 0px;
margin-bottom: 2.5rem;
}
small, sub, sup {
font-size: 75%;
}
hr {
border-color: #ffffff;
}
a {
text-decoration: none;
color: #ffffff;
}
a:visited {
color: #e6e6e6;
}
a:hover {
color: #c9c9c9;
border-bottom: 2px solid #c9c9c9;
}
ul {
padding-left: 1.4em;
margin-top: 0px;
margin-bottom: 2.5rem;
}
li {
margin-bottom: 0.4em;
}
blockquote {
margin-left: 0px;
margin-right: 0px;
padding-left: 1em;
padding-top: 0.8em;
padding-bottom: 0.8em;
padding-right: 0.8em;
border-left: 5px solid #ffffff;
margin-bottom: 2.5rem;
background-color: #4a4a4a;
}
blockquote p {
margin-bottom: 0;
}
img, video {
height: auto;
max-width: 100%;
margin-top: 0px;
margin-bottom: 2.5rem;
}
/* Pre and Code */
pre {
background-color: #4a4a4a;
display: block;
padding: 1em;
overflow-x: auto;
margin-top: 0px;
margin-bottom: 2.5rem;
font-size: 0.9em;
}
code, kbd, samp {
font-size: 0.9em;
padding: 0 0.5em;
background-color: #4a4a4a;
white-space: pre-wrap;
}
pre > code {
padding: 0;
background-color: transparent;
white-space: pre;
font-size: 1em;
}
/* Tables */
table {
text-align: justify;
width: 100%;
border-collapse: collapse;
}
td, th {
padding: 0.5em;
border-bottom: 1px solid #4a4a4a;
}
/* Buttons, forms and input */
input, textarea {
border: 1px solid #c9c9c9;
}
input:focus, textarea:focus {
border: 1px solid #ffffff;
}
textarea {
width: 100%;
}
.button, button, input[type=submit], input[type=reset], input[type=button] {
display: inline-block;
padding: 5px 10px;
text-align: center;
text-decoration: none;
white-space: nowrap;
background-color: #ffffff;
color: #222222;
border-radius: 1px;
border: 1px solid #ffffff;
cursor: pointer;
box-sizing: border-box;
}
.button[disabled], button[disabled], input[type=submit][disabled], input[type=reset][disabled], input[type=button][disabled] {
cursor: default;
opacity: 0.5;
}
.button:focus:enabled, .button:hover:enabled, button:focus:enabled, button:hover:enabled, input[type=submit]:focus:enabled, input[type=submit]:hover:enabled, input[type=reset]:focus:enabled, input[type=reset]:hover:enabled, input[type=button]:focus:enabled, input[type=button]:hover:enabled {
background-color: #c9c9c9;
border-color: #c9c9c9;
color: #222222;
outline: 0;
}
textarea, select, input {
color: #c9c9c9;
padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */
margin-bottom: 10px;
background-color: #4a4a4a;
border: 1px solid #4a4a4a;
border-radius: 4px;
box-shadow: none;
box-sizing: border-box;
}
textarea:focus, select:focus, input:focus {
border: 1px solid #ffffff;
outline: 0;
}
input[type=checkbox]:focus {
outline: 1px dotted #ffffff;
}
label, legend, fieldset {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
}

222
public/sakura.css Normal file
View File

@ -0,0 +1,222 @@
/* Sakura.css v1.4.1
* ================
* Minimal css theme.
* Project: https://github.com/oxalorg/sakura/
*/
/* Body */
html {
font-size: 62.5%;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
}
body {
font-size: 1.8rem;
line-height: 1.618;
max-width: 38em;
margin: auto;
color: #4a4a4a;
background-color: #f9f9f9;
padding: 13px;
}
@media (max-width: 684px) {
body {
font-size: 1.53rem;
}
}
@media (max-width: 382px) {
body {
font-size: 1.35rem;
}
}
h1, h2, h3, h4, h5, h6 {
line-height: 1.1;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
font-weight: 700;
margin-top: 3rem;
margin-bottom: 1.5rem;
overflow-wrap: break-word;
word-wrap: break-word;
-ms-word-break: break-all;
word-break: break-word;
}
h1 {
font-size: 2.35em;
}
h2 {
font-size: 2em;
}
h3 {
font-size: 1.75em;
}
h4 {
font-size: 1.5em;
}
h5 {
font-size: 1.25em;
}
h6 {
font-size: 1em;
}
p {
margin-top: 0px;
margin-bottom: 2.5rem;
}
small, sub, sup {
font-size: 75%;
}
hr {
border-color: #1d7484;
}
a {
text-decoration: none;
color: #1d7484;
}
a:visited {
color: #144f5a;
}
a:hover {
color: #982c61;
border-bottom: 2px solid #4a4a4a;
}
ul {
padding-left: 1.4em;
margin-top: 0px;
margin-bottom: 2.5rem;
}
li {
margin-bottom: 0.4em;
}
blockquote {
margin-left: 0px;
margin-right: 0px;
padding-left: 1em;
padding-top: 0.8em;
padding-bottom: 0.8em;
padding-right: 0.8em;
border-left: 5px solid #1d7484;
margin-bottom: 2.5rem;
background-color: #f1f1f1;
}
blockquote p {
margin-bottom: 0;
}
img, video {
height: auto;
max-width: 100%;
margin-top: 0px;
margin-bottom: 2.5rem;
}
/* Pre and Code */
pre {
background-color: #f1f1f1;
display: block;
padding: 1em;
overflow-x: auto;
margin-top: 0px;
margin-bottom: 2.5rem;
font-size: 0.9em;
}
code, kbd, samp {
font-size: 0.9em;
padding: 0 0.5em;
background-color: #f1f1f1;
white-space: pre-wrap;
}
pre > code {
padding: 0;
background-color: transparent;
white-space: pre;
font-size: 1em;
}
/* Tables */
table {
text-align: justify;
width: 100%;
border-collapse: collapse;
}
td, th {
padding: 0.5em;
border-bottom: 1px solid #f1f1f1;
}
/* Buttons, forms and input */
input, textarea {
border: 1px solid #4a4a4a;
}
input:focus, textarea:focus {
border: 1px solid #1d7484;
}
textarea {
width: 100%;
}
.button, button, input[type=submit], input[type=reset], input[type=button] {
display: inline-block;
padding: 5px 10px;
text-align: center;
text-decoration: none;
white-space: nowrap;
background-color: #1d7484;
color: #f9f9f9;
border-radius: 1px;
border: 1px solid #1d7484;
cursor: pointer;
box-sizing: border-box;
}
.button[disabled], button[disabled], input[type=submit][disabled], input[type=reset][disabled], input[type=button][disabled] {
cursor: default;
opacity: 0.5;
}
.button:focus:enabled, .button:hover:enabled, button:focus:enabled, button:hover:enabled, input[type=submit]:focus:enabled, input[type=submit]:hover:enabled, input[type=reset]:focus:enabled, input[type=reset]:hover:enabled, input[type=button]:focus:enabled, input[type=button]:hover:enabled {
background-color: #982c61;
border-color: #982c61;
color: #f9f9f9;
outline: 0;
}
textarea, select, input {
color: #4a4a4a;
padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */
margin-bottom: 10px;
background-color: #f1f1f1;
border: 1px solid #f1f1f1;
border-radius: 4px;
box-shadow: none;
box-sizing: border-box;
}
textarea:focus, select:focus, input:focus {
border: 1px solid #1d7484;
outline: 0;
}
input[type=checkbox]:focus {
outline: 1px dotted #1d7484;
}
label, legend, fieldset {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
}

View File

@ -0,0 +1,374 @@
[2024-11-04 19:39:05] default.ERROR: 127.0.0.1 POST localhost:8780/!/short
PDOException: could not find driver in /www/SPQRjump/vendor/illuminate/database/Connectors/Connector.php:70
Stack trace:
#0 /www/SPQRjump/vendor/illuminate/database/Connectors/Connector.php(70): PDO->__construct()
#1 /www/SPQRjump/vendor/illuminate/database/Connectors/Connector.php(46): Illuminate\Database\Connectors\Connector->createPdoConnection()
#2 /www/SPQRjump/vendor/illuminate/database/Connectors/MySqlConnector.php(24): Illuminate\Database\Connectors\Connector->createConnection()
#3 /www/SPQRjump/vendor/illuminate/database/Connectors/ConnectionFactory.php(184): Illuminate\Database\Connectors\MySqlConnector->connect()
#4 [internal function]: Illuminate\Database\Connectors\ConnectionFactory->Illuminate\Database\Connectors\{closure}()
#5 /www/SPQRjump/vendor/illuminate/database/Connection.php(1181): call_user_func()
#6 /www/SPQRjump/vendor/illuminate/database/Connection.php(1217): Illuminate\Database\Connection->getPdo()
#7 /www/SPQRjump/vendor/illuminate/database/Connection.php(486): Illuminate\Database\Connection->getReadPdo()
#8 /www/SPQRjump/vendor/illuminate/database/Connection.php(414): Illuminate\Database\Connection->getPdoForSelect()
#9 /www/SPQRjump/vendor/illuminate/database/Connection.php(753): Illuminate\Database\Connection->Illuminate\Database\{closure}()
#10 /www/SPQRjump/vendor/illuminate/database/Connection.php(720): Illuminate\Database\Connection->runQueryCallback()
#11 /www/SPQRjump/vendor/illuminate/database/Connection.php(422): Illuminate\Database\Connection->run()
#12 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(3030): Illuminate\Database\Connection->select()
#13 /www/SPQRjump/app/controller/Server.php(49): Illuminate\Database\Query\Builder->exists()
#14 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Server->short()
#15 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#16 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#17 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#18 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#19 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#20 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#21 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#22 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#23 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
#24 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
#25 /www/SPQRjump/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
#26 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#27 /www/SPQRjump/start.php(4): support\App::run()
#28 {main}
Next Illuminate\Database\QueryException: could not find driver (SQL: select exists(select * from `links` where `link` = 6271) as `exists`) in /www/SPQRjump/vendor/illuminate/database/Connection.php:760
Stack trace:
#0 /www/SPQRjump/vendor/illuminate/database/Connection.php(720): Illuminate\Database\Connection->runQueryCallback()
#1 /www/SPQRjump/vendor/illuminate/database/Connection.php(422): Illuminate\Database\Connection->run()
#2 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(3030): Illuminate\Database\Connection->select()
#3 /www/SPQRjump/app/controller/Server.php(49): Illuminate\Database\Query\Builder->exists()
#4 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Server->short()
#5 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#6 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#7 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#8 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#9 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#10 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#11 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#12 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#13 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
#14 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
#15 /www/SPQRjump/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
#16 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#17 /www/SPQRjump/start.php(4): support\App::run()
#18 {main} [] []
[2024-11-04 19:42:51] default.ERROR: 127.0.0.1 POST localhost:8780/!/short
PDOException: could not find driver in /www/SPQRjump/vendor/illuminate/database/Connectors/Connector.php:70
Stack trace:
#0 /www/SPQRjump/vendor/illuminate/database/Connectors/Connector.php(70): PDO->__construct()
#1 /www/SPQRjump/vendor/illuminate/database/Connectors/Connector.php(46): Illuminate\Database\Connectors\Connector->createPdoConnection()
#2 /www/SPQRjump/vendor/illuminate/database/Connectors/MySqlConnector.php(24): Illuminate\Database\Connectors\Connector->createConnection()
#3 /www/SPQRjump/vendor/illuminate/database/Connectors/ConnectionFactory.php(184): Illuminate\Database\Connectors\MySqlConnector->connect()
#4 [internal function]: Illuminate\Database\Connectors\ConnectionFactory->Illuminate\Database\Connectors\{closure}()
#5 /www/SPQRjump/vendor/illuminate/database/Connection.php(1181): call_user_func()
#6 /www/SPQRjump/vendor/illuminate/database/Connection.php(1217): Illuminate\Database\Connection->getPdo()
#7 /www/SPQRjump/vendor/illuminate/database/Connection.php(486): Illuminate\Database\Connection->getReadPdo()
#8 /www/SPQRjump/vendor/illuminate/database/Connection.php(414): Illuminate\Database\Connection->getPdoForSelect()
#9 /www/SPQRjump/vendor/illuminate/database/Connection.php(753): Illuminate\Database\Connection->Illuminate\Database\{closure}()
#10 /www/SPQRjump/vendor/illuminate/database/Connection.php(720): Illuminate\Database\Connection->runQueryCallback()
#11 /www/SPQRjump/vendor/illuminate/database/Connection.php(422): Illuminate\Database\Connection->run()
#12 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(3030): Illuminate\Database\Connection->select()
#13 /www/SPQRjump/app/controller/Server.php(49): Illuminate\Database\Query\Builder->exists()
#14 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Server->short()
#15 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#16 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#17 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#18 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#19 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#20 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#21 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#22 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#23 /www/SPQRjump/vendor/workerman/workerman/Worker.php(560): Workerman\Worker::forkWorkers()
#24 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#25 /www/SPQRjump/start.php(4): support\App::run()
#26 {main}
Next Illuminate\Database\QueryException: could not find driver (SQL: select exists(select * from `links` where `link` = 6271) as `exists`) in /www/SPQRjump/vendor/illuminate/database/Connection.php:760
Stack trace:
#0 /www/SPQRjump/vendor/illuminate/database/Connection.php(720): Illuminate\Database\Connection->runQueryCallback()
#1 /www/SPQRjump/vendor/illuminate/database/Connection.php(422): Illuminate\Database\Connection->run()
#2 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(3030): Illuminate\Database\Connection->select()
#3 /www/SPQRjump/app/controller/Server.php(49): Illuminate\Database\Query\Builder->exists()
#4 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Server->short()
#5 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#6 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#7 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#8 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#9 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#10 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#11 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#12 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#13 /www/SPQRjump/vendor/workerman/workerman/Worker.php(560): Workerman\Worker::forkWorkers()
#14 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#15 /www/SPQRjump/start.php(4): support\App::run()
#16 {main} [] []
[2024-11-04 19:46:31] default.ERROR: 127.0.0.1 POST localhost:8780/!/short
PDOException: SQLSTATE[HY000] [3159] Connections using insecure transport are prohibited while --require_secure_transport=ON. in /www/SPQRjump/vendor/illuminate/database/Connectors/Connector.php:70
Stack trace:
#0 /www/SPQRjump/vendor/illuminate/database/Connectors/Connector.php(70): PDO->__construct()
#1 /www/SPQRjump/vendor/illuminate/database/Connectors/Connector.php(46): Illuminate\Database\Connectors\Connector->createPdoConnection()
#2 /www/SPQRjump/vendor/illuminate/database/Connectors/MySqlConnector.php(24): Illuminate\Database\Connectors\Connector->createConnection()
#3 /www/SPQRjump/vendor/illuminate/database/Connectors/ConnectionFactory.php(184): Illuminate\Database\Connectors\MySqlConnector->connect()
#4 [internal function]: Illuminate\Database\Connectors\ConnectionFactory->Illuminate\Database\Connectors\{closure}()
#5 /www/SPQRjump/vendor/illuminate/database/Connection.php(1181): call_user_func()
#6 /www/SPQRjump/vendor/illuminate/database/Connection.php(1217): Illuminate\Database\Connection->getPdo()
#7 /www/SPQRjump/vendor/illuminate/database/Connection.php(486): Illuminate\Database\Connection->getReadPdo()
#8 /www/SPQRjump/vendor/illuminate/database/Connection.php(414): Illuminate\Database\Connection->getPdoForSelect()
#9 /www/SPQRjump/vendor/illuminate/database/Connection.php(753): Illuminate\Database\Connection->Illuminate\Database\{closure}()
#10 /www/SPQRjump/vendor/illuminate/database/Connection.php(720): Illuminate\Database\Connection->runQueryCallback()
#11 /www/SPQRjump/vendor/illuminate/database/Connection.php(422): Illuminate\Database\Connection->run()
#12 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(3030): Illuminate\Database\Connection->select()
#13 /www/SPQRjump/app/controller/Server.php(49): Illuminate\Database\Query\Builder->exists()
#14 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Server->short()
#15 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#16 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#17 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#18 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#19 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#20 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#21 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#22 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#23 /www/SPQRjump/vendor/workerman/workerman/Worker.php(560): Workerman\Worker::forkWorkers()
#24 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#25 /www/SPQRjump/start.php(4): support\App::run()
#26 {main}
Next Illuminate\Database\QueryException: SQLSTATE[HY000] [3159] Connections using insecure transport are prohibited while --require_secure_transport=ON. (SQL: select exists(select * from `links` where `link` = 6271) as `exists`) in /www/SPQRjump/vendor/illuminate/database/Connection.php:760
Stack trace:
#0 /www/SPQRjump/vendor/illuminate/database/Connection.php(720): Illuminate\Database\Connection->runQueryCallback()
#1 /www/SPQRjump/vendor/illuminate/database/Connection.php(422): Illuminate\Database\Connection->run()
#2 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(3030): Illuminate\Database\Connection->select()
#3 /www/SPQRjump/app/controller/Server.php(49): Illuminate\Database\Query\Builder->exists()
#4 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Server->short()
#5 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#6 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#7 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#8 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#9 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#10 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#11 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#12 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#13 /www/SPQRjump/vendor/workerman/workerman/Worker.php(560): Workerman\Worker::forkWorkers()
#14 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#15 /www/SPQRjump/start.php(4): support\App::run()
#16 {main} [] []
[2024-11-04 19:53:15] default.ERROR: 127.0.0.1 POST localhost:8780/!/short
PDOException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'user' cannot be null in /www/SPQRjump/vendor/illuminate/database/Connection.php:545
Stack trace:
#0 /www/SPQRjump/vendor/illuminate/database/Connection.php(545): PDOStatement->execute()
#1 /www/SPQRjump/vendor/illuminate/database/Connection.php(753): Illuminate\Database\Connection->Illuminate\Database\{closure}()
#2 /www/SPQRjump/vendor/illuminate/database/Connection.php(720): Illuminate\Database\Connection->runQueryCallback()
#3 /www/SPQRjump/vendor/illuminate/database/Connection.php(546): Illuminate\Database\Connection->run()
#4 /www/SPQRjump/vendor/illuminate/database/Connection.php(498): Illuminate\Database\Connection->statement()
#5 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(3274): Illuminate\Database\Connection->insert()
#6 /www/SPQRjump/app/controller/Server.php(69): Illuminate\Database\Query\Builder->insert()
#7 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Server->short()
#8 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#9 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#10 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#11 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#12 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#13 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#14 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#15 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#16 /www/SPQRjump/vendor/workerman/workerman/Worker.php(560): Workerman\Worker::forkWorkers()
#17 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#18 /www/SPQRjump/start.php(4): support\App::run()
#19 {main}
Next Illuminate\Database\QueryException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'user' cannot be null (SQL: insert into `links` (`link`, `source`, `owner`, `webid`, `user`, `type`, `time`) values (6271, https://www.cnki.net/, 《中国学术期刊(光盘版)》电子杂志社有限公司, 京ICP备11019815号-4, ?, false, 1730721195)) in /www/SPQRjump/vendor/illuminate/database/Connection.php:760
Stack trace:
#0 /www/SPQRjump/vendor/illuminate/database/Connection.php(720): Illuminate\Database\Connection->runQueryCallback()
#1 /www/SPQRjump/vendor/illuminate/database/Connection.php(546): Illuminate\Database\Connection->run()
#2 /www/SPQRjump/vendor/illuminate/database/Connection.php(498): Illuminate\Database\Connection->statement()
#3 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(3274): Illuminate\Database\Connection->insert()
#4 /www/SPQRjump/app/controller/Server.php(69): Illuminate\Database\Query\Builder->insert()
#5 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Server->short()
#6 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#7 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#8 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#9 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#10 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#11 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#12 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#13 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#14 /www/SPQRjump/vendor/workerman/workerman/Worker.php(560): Workerman\Worker::forkWorkers()
#15 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#16 /www/SPQRjump/start.php(4): support\App::run()
#17 {main} [] []
[2024-11-04 19:54:20] default.ERROR: 127.0.0.1 POST localhost:8780/!/short
PDOException: SQLSTATE[HY000]: General error: 1366 Incorrect integer value: 'false' for column 'type' at row 1 in /www/SPQRjump/vendor/illuminate/database/Connection.php:545
Stack trace:
#0 /www/SPQRjump/vendor/illuminate/database/Connection.php(545): PDOStatement->execute()
#1 /www/SPQRjump/vendor/illuminate/database/Connection.php(753): Illuminate\Database\Connection->Illuminate\Database\{closure}()
#2 /www/SPQRjump/vendor/illuminate/database/Connection.php(720): Illuminate\Database\Connection->runQueryCallback()
#3 /www/SPQRjump/vendor/illuminate/database/Connection.php(546): Illuminate\Database\Connection->run()
#4 /www/SPQRjump/vendor/illuminate/database/Connection.php(498): Illuminate\Database\Connection->statement()
#5 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(3274): Illuminate\Database\Connection->insert()
#6 /www/SPQRjump/app/controller/Server.php(72): Illuminate\Database\Query\Builder->insert()
#7 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Server->short()
#8 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#9 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#10 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#11 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#12 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#13 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#14 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#15 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#16 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
#17 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
#18 /www/SPQRjump/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
#19 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#20 /www/SPQRjump/start.php(4): support\App::run()
#21 {main}
Next Illuminate\Database\QueryException: SQLSTATE[HY000]: General error: 1366 Incorrect integer value: 'false' for column 'type' at row 1 (SQL: insert into `links` (`link`, `source`, `owner`, `webid`, `user`, `type`, `time`) values (6271, https://www.cnki.net/, 《中国学术期刊(光盘版)》电子杂志社有限公司, 京ICP备11019815号-4, anonymous, false, 1730721260)) in /www/SPQRjump/vendor/illuminate/database/Connection.php:760
Stack trace:
#0 /www/SPQRjump/vendor/illuminate/database/Connection.php(720): Illuminate\Database\Connection->runQueryCallback()
#1 /www/SPQRjump/vendor/illuminate/database/Connection.php(546): Illuminate\Database\Connection->run()
#2 /www/SPQRjump/vendor/illuminate/database/Connection.php(498): Illuminate\Database\Connection->statement()
#3 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(3274): Illuminate\Database\Connection->insert()
#4 /www/SPQRjump/app/controller/Server.php(72): Illuminate\Database\Query\Builder->insert()
#5 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Server->short()
#6 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#7 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#8 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#9 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#10 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#11 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#12 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#13 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#14 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
#15 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
#16 /www/SPQRjump/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
#17 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#18 /www/SPQRjump/start.php(4): support\App::run()
#19 {main} [] []
[2024-11-04 19:54:58] default.ERROR: 127.0.0.1 POST localhost:8780/!/short
PDOException: SQLSTATE[22007]: Invalid datetime format: 1292 Incorrect datetime value: '1730721298' for column 'time' at row 1 in /www/SPQRjump/vendor/illuminate/database/Connection.php:545
Stack trace:
#0 /www/SPQRjump/vendor/illuminate/database/Connection.php(545): PDOStatement->execute()
#1 /www/SPQRjump/vendor/illuminate/database/Connection.php(753): Illuminate\Database\Connection->Illuminate\Database\{closure}()
#2 /www/SPQRjump/vendor/illuminate/database/Connection.php(720): Illuminate\Database\Connection->runQueryCallback()
#3 /www/SPQRjump/vendor/illuminate/database/Connection.php(546): Illuminate\Database\Connection->run()
#4 /www/SPQRjump/vendor/illuminate/database/Connection.php(498): Illuminate\Database\Connection->statement()
#5 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(3274): Illuminate\Database\Connection->insert()
#6 /www/SPQRjump/app/controller/Server.php(77): Illuminate\Database\Query\Builder->insert()
#7 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Server->short()
#8 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#9 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#10 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#11 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#12 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#13 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#14 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#15 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#16 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
#17 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
#18 /www/SPQRjump/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
#19 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#20 /www/SPQRjump/start.php(4): support\App::run()
#21 {main}
Next Illuminate\Database\QueryException: SQLSTATE[22007]: Invalid datetime format: 1292 Incorrect datetime value: '1730721298' for column 'time' at row 1 (SQL: insert into `links` (`link`, `source`, `owner`, `webid`, `user`, `type`, `time`) values (6271, https://www.cnki.net/, 《中国学术期刊(光盘版)》电子杂志社有限公司, 京ICP备11019815号-4, anonymous, 0, 1730721298)) in /www/SPQRjump/vendor/illuminate/database/Connection.php:760
Stack trace:
#0 /www/SPQRjump/vendor/illuminate/database/Connection.php(720): Illuminate\Database\Connection->runQueryCallback()
#1 /www/SPQRjump/vendor/illuminate/database/Connection.php(546): Illuminate\Database\Connection->run()
#2 /www/SPQRjump/vendor/illuminate/database/Connection.php(498): Illuminate\Database\Connection->statement()
#3 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(3274): Illuminate\Database\Connection->insert()
#4 /www/SPQRjump/app/controller/Server.php(77): Illuminate\Database\Query\Builder->insert()
#5 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Server->short()
#6 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#7 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#8 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#9 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#10 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#11 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#12 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#13 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#14 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
#15 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
#16 /www/SPQRjump/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
#17 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#18 /www/SPQRjump/start.php(4): support\App::run()
#19 {main} [] []
[2024-11-04 23:14:35] default.ERROR: 127.0.0.1 GET localhost:8780/ed62
ErrorException: Undefined variable $code in /www/SPQRjump/app/controller/Server.php:86
Stack trace:
#0 /www/SPQRjump/app/controller/Server.php(86): support\App::{closure}()
#1 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Server->view()
#2 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(369): Webman\App::Webman\{closure}()
#3 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#4 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#5 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#6 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#7 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#8 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#9 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#10 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#11 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
#12 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
#13 /www/SPQRjump/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
#14 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#15 /www/SPQRjump/start.php(4): support\App::run()
#16 {main} [] []
[2024-11-04 23:14:53] default.ERROR: 127.0.0.1 GET localhost:8780/ed62
ErrorException: Undefined variable $ba in /www/SPQRjump/app/controller/Server.php:94
Stack trace:
#0 /www/SPQRjump/app/controller/Server.php(94): support\App::{closure}()
#1 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Server->view()
#2 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(369): Webman\App::Webman\{closure}()
#3 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#4 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#5 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#6 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#7 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#8 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#9 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#10 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#11 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
#12 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
#13 /www/SPQRjump/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
#14 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#15 /www/SPQRjump/start.php(4): support\App::run()
#16 {main} [] []
[2024-11-04 23:57:53] default.ERROR: 127.0.0.1 POST localhost:8780/!/loginapi
TypeError: count(): Argument #1 ($value) must be of type Countable|array, string given in /www/SPQRjump/app/controller/Account.php:55
Stack trace:
#0 /www/SPQRjump/app/controller/Account.php(55): count()
#1 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Account->loginapi()
#2 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(141): Webman\App::Webman\{closure}()
#3 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#4 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#5 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#6 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#7 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#8 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#9 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
#10 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
#11 /www/SPQRjump/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
#12 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#13 /www/SPQRjump/start.php(4): support\App::run()
#14 {main} [] []
[2024-11-04 23:59:50] default.ERROR: 127.0.0.1 GET localhost:8780
ErrorException: Undefined variable $realname in /www/SPQRjump/app/view/index.html:142
Stack trace:
#0 /www/SPQRjump/app/view/index.html(142): support\App::{closure}()
#1 /www/SPQRjump/vendor/workerman/webman-framework/src/support/view/Raw.php(70): include('...')
#2 /www/SPQRjump/support/helpers.php(201): support\view\Raw::render()
#3 /www/SPQRjump/app/controller/IndexController.php(14): view()
#4 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\IndexController->index()
#5 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#6 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#7 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#8 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#9 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#10 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#11 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#12 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#13 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
#14 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
#15 /www/SPQRjump/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
#16 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#17 /www/SPQRjump/start.php(4): support\App::run()
#18 {main} [] []

View File

@ -0,0 +1,114 @@
[2024-11-05 00:23:54] default.ERROR: 127.0.0.1 GET localhost:8780/!/my
ParseError: Unclosed '{' on line 36 in /www/SPQRjump/app/view/my.html:58
Stack trace:
#0 /www/SPQRjump/support/helpers.php(201): support\view\Raw::render()
#1 /www/SPQRjump/app/controller/Account.php(39): view()
#2 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Account->my()
#3 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#4 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#5 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#6 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#7 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#8 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#9 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#10 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#11 /www/SPQRjump/vendor/workerman/workerman/Worker.php(560): Workerman\Worker::forkWorkers()
#12 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#13 /www/SPQRjump/start.php(4): support\App::run()
#14 {main} [] []
[2024-11-05 00:25:15] default.ERROR: 127.0.0.1 GET localhost:8780/!/my
ErrorException: Undefined property: Illuminate\Database\MySqlConnection::$link in /www/SPQRjump/app/view/my.html:38
Stack trace:
#0 /www/SPQRjump/app/view/my.html(38): support\App::{closure}()
#1 /www/SPQRjump/vendor/workerman/webman-framework/src/support/view/Raw.php(70): include('...')
#2 /www/SPQRjump/support/helpers.php(201): support\view\Raw::render()
#3 /www/SPQRjump/app/controller/Account.php(39): view()
#4 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Account->my()
#5 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#6 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#7 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#8 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#9 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#10 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#11 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#12 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#13 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
#14 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
#15 /www/SPQRjump/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
#16 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#17 /www/SPQRjump/start.php(4): support\App::run()
#18 {main} [] []
[2024-11-05 00:26:24] default.ERROR: 127.0.0.1 GET localhost:8780/!/my
PDOException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'enoch' in 'field list' in /www/SPQRjump/vendor/illuminate/database/Connection.php:414
Stack trace:
#0 /www/SPQRjump/vendor/illuminate/database/Connection.php(414): PDO->prepare()
#1 /www/SPQRjump/vendor/illuminate/database/Connection.php(753): Illuminate\Database\Connection->Illuminate\Database\{closure}()
#2 /www/SPQRjump/vendor/illuminate/database/Connection.php(720): Illuminate\Database\Connection->runQueryCallback()
#3 /www/SPQRjump/vendor/illuminate/database/Connection.php(422): Illuminate\Database\Connection->run()
#4 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(2706): Illuminate\Database\Connection->select()
#5 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(2694): Illuminate\Database\Query\Builder->runSelect()
#6 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(3230): Illuminate\Database\Query\Builder->Illuminate\Database\Query\{closure}()
#7 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(2695): Illuminate\Database\Query\Builder->onceWithColumns()
#8 /www/SPQRjump/app/controller/Account.php(38): Illuminate\Database\Query\Builder->get()
#9 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Account->my()
#10 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#11 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#12 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#13 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#14 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#15 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#16 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#17 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#18 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
#19 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
#20 /www/SPQRjump/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
#21 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#22 /www/SPQRjump/start.php(4): support\App::run()
#23 {main}
Next Illuminate\Database\QueryException: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'enoch' in 'field list' (SQL: select `owner`, `enoch` from `links`) in /www/SPQRjump/vendor/illuminate/database/Connection.php:760
Stack trace:
#0 /www/SPQRjump/vendor/illuminate/database/Connection.php(720): Illuminate\Database\Connection->runQueryCallback()
#1 /www/SPQRjump/vendor/illuminate/database/Connection.php(422): Illuminate\Database\Connection->run()
#2 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(2706): Illuminate\Database\Connection->select()
#3 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(2694): Illuminate\Database\Query\Builder->runSelect()
#4 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(3230): Illuminate\Database\Query\Builder->Illuminate\Database\Query\{closure}()
#5 /www/SPQRjump/vendor/illuminate/database/Query/Builder.php(2695): Illuminate\Database\Query\Builder->onceWithColumns()
#6 /www/SPQRjump/app/controller/Account.php(38): Illuminate\Database\Query\Builder->get()
#7 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Account->my()
#8 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#9 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#10 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#11 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#12 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#13 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#14 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#15 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#16 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
#17 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
#18 /www/SPQRjump/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
#19 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#20 /www/SPQRjump/start.php(4): support\App::run()
#21 {main} [] []
[2024-11-05 00:27:11] default.ERROR: 127.0.0.1 GET localhost:8780/!/my
ErrorException: Undefined property: Illuminate\Database\MySqlConnection::$link in /www/SPQRjump/app/view/my.html:38
Stack trace:
#0 /www/SPQRjump/app/view/my.html(38): support\App::{closure}()
#1 /www/SPQRjump/vendor/workerman/webman-framework/src/support/view/Raw.php(70): include('...')
#2 /www/SPQRjump/support/helpers.php(201): support\view\Raw::render()
#3 /www/SPQRjump/app/controller/Account.php(40): view()
#4 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(319): app\controller\Account->my()
#5 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(560): Webman\App::Webman\{closure}()
#6 /www/SPQRjump/vendor/workerman/webman-framework/src/App.php(148): Webman\App::findRoute()
#7 /www/SPQRjump/vendor/workerman/workerman/Connection/TcpConnection.php(646): Webman\App->onMessage()
#8 [internal function]: Workerman\Connection\TcpConnection->baseRead()
#9 /www/SPQRjump/vendor/workerman/workerman/Events/Event.php(193): EventBase->loop()
#10 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1638): Workerman\Events\Event->loop()
#11 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1429): Workerman\Worker::forkOneWorkerForLinux()
#12 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1403): Workerman\Worker::forkWorkersForLinux()
#13 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1788): Workerman\Worker::forkWorkers()
#14 /www/SPQRjump/vendor/workerman/workerman/Worker.php(1724): Workerman\Worker::monitorWorkersForLinux()
#15 /www/SPQRjump/vendor/workerman/workerman/Worker.php(562): Workerman\Worker::monitorWorkers()
#16 /www/SPQRjump/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()
#17 /www/SPQRjump/start.php(4): support\App::run()
#18 {main} [] []

135
runtime/logs/workerman.log Normal file
View File

@ -0,0 +1,135 @@
2024-11-04 15:41:56 pid:5686 Workerman[start.php] start in DEBUG mode
2024-11-04 15:43:21 pid:5686 Workerman[start.php] reloading
2024-11-04 15:44:33 pid:5686 Workerman[start.php] reloading
2024-11-04 15:49:31 pid:5686 Workerman[start.php] reloading
2024-11-04 16:05:12 pid:5686 Workerman[start.php] reloading
2024-11-04 16:05:20 pid:5686 Workerman[start.php] reloading
2024-11-04 16:05:40 pid:5686 Workerman[start.php] reloading
2024-11-04 16:08:02 pid:5686 Workerman[start.php] reloading
2024-11-04 16:10:14 pid:5686 Workerman[start.php] reloading
2024-11-04 16:10:52 pid:5686 Workerman[start.php] reloading
2024-11-04 16:11:04 pid:5686 Workerman[start.php] reloading
2024-11-04 16:15:29 pid:5686 Workerman[start.php] reloading
2024-11-04 16:19:19 pid:5686 Workerman[start.php] reloading
2024-11-04 16:27:52 pid:5686 Workerman[start.php] reloading
2024-11-04 16:33:06 pid:5686 Workerman[start.php] reloading
2024-11-04 19:11:11 pid:5686 Workerman[start.php] stopping ...
2024-11-04 19:11:12 pid:5686 Workerman[start.php] has been stopped
2024-11-04 19:35:58 pid:23658 Workerman[start.php] start in DEBUG mode
2024-11-04 19:37:16 pid:23658 Workerman[start.php] reloading
2024-11-04 19:37:17 pid:23658 Workerman[start.php] stopping ...
2024-11-04 19:37:18 pid:23658 Workerman[start.php] has been stopped
2024-11-04 19:37:24 pid:24988 Workerman[start.php] start in DEBUG mode
2024-11-04 19:38:10 pid:24988 Workerman[start.php] reloading
2024-11-04 19:38:39 pid:24988 Workerman[start.php] reloading
2024-11-04 19:39:42 pid:24988 Workerman[start.php] stopping ...
2024-11-04 19:39:42 pid:24988 Workerman[start.php] has been stopped
2024-11-04 19:42:45 pid:28498 Workerman[start.php] start in DEBUG mode
2024-11-04 19:44:44 pid:28498 Workerman[start.php] stopping ...
2024-11-04 19:44:44 pid:28498 Workerman[start.php] has been stopped
2024-11-04 19:46:27 pid:30000 Workerman[start.php] start in DEBUG mode
2024-11-04 19:54:16 pid:30000 Workerman[start.php] reloading
2024-11-04 19:54:51 pid:30000 Workerman[start.php] reloading
2024-11-04 22:44:39 pid:30000 Workerman[start.php] reloading
2024-11-04 22:46:14 pid:30000 Workerman[start.php] reloading
2024-11-04 22:46:44 pid:30000 Workerman[start.php] reloading
2024-11-04 22:48:16 pid:30000 Workerman[start.php] reloading
2024-11-04 22:52:23 pid:30000 Workerman[start.php] reloading
2024-11-04 22:53:42 pid:30000 Workerman[start.php] stopping ...
2024-11-04 22:53:42 pid:30000 Workerman[start.php] has been stopped
2024-11-04 22:55:54 pid:2219 Workerman[start.php] start in DEBUG mode
2024-11-04 23:00:07 pid:2219 Workerman[start.php] reloading
2024-11-04 23:06:02 pid:2219 Workerman[start.php] reloading
2024-11-04 23:09:06 pid:2219 Workerman[start.php] reloading
2024-11-04 23:09:28 pid:2219 Workerman[start.php] reloading
2024-11-04 23:09:35 pid:2219 Workerman[start.php] reloading
2024-11-04 23:10:02 pid:2219 Workerman[start.php] reloading
2024-11-04 23:10:08 pid:2219 Workerman[start.php] reloading
2024-11-04 23:10:14 pid:2219 Workerman[start.php] reloading
2024-11-04 23:13:04 pid:2219 Workerman[start.php] reloading
2024-11-04 23:13:13 pid:2219 Workerman[start.php] reloading
2024-11-04 23:13:19 pid:2219 Workerman[start.php] reloading
2024-11-04 23:13:25 pid:2219 Workerman[start.php] reloading
2024-11-04 23:13:33 pid:2219 Workerman[start.php] reloading
2024-11-04 23:13:48 pid:2219 Workerman[start.php] reloading
2024-11-04 23:13:54 pid:2219 Workerman[start.php] reloading
2024-11-04 23:14:22 pid:2219 Workerman[start.php] reloading
2024-11-04 23:14:50 pid:2219 Workerman[start.php] reloading
2024-11-04 23:15:13 pid:2219 Workerman[start.php] reloading
2024-11-04 23:17:01 pid:2219 Workerman[start.php] reloading
2024-11-04 23:17:18 pid:2219 Workerman[start.php] reloading
2024-11-04 23:18:00 pid:2219 Workerman[start.php] reloading
2024-11-04 23:25:18 pid:2219 Workerman[start.php] reloading
2024-11-04 23:28:01 pid:2219 Workerman[start.php] reloading
2024-11-04 23:31:04 pid:2219 Workerman[start.php] reloading
2024-11-04 23:35:22 pid:2219 Workerman[start.php] reloading
2024-11-04 23:35:39 pid:2219 Workerman[start.php] reloading
2024-11-04 23:35:50 pid:2219 Workerman[start.php] reloading
2024-11-04 23:36:09 pid:2219 Workerman[start.php] reloading
2024-11-04 23:36:11 pid:2219 Workerman[start.php] stopping ...
2024-11-04 23:36:11 pid:2219 Workerman[start.php] has been stopped
2024-11-04 23:36:33 pid:14722 Workerman[start.php] start in DEBUG mode
2024-11-04 23:37:12 pid:14722 Workerman[start.php] reloading
2024-11-04 23:38:47 pid:14722 Workerman[start.php] reloading
2024-11-04 23:38:55 pid:14722 Workerman[start.php] reloading
2024-11-04 23:42:23 pid:14722 Workerman[start.php] reloading
2024-11-04 23:42:54 pid:14722 Workerman[start.php] reloading
2024-11-04 23:43:52 pid:14722 Workerman[start.php] reloading
2024-11-04 23:44:23 pid:14722 Workerman[start.php] stopping ...
2024-11-04 23:44:23 pid:14722 Workerman[start.php] has been stopped
2024-11-04 23:45:28 pid:17683 Workerman[start.php] start in DEBUG mode
2024-11-04 23:45:37 pid:17683 Workerman[start.php] reloading
2024-11-04 23:47:16 pid:17683 Workerman[start.php] reloading
2024-11-04 23:47:32 pid:17683 Workerman[start.php] reloading
2024-11-04 23:48:05 pid:17683 Workerman[start.php] reloading
2024-11-04 23:49:42 pid:17683 Workerman[start.php] reloading
2024-11-04 23:50:09 pid:17683 Workerman[start.php] reloading
2024-11-04 23:50:33 pid:17683 Workerman[start.php] reloading
2024-11-04 23:50:49 pid:17683 Workerman[start.php] reloading
2024-11-04 23:52:45 pid:17683 Workerman[start.php] reloading
2024-11-04 23:57:35 pid:17683 Workerman[start.php] reloading
2024-11-04 23:58:10 pid:17683 Workerman[start.php] reloading
2024-11-04 23:58:55 pid:17683 Workerman[start.php] reloading
2024-11-04 23:58:58 pid:17683 Workerman[start.php] reloading
2024-11-04 23:59:35 pid:17683 Workerman[start.php] reloading
2024-11-04 23:59:48 pid:17683 Workerman[start.php] reloading
2024-11-04 23:59:56 pid:17683 Workerman[start.php] reloading
2024-11-04 23:59:57 pid:17683 Workerman[start.php] reloading
2024-11-05 00:01:10 pid:17683 Workerman[start.php] reloading
2024-11-05 00:01:18 pid:17683 Workerman[start.php] reloading
2024-11-05 00:03:28 pid:17683 Workerman[start.php] reloading
2024-11-05 00:04:52 pid:17683 Workerman[start.php] reloading
2024-11-05 00:05:54 pid:17683 Workerman[start.php] reloading
2024-11-05 00:06:19 pid:17683 Workerman[start.php] reloading
2024-11-05 00:08:00 pid:17683 Workerman[start.php] reloading
2024-11-05 00:11:41 pid:17683 Workerman[start.php] reloading
2024-11-05 00:14:04 pid:17683 Workerman[start.php] reloading
2024-11-05 00:14:07 pid:17683 Workerman[start.php] reloading
2024-11-05 00:14:35 pid:17683 Workerman[start.php] reloading
2024-11-05 00:14:56 pid:17683 Workerman[start.php] reloading
2024-11-05 00:16:47 pid:17683 Workerman[start.php] reloading
2024-11-05 00:23:50 pid:17683 Workerman[start.php] stopping ...
2024-11-05 00:23:50 pid:17683 Workerman[start.php] has been stopped
2024-11-05 00:23:51 pid:30205 Workerman[start.php] start in DEBUG mode
2024-11-05 00:24:43 pid:30205 Workerman[start.php] reloading
2024-11-05 00:24:55 pid:30205 Workerman[start.php] reloading
2024-11-05 00:25:08 pid:30205 Workerman[start.php] reloading
2024-11-05 00:26:21 pid:30205 Workerman[start.php] reloading
2024-11-05 00:26:53 pid:30205 Workerman[start.php] reloading
2024-11-05 00:27:09 pid:30205 Workerman[start.php] reloading
2024-11-05 00:27:33 pid:30205 Workerman[start.php] reloading
2024-11-05 00:28:04 pid:30205 Workerman[start.php] reloading
2024-11-05 00:29:59 pid:30205 Workerman[start.php] reloading
2024-11-05 00:30:15 pid:30205 Workerman[start.php] reloading
2024-11-05 00:31:08 pid:30205 Workerman[start.php] reloading
2024-11-05 00:32:10 pid:30205 Workerman[start.php] reloading
2024-11-05 00:37:46 pid:30205 Workerman[start.php] reloading
2024-11-05 00:38:29 pid:30205 Workerman[start.php] reloading
2024-11-05 00:38:41 pid:30205 Workerman[start.php] reloading
2024-11-05 00:39:30 pid:30205 Workerman[start.php] reloading
2024-11-05 00:39:43 pid:30205 Workerman[start.php] reloading
2024-11-05 00:40:44 pid:30205 Workerman[start.php] reloading
2024-11-05 00:41:08 pid:30205 Workerman[start.php] reloading
2024-11-05 00:41:47 pid:30205 Workerman[start.php] reloading
2024-11-05 00:43:44 pid:30205 Workerman[start.php] stopping ...
2024-11-05 00:43:44 pid:30205 Workerman[start.php] has been stopped

View File

@ -0,0 +1 @@
a:2:{s:7:"captcha";s:5:"tcqyy";s:4:"user";s:5:"enoch";}

View File

@ -0,0 +1 @@
a:3:{s:7:"captcha";s:5:"sq8y4";s:4:"user";s:5:"enoch";s:8:"realname";s:9:"以诺书";}

4
start.php Executable file
View 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
View 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
View 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
View 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);

528
support/helpers.php Normal file
View File

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

25
vendor/autoload.php vendored Normal file
View 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 ComposerAutoloaderInit7604022b3ed72e518bc17cb475d0aba8::getLoader();

120
vendor/bin/carbon vendored Executable file
View File

@ -0,0 +1,120 @@
#!/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'))
) {
include("phpvfscomposer://" . __DIR__ . '/..'.'/nesbot/carbon/bin/carbon');
exit(0);
}
}
include __DIR__ . '/..'.'/nesbot/carbon/bin/carbon';

5
vendor/bin/carbon.bat vendored Executable file
View File

@ -0,0 +1,5 @@
@ECHO OFF
setlocal DISABLEDELAYEDEXPANSION
SET BIN_TARGET=%~dp0/carbon
SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
php "%BIN_TARGET%" %*

120
vendor/bin/phinx vendored Executable file
View File

@ -0,0 +1,120 @@
#!/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'))
) {
include("phpvfscomposer://" . __DIR__ . '/..'.'/robmorgan/phinx/bin/phinx');
exit(0);
}
}
include __DIR__ . '/..'.'/robmorgan/phinx/bin/phinx';

5
vendor/bin/phinx.bat vendored Executable file
View File

@ -0,0 +1,5 @@
@ECHO OFF
setlocal DISABLEDELAYEDEXPANSION
SET BIN_TARGET=%~dp0/phinx
SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
php "%BIN_TARGET%" %*

120
vendor/bin/var-dump-server vendored Executable file
View File

@ -0,0 +1,120 @@
#!/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'))
) {
include("phpvfscomposer://" . __DIR__ . '/..'.'/symfony/var-dumper/Resources/bin/var-dump-server');
exit(0);
}
}
include __DIR__ . '/..'.'/symfony/var-dumper/Resources/bin/var-dump-server';

5
vendor/bin/var-dump-server.bat vendored Executable file
View File

@ -0,0 +1,5 @@
@ECHO OFF
setlocal DISABLEDELAYEDEXPANSION
SET BIN_TARGET=%~dp0/var-dump-server
SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
php "%BIN_TARGET%" %*

445
vendor/brick/math/CHANGELOG.md vendored Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

512
vendor/brick/math/src/BigNumber.php vendored Normal file
View 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
View 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);
}
}

View 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.');
}
}

View 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));
}
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Brick\Math\Exception;
/**
* Base class for all math exceptions.
*/
class MathException extends \Exception
{
}

View 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
{
}

View 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));
}
}

View 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.');
}
}

View 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 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;
}
}

View 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);
}
}

View 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));
}
}

View 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
View 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
View 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
View 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
View 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
View 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 = [];
}
}

View 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;
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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));
}
}

View 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
View 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
{
}

View 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;
}

View 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
View 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);
}
}

View 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

View 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');

View 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.';
}

View 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;
}

View 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
View 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
View 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
View 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;
}
}

View 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;
}

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